Skip to main content

antlr4_runtime/
parser.rs

1// `HashMap`/`HashSet` here are used as parser-internal caches keyed on
2// stable ATN coordinates (state numbers, token indices). They're never
3// iterated externally, so the project's `disallowed_types` lint (which
4// guards against non-deterministic iteration order leaking out) does not
5// apply to these uses.
6use std::cell::RefCell;
7use std::cmp::Ordering;
8#[allow(clippy::disallowed_types)]
9use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
10use std::hash::{BuildHasherDefault, Hash, Hasher};
11use std::rc::Rc;
12
13/// Rotate constant copied from rustc-hash / `FxHash`. The default
14/// `RandomState` hasher seeds itself from the OS RNG and runs `SipHash` on
15/// every key, which dominates `recognize_state_fast`'s memo lookups;
16/// `FxHasher` is a streaming integer hasher with near-zero per-call overhead
17/// and matches the access pattern of small integer keys that the parser memo
18/// uses.
19#[derive(Clone, Copy, Default)]
20struct FxHasher {
21    hash: u64,
22}
23
24const FX_ROT: u32 = 5;
25const FX_SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
26
27impl Hasher for FxHasher {
28    /// Folds bytes 8 at a time so a `write(&[u8; 8])` call hashes to the same
29    /// state as a `write_u64` of the same little-endian bits. The `Hash` impls
30    /// for `String`, `[u8; N]`, and slice-like types reach the hasher through
31    /// `write`; matching the typed-method behaviour avoids the silent
32    /// divergence flagged in PR #5 review (Greptile P2). Tail bytes that do
33    /// not form a full word are mixed one at a time with the same constants,
34    /// keeping behaviour deterministic regardless of the slice length.
35    #[inline]
36    fn write(&mut self, mut bytes: &[u8]) {
37        while bytes.len() >= 8 {
38            let (head, rest) = bytes.split_at(8);
39            let word = u64::from_le_bytes(head.try_into().expect("8-byte chunk"));
40            self.hash = (self.hash.rotate_left(FX_ROT) ^ word).wrapping_mul(FX_SEED);
41            bytes = rest;
42        }
43        for byte in bytes {
44            self.hash = (self.hash.rotate_left(FX_ROT) ^ u64::from(*byte)).wrapping_mul(FX_SEED);
45        }
46    }
47    #[inline]
48    fn write_u64(&mut self, value: u64) {
49        self.hash = (self.hash.rotate_left(FX_ROT) ^ value).wrapping_mul(FX_SEED);
50    }
51    #[inline]
52    fn write_usize(&mut self, value: usize) {
53        self.write_u64(value as u64);
54    }
55    #[inline]
56    fn write_u32(&mut self, value: u32) {
57        self.write_u64(u64::from(value));
58    }
59    #[inline]
60    fn write_i32(&mut self, value: i32) {
61        self.write_u64(u64::from(i32::cast_unsigned(value)));
62    }
63    #[inline]
64    fn finish(&self) -> u64 {
65        self.hash
66    }
67}
68
69type FxBuildHasher = BuildHasherDefault<FxHasher>;
70#[allow(clippy::disallowed_types)]
71type FxHashMap<K, V> = HashMap<K, V, FxBuildHasher>;
72#[allow(clippy::disallowed_types)]
73type FxHashSet<K> = HashSet<K, FxBuildHasher>;
74
75use crate::atn::AtnStateKind;
76use crate::atn::parser::{
77    ParserAtnPrediction, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
78};
79use crate::atn::parser_atn::{
80    ParserAtn as Atn, ParserAtnState as AtnState, ParserIntervalSet, ParserTransition,
81    ParserTransitionData as Transition, ParserTransitionKind,
82};
83#[cfg(test)]
84use crate::atn::parser_atn::{ParserAtnBuilder, ParserTransitionSpec};
85use crate::char_stream::CharStream;
86use crate::errors::AntlrError;
87use crate::int_stream::IntStream;
88use crate::lexer::{LexerCustomAction, LexerLifecycleCtx, LexerSemCtx};
89use crate::recognizer::{Recognizer, RecognizerData};
90use crate::semir::{self, AStmt, ArithOp, CmpOp, ExprId, HookId, 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    /// Emits a fatal parser error after an entry-rule parse commits to returning it.
5247    ///
5248    /// Generated parsers call this only at their public entry boundary. Nested
5249    /// failures remain silent until generated recovery commits and buffers them.
5250    pub fn report_unrecovered_parser_error(&self, error: &AntlrError) {
5251        let AntlrError::ParserError {
5252            line,
5253            column,
5254            message,
5255            offending,
5256        } = error
5257        else {
5258            return;
5259        };
5260        let offending = offending.and_then(|token| self.token_store().view(token));
5261        self.notify_error_listeners(offending, *line, *column, message, Some(error));
5262    }
5263
5264    fn dispatch_parser_diagnostic(&self, diagnostic: &ParserDiagnostic) {
5265        let offending = diagnostic
5266            .offending
5267            .and_then(|token| self.token_store().view(token));
5268        self.notify_error_listeners(
5269            offending,
5270            diagnostic.line,
5271            diagnostic.column,
5272            &diagnostic.message,
5273            None,
5274        );
5275    }
5276
5277    fn dispatch_parser_diagnostics<'a>(
5278        &self,
5279        diagnostics: impl IntoIterator<Item = &'a ParserDiagnostic>,
5280    ) {
5281        for diagnostic in diagnostics {
5282            self.dispatch_parser_diagnostic(diagnostic);
5283        }
5284    }
5285
5286    fn dispatch_token_source_error(&self, source_error: &TokenSourceError) {
5287        if self.input.token_source().report_error(source_error) {
5288            return;
5289        }
5290        // Lexer errors have no offending token: the failure is that no token
5291        // could be produced, matching ANTLR's null offendingSymbol.
5292        self.notify_error_listeners(
5293            None,
5294            source_error.line,
5295            source_error.column,
5296            &source_error.message,
5297            None,
5298        );
5299    }
5300
5301    fn dispatch_token_source_errors(&self, errors: &[TokenSourceError]) {
5302        for error in errors {
5303            self.dispatch_token_source_error(error);
5304        }
5305    }
5306
5307    /// Dispatches generated parser and lexer diagnostics in the same
5308    /// source-position order as ANTLR's lazy token stream reports them.
5309    fn dispatch_generated_diagnostics(
5310        &self,
5311        parser_diagnostics: &[ParserDiagnostic],
5312        token_errors: &[TokenSourceError],
5313    ) {
5314        // Parser diagnostics keep their event order: Java's console and
5315        // DiagnosticErrorListener print reports as prediction produces them,
5316        // so reportAttemptingFullContext precedes reportContextSensitivity
5317        // even though the latter's position is earlier. Buffered token-source
5318        // errors interleave by source position and win ties.
5319        let mut token_iter = token_errors.iter().peekable();
5320        for diagnostic in parser_diagnostics {
5321            while let Some(error) = token_iter.peek() {
5322                if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
5323                    self.dispatch_token_source_error(error);
5324                    token_iter.next();
5325                } else {
5326                    break;
5327                }
5328            }
5329            self.dispatch_parser_diagnostic(diagnostic);
5330        }
5331        for error in token_iter {
5332            self.dispatch_token_source_error(error);
5333        }
5334    }
5335
5336    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
5337    /// decision code.
5338    pub fn record_generated_ambiguity_diagnostic(
5339        &mut self,
5340        atn: &Atn,
5341        state_number: usize,
5342        start_index: usize,
5343        stop_index: usize,
5344        alts: &[usize],
5345    ) {
5346        if !self.report_diagnostic_errors || alts.len() < 2 {
5347            return;
5348        }
5349        let Some(decision) = atn
5350            .decision_to_state()
5351            .iter()
5352            .position(|candidate| candidate == state_number)
5353        else {
5354            return;
5355        };
5356        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5357            return;
5358        };
5359        let rule_name = self
5360            .rule_names()
5361            .get(rule_index)
5362            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5363        let input = display_input_text(&self.input.text(start_index, stop_index));
5364        let alts = alts
5365            .iter()
5366            .map(usize::to_string)
5367            .collect::<Vec<_>>()
5368            .join(", ");
5369        let key = (decision, start_index, format!("{alts}:{input}"));
5370        if !self.reported_prediction_diagnostics.insert(key) {
5371            return;
5372        }
5373        let start_diagnostic = diagnostic_for_token(
5374            self.token_at(start_index),
5375            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
5376        );
5377        let stop_diagnostic = diagnostic_for_token(
5378            self.token_at(stop_index),
5379            format!(
5380                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
5381            ),
5382        );
5383        self.generated_parser_diagnostics.push(start_diagnostic);
5384        self.generated_parser_diagnostics.push(stop_diagnostic);
5385    }
5386
5387    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
5388    /// parser calls to the adaptive simulator.
5389    pub fn record_generated_prediction_diagnostic(
5390        &mut self,
5391        atn: &Atn,
5392        state_number: usize,
5393        prediction: &ParserAtnPrediction,
5394    ) {
5395        let Some(diagnostic) = &prediction.diagnostic else {
5396            return;
5397        };
5398        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
5399            return;
5400        }
5401        let Some(decision) = atn
5402            .decision_to_state()
5403            .iter()
5404            .position(|candidate| candidate == state_number)
5405        else {
5406            return;
5407        };
5408        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5409            return;
5410        };
5411        let rule_name = self
5412            .rule_names()
5413            .get(rule_index)
5414            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5415        let attempt_input = display_input_text(
5416            &self
5417                .input
5418                .text(diagnostic.start_index, diagnostic.sll_stop_index),
5419        );
5420        let result_input = display_input_text(
5421            &self
5422                .input
5423                .text(diagnostic.start_index, diagnostic.ll_stop_index),
5424        );
5425        let alts = diagnostic
5426            .conflicting_alts
5427            .iter()
5428            .map(usize::to_string)
5429            .collect::<Vec<_>>()
5430            .join(", ");
5431        let key = (
5432            decision,
5433            diagnostic.start_index,
5434            format!(
5435                "{:?}:{alts}:{attempt_input}:{result_input}",
5436                diagnostic.kind
5437            ),
5438        );
5439        if !self.reported_prediction_diagnostics.insert(key) {
5440            return;
5441        }
5442        let attempt_diagnostic = diagnostic_for_token(
5443            self.token_at(diagnostic.sll_stop_index),
5444            format!(
5445                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
5446            ),
5447        );
5448        self.generated_parser_diagnostics.push(attempt_diagnostic);
5449        let message = match diagnostic.kind {
5450            ParserAtnPredictionDiagnosticKind::Ambiguity => {
5451                // Java's DiagnosticErrorListener is exactOnly by default:
5452                // non-exact ambiguities (default LL mode stopping at the
5453                // first resolvable conflict) report the attempt above but
5454                // suppress the ambiguity line itself.
5455                if !diagnostic.exact {
5456                    return;
5457                }
5458                format!(
5459                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
5460                )
5461            }
5462            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
5463                format!(
5464                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
5465                )
5466            }
5467        };
5468        let result_diagnostic =
5469            diagnostic_for_token(self.token_at(diagnostic.ll_stop_index), message);
5470        self.generated_parser_diagnostics.push(result_diagnostic);
5471    }
5472
5473    pub fn la(&self, offset: isize) -> i32 {
5474        self.input.la_token(offset)
5475    }
5476
5477    pub fn consume(&mut self) {
5478        IntStream::consume(&mut self.input);
5479    }
5480
5481    /// Sets a generated integer member value used by target-template tests.
5482    pub fn set_int_member(&mut self, member: usize, value: i64) {
5483        self.int_members.set_scalar(member, value);
5484    }
5485
5486    /// Reads a generated integer member value.
5487    pub fn int_member(&self, member: usize) -> Option<i64> {
5488        self.int_members.scalar(member)
5489    }
5490
5491    /// Pushes onto a generated stack-valued member slot (issue #206).
5492    pub fn push_stack_member(&mut self, member: usize, value: i64) {
5493        self.int_members.push_stack(member, value);
5494    }
5495
5496    /// Pops a generated stack-valued member slot, returning the removed value.
5497    /// `None` when the stack is empty.
5498    pub fn pop_stack_member(&mut self, member: usize) -> Option<i64> {
5499        self.int_members.pop_stack(member)
5500    }
5501
5502    /// Reads the top of a generated stack-valued member slot; `None` when
5503    /// empty or never pushed.
5504    #[must_use]
5505    pub fn stack_member_top(&self, member: usize) -> Option<i64> {
5506        self.int_members.stack_top(member)
5507    }
5508
5509    /// Depth of a generated stack-valued member slot.
5510    #[must_use]
5511    pub fn stack_member_len(&self, member: usize) -> usize {
5512        self.int_members.stack_len(member)
5513    }
5514
5515    /// Seeds grammar-declared initial member values (issue #206).
5516    ///
5517    /// Generated parsers call this at construction for a grammar whose
5518    /// `@members` declares an initializer (`private int level = 1;`). Without
5519    /// it the slot would start at 0, so a predicate reading it would reject
5520    /// input the source grammar accepts.
5521    pub fn set_initial_members(&mut self, initial: impl IntoIterator<Item = (usize, i64)>) {
5522        self.int_members = MemberEnv::with_initial_scalars(initial);
5523    }
5524
5525    /// Captures generated member state before speculative generated parser
5526    /// execution.
5527    ///
5528    /// The snapshot covers scalar *and* stack slots: restoring only scalars
5529    /// would leave a rolled-back path's pushes behind.
5530    #[must_use]
5531    pub fn int_members_checkpoint(&self) -> MemberEnv {
5532        self.int_members.clone()
5533    }
5534
5535    /// Restores generated member state after generated parser fallback.
5536    pub fn restore_int_members(&mut self, members: MemberEnv) {
5537        self.int_members = members;
5538    }
5539
5540    /// Adds `delta` to a generated integer member and returns the new value.
5541    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
5542        self.int_members.add_scalar(member, delta)
5543    }
5544
5545    fn token_type_for_id(&self, id: TokenId) -> i32 {
5546        self.input.token_store().token_type(id).unwrap_or(TOKEN_EOF)
5547    }
5548
5549    fn terminal_tree(&mut self, id: TokenId) -> ParseTree {
5550        if self.build_parse_trees {
5551            self.tree.terminal(id)
5552        } else {
5553            NodeId::placeholder()
5554        }
5555    }
5556
5557    fn error_tree(&mut self, id: TokenId) -> ParseTree {
5558        if self.build_parse_trees {
5559            self.tree.error(id)
5560        } else {
5561            NodeId::placeholder()
5562        }
5563    }
5564
5565    const fn set_context_start(&self, context: &mut ParserRuleContext, id: TokenId) {
5566        context.set_start_id(id);
5567    }
5568
5569    const fn set_context_stop(&self, context: &mut ParserRuleContext, id: TokenId) {
5570        context.set_stop_id(id);
5571    }
5572
5573    fn insert_synthetic_token(
5574        &mut self,
5575        token_type: i32,
5576        text: String,
5577        line: usize,
5578        column: usize,
5579    ) -> Result<TokenId, AntlrError> {
5580        self.input
5581            .insert(
5582                TokenSpec::explicit(token_type, text)
5583                    .with_span(usize::MAX, usize::MAX)
5584                    .with_byte_span(0, 0)
5585                    .with_position(line, column),
5586            )
5587            .map_err(|error| AntlrError::Unsupported(error.to_string()))
5588    }
5589
5590    /// Matches and consumes the current token when it has the expected token
5591    /// type.
5592    ///
5593    /// On success the consumed token is wrapped as a terminal parse-tree node.
5594    /// On mismatch the error carries vocabulary display names so diagnostics are
5595    /// stable across literal and symbolic token naming.
5596    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
5597        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5598            line: 0,
5599            column: 0,
5600            message: "missing current token".to_owned(),
5601            offending: None,
5602        })?;
5603        let current_type = self.token_type_for_id(current);
5604        if current_type == token_type {
5605            self.reset_generated_recovery_state();
5606            self.consume();
5607            Ok(self.terminal_tree(current))
5608        } else {
5609            Err(AntlrError::MismatchedInput {
5610                expected: self.vocabulary().display_name(token_type),
5611                found: self.vocabulary().display_name(current_type),
5612            })
5613        }
5614    }
5615
5616    /// Matches a token from generated recursive-descent code, including ANTLR's
5617    /// single-token insertion recovery when the active rule context can legally
5618    /// continue at the current input symbol.
5619    pub fn match_token_recovering(
5620        &mut self,
5621        token_type: i32,
5622        follow_state: usize,
5623        atn: &Atn,
5624    ) -> Result<GeneratedMatch, AntlrError> {
5625        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5626            line: 0,
5627            column: 0,
5628            message: "missing current token".to_owned(),
5629            offending: None,
5630        })?;
5631        let current_type = self.token_type_for_id(current);
5632        if current_type == token_type {
5633            self.generated_sync_expected = None;
5634            self.reset_generated_recovery_state();
5635            let consumed_eof = current_type == TOKEN_EOF;
5636            self.consume();
5637            return Ok(GeneratedMatch {
5638                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5639                consumed_eof,
5640            });
5641        }
5642        let mut expected_symbols = BTreeSet::new();
5643        expected_symbols.insert(token_type);
5644        self.recover_generated_match(
5645            current,
5646            GeneratedExpectedSymbols::Tree(&expected_symbols),
5647            follow_state,
5648            atn,
5649            |symbol| symbol == token_type,
5650        )
5651    }
5652
5653    pub fn match_set_recovering(
5654        &mut self,
5655        intervals: &[(i32, i32)],
5656        follow_state: usize,
5657        atn: &Atn,
5658    ) -> Result<GeneratedMatch, AntlrError> {
5659        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5660            line: 0,
5661            column: 0,
5662            message: "missing current token".to_owned(),
5663            offending: None,
5664        })?;
5665        let current_type = self.token_type_for_id(current);
5666        if interval_set_contains(intervals, current_type) {
5667            self.generated_sync_expected = None;
5668            self.reset_generated_recovery_state();
5669            let consumed_eof = current_type == TOKEN_EOF;
5670            self.consume();
5671            return Ok(GeneratedMatch {
5672                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5673                consumed_eof,
5674            });
5675        }
5676        let expected_symbols = interval_symbols(intervals);
5677        self.recover_generated_match(
5678            current,
5679            GeneratedExpectedSymbols::Tree(&expected_symbols),
5680            follow_state,
5681            atn,
5682            |symbol| interval_set_contains(intervals, symbol),
5683        )
5684    }
5685
5686    pub fn match_token_set_recovering(
5687        &mut self,
5688        set: ParserIntervalSet<'_>,
5689        follow_state: usize,
5690        atn: &Atn,
5691    ) -> Result<GeneratedMatch, AntlrError> {
5692        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5693            line: 0,
5694            column: 0,
5695            message: "missing current token".to_owned(),
5696            offending: None,
5697        })?;
5698        let current_type = self.token_type_for_id(current);
5699        if set.contains(current_type) {
5700            self.generated_sync_expected = None;
5701            self.reset_generated_recovery_state();
5702            let consumed_eof = current_type == TOKEN_EOF;
5703            self.consume();
5704            return Ok(GeneratedMatch {
5705                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5706                consumed_eof,
5707            });
5708        }
5709        self.recover_generated_match(
5710            current,
5711            GeneratedExpectedSymbols::TokenSet(set),
5712            follow_state,
5713            atn,
5714            |symbol| set.contains(symbol),
5715        )
5716    }
5717
5718    pub fn match_not_set_recovering(
5719        &mut self,
5720        intervals: &[(i32, i32)],
5721        min_vocabulary: i32,
5722        max_vocabulary: i32,
5723        follow_state: usize,
5724        atn: &Atn,
5725    ) -> Result<GeneratedMatch, AntlrError> {
5726        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5727            line: 0,
5728            column: 0,
5729            message: "missing current token".to_owned(),
5730            offending: None,
5731        })?;
5732        let current_type = self.token_type_for_id(current);
5733        if (min_vocabulary..=max_vocabulary).contains(&current_type)
5734            && !interval_set_contains(intervals, current_type)
5735        {
5736            self.generated_sync_expected = None;
5737            self.reset_generated_recovery_state();
5738            let consumed_eof = current_type == TOKEN_EOF;
5739            self.consume();
5740            return Ok(GeneratedMatch {
5741                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5742                consumed_eof,
5743            });
5744        }
5745        let expected_symbols =
5746            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
5747        self.recover_generated_match(
5748            current,
5749            GeneratedExpectedSymbols::Tree(&expected_symbols),
5750            follow_state,
5751            atn,
5752            |symbol| {
5753                (min_vocabulary..=max_vocabulary).contains(&symbol)
5754                    && !interval_set_contains(intervals, symbol)
5755            },
5756        )
5757    }
5758
5759    pub fn match_not_token_set_recovering(
5760        &mut self,
5761        set: ParserIntervalSet<'_>,
5762        min_vocabulary: i32,
5763        max_vocabulary: i32,
5764        follow_state: usize,
5765        atn: &Atn,
5766    ) -> Result<GeneratedMatch, AntlrError> {
5767        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5768            line: 0,
5769            column: 0,
5770            message: "missing current token".to_owned(),
5771            offending: None,
5772        })?;
5773        let current_type = self.token_type_for_id(current);
5774        if (min_vocabulary..=max_vocabulary).contains(&current_type) && !set.contains(current_type)
5775        {
5776            self.generated_sync_expected = None;
5777            self.reset_generated_recovery_state();
5778            let consumed_eof = current_type == TOKEN_EOF;
5779            self.consume();
5780            return Ok(GeneratedMatch {
5781                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5782                consumed_eof,
5783            });
5784        }
5785        self.recover_generated_match(
5786            current,
5787            GeneratedExpectedSymbols::TokenSetComplement {
5788                set,
5789                min_vocabulary,
5790                max_vocabulary,
5791            },
5792            follow_state,
5793            atn,
5794            |symbol| (min_vocabulary..=max_vocabulary).contains(&symbol) && !set.contains(symbol),
5795        )
5796    }
5797
5798    fn recover_generated_match(
5799        &mut self,
5800        current: TokenId,
5801        expected_symbols: GeneratedExpectedSymbols<'_>,
5802        follow_state: usize,
5803        atn: &Atn,
5804        matches: impl Fn(i32) -> bool,
5805    ) -> Result<GeneratedMatch, AntlrError> {
5806        let expected_display = expected_symbols.display(self.vocabulary());
5807        let (current_type, current_line, current_column, current_display) = {
5808            let token = self
5809                .input
5810                .token_view(current)
5811                .expect("current token ID should be valid");
5812            (
5813                token.token_type(),
5814                token.line(),
5815                token.column(),
5816                token_input_display(&token),
5817            )
5818        };
5819        if self.bail_on_error {
5820            return Err(AntlrError::ParserError {
5821                line: current_line,
5822                column: current_column,
5823                message: format!("mismatched input {current_display} expecting {expected_display}"),
5824                offending: Some(current),
5825            });
5826        }
5827        if current_type != TOKEN_EOF
5828            && let Some(next) = self.input.lt_id(2)
5829            && matches(self.token_type_for_id(next))
5830        {
5831            let message =
5832                format!("extraneous input {current_display} expecting {expected_display}");
5833            self.push_generated_parser_diagnostic(ParserDiagnostic {
5834                line: current_line,
5835                column: current_column,
5836                message,
5837                offending: Some(current),
5838            });
5839            self.record_syntax_errors(1);
5840            self.generated_sync_expected = None;
5841            // Single-token deletion: skip `current`, then accept `next`. The
5842            // accepted token can be EOF only if it is a real EOF terminal.
5843            let consumed_eof = self.token_type_for_id(next) == TOKEN_EOF;
5844            self.consume();
5845            self.consume();
5846            self.reset_generated_recovery_state();
5847            return Ok(GeneratedMatch {
5848                children: GeneratedMatchChildren::Many(vec![
5849                    self.error_tree(current),
5850                    self.terminal_tree(next),
5851                ]),
5852                consumed_eof,
5853            });
5854        }
5855        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
5856        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
5857        // *after* the current element can consume the current symbol. At EOF that
5858        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
5859        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
5860        // when EOF merely leaks in from the empty enclosing context (as in
5861        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
5862        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
5863        // so consult the follow state's OWN expected set for the explicit case.
5864        let follow_explicitly_expects_eof = current_type == TOKEN_EOF
5865            && self
5866                .cached_state_expected_symbols(atn, follow_state)
5867                .contains(&TOKEN_EOF);
5868        if follow_symbols.contains(&current_type)
5869            && (current_type != TOKEN_EOF
5870                || self.rule_context_stack.len() > 1
5871                || expected_symbols.is_empty()
5872                || follow_explicitly_expects_eof)
5873        {
5874            let message = format!("missing {expected_display} at {current_display}");
5875            self.push_generated_parser_diagnostic(ParserDiagnostic {
5876                line: current_line,
5877                column: current_column,
5878                message,
5879                offending: Some(current),
5880            });
5881            self.record_syntax_errors(1);
5882            self.generated_sync_expected = None;
5883            let token_type = expected_symbols.first().unwrap_or(TOKEN_EOF);
5884            let missing_display = expected_symbol_display(token_type, self.vocabulary());
5885            let token = self.insert_synthetic_token(
5886                token_type,
5887                format!("<missing {missing_display}>"),
5888                current_line,
5889                current_column,
5890            )?;
5891            // Single-token insertion synthesizes a missing token and consumes
5892            // nothing, so no EOF terminal is consumed even when the lookahead is
5893            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
5894            // from recording EOF as the rule stop on this recovery path.
5895            return Ok(GeneratedMatch {
5896                children: GeneratedMatchChildren::One(self.error_tree(token)),
5897                consumed_eof: false,
5898            });
5899        }
5900        let mismatch_expected_display = self
5901            .generated_sync_expected
5902            .take()
5903            .map_or(expected_display, |symbols| {
5904                expected_symbols_display_iter(symbols.symbols(), self.vocabulary())
5905            });
5906        Err(AntlrError::ParserError {
5907            line: current_line,
5908            column: current_column,
5909            message: format!(
5910                "mismatched input {current_display} expecting {mismatch_expected_display}"
5911            ),
5912            offending: Some(current),
5913        })
5914    }
5915
5916    fn generated_recovery_follow_symbols(
5917        &mut self,
5918        atn: &Atn,
5919        follow_state: usize,
5920    ) -> BTreeSet<i32> {
5921        let mut follow = self
5922            .cached_state_expected_symbols(atn, follow_state)
5923            .as_ref()
5924            .clone();
5925        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
5926            follow.extend(self.context_expected_symbols(atn));
5927        }
5928        follow
5929    }
5930
5931    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
5932        self.match_token(TOKEN_EOF)
5933    }
5934
5935    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
5936        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
5937    }
5938
5939    pub fn match_not_set(
5940        &mut self,
5941        intervals: &[(i32, i32)],
5942        min_vocabulary: i32,
5943        max_vocabulary: i32,
5944    ) -> Result<ParseTree, AntlrError> {
5945        self.match_interval_condition(intervals, |symbol| {
5946            (min_vocabulary..=max_vocabulary).contains(&symbol)
5947                && !interval_set_contains(intervals, symbol)
5948        })
5949    }
5950
5951    fn match_interval_condition(
5952        &mut self,
5953        intervals: &[(i32, i32)],
5954        matches: impl FnOnce(i32) -> bool,
5955    ) -> Result<ParseTree, AntlrError> {
5956        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5957            line: 0,
5958            column: 0,
5959            message: "missing current token".to_owned(),
5960            offending: None,
5961        })?;
5962        let current_type = self.token_type_for_id(current);
5963        if matches(current_type) {
5964            self.reset_generated_recovery_state();
5965            self.consume();
5966            Ok(self.terminal_tree(current))
5967        } else {
5968            Err(AntlrError::MismatchedInput {
5969                expected: self.interval_display(intervals),
5970                found: self.vocabulary().display_name(current_type),
5971            })
5972        }
5973    }
5974
5975    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
5976        let values = intervals
5977            .iter()
5978            .map(|(start, stop)| {
5979                if start == stop {
5980                    self.vocabulary().display_name(*start)
5981                } else {
5982                    format!(
5983                        "{}..{}",
5984                        self.vocabulary().display_name(*start),
5985                        self.vocabulary().display_name(*stop)
5986                    )
5987                }
5988            })
5989            .collect::<Vec<_>>()
5990            .join(", ");
5991        format!("{{{values}}}")
5992    }
5993
5994    pub fn rule_node(&mut self, context: ParserRuleContext) -> ParseTree {
5995        if self.build_parse_trees {
5996            self.tree.finish_rule(context)
5997        } else {
5998            NodeId::placeholder()
5999        }
6000    }
6001
6002    /// Reports whether the generated rule dispatch should sample native stack
6003    /// capacity before descending into the next rule body.
6004    ///
6005    /// Generated recursive-descent methods otherwise map unbounded grammar
6006    /// nesting straight onto native call depth; sampling every
6007    /// [`GENERATED_RULE_STACK_CHECK_INTERVAL`] rule-context frames keeps the
6008    /// hot path free of per-call probes while guaranteeing a check runs before
6009    /// the red zone can be crossed.
6010    #[must_use]
6011    pub const fn generated_rule_stack_check_due(&self) -> bool {
6012        self.rule_context_stack
6013            .len()
6014            .is_multiple_of(GENERATED_RULE_STACK_CHECK_INTERVAL)
6015    }
6016
6017    /// Returns the positioned error to abort with when the configured
6018    /// rule-nesting depth cap would be exceeded by one more level, or `None`
6019    /// to keep parsing.
6020    ///
6021    /// Generated rule dispatch calls this before deepening — ahead of the
6022    /// rule-frame push at the dispatch boundary and ahead of each
6023    /// left-recursive expansion — letting callers parsing untrusted input
6024    /// bound CPU and tree memory ([`Parser::set_max_rule_depth`]). The
6025    /// inline fast path is one `Option` check when no cap is set (the
6026    /// default) and one addition plus compare when one is; only an actual
6027    /// violation leaves the inline path.
6028    ///
6029    /// The violation is sticky: rule-level recovery absorbs the returned
6030    /// error like any other rule failure and would otherwise keep spending
6031    /// the very resources the cap exists to bound, so every check after the
6032    /// first violation fails until [`Self::take_rule_depth_error`] drains it
6033    /// at the top-level entry.
6034    #[inline]
6035    pub fn rule_depth_cap_violation(&mut self) -> Option<AntlrError> {
6036        let max = self.max_rule_depth?;
6037        // Left-recursive operator iterations deepen the tree without pushing
6038        // a rule frame, so they count alongside the rule-context stack.
6039        if self.rule_depth_error.is_none()
6040            && self.rule_context_stack.len() + self.recursion_expansions < max
6041        {
6042            return None;
6043        }
6044        Some(self.rule_depth_cap_violation_cold(max))
6045    }
6046
6047    #[cold]
6048    fn rule_depth_cap_violation_cold(&mut self, max: usize) -> AntlrError {
6049        if let Some(error) = &self.rule_depth_error {
6050            return error.clone();
6051        }
6052        let current = self.input.lt(1);
6053        let (line, column) = current
6054            .as_ref()
6055            .map_or((0, 0), |token| (token.line(), token.column()));
6056        let error = AntlrError::ParserError {
6057            line,
6058            column,
6059            message: format!("rule nesting depth limit of {max} exceeded"),
6060            offending: current.as_ref().map(Token::token_id),
6061        };
6062        self.rule_depth_error = Some(error.clone());
6063        error
6064    }
6065
6066    /// Drains the sticky depth-cap violation recorded by
6067    /// [`Self::rule_depth_cap_violation`], if any.
6068    ///
6069    /// Generated top-level rule entries call this after recognition so a
6070    /// recovered parse that crossed the cap still fails, and so a reused
6071    /// parser starts its next parse clean.
6072    pub const fn take_rule_depth_error(&mut self) -> Option<AntlrError> {
6073        self.rule_depth_error.take()
6074    }
6075
6076    /// Reports whether a rule-nesting depth cap is configured.
6077    ///
6078    /// Generated dispatch consults this when selecting between the guarded
6079    /// recursive-descent body and the ATN-preferred interpreted fast path:
6080    /// only the generated body enforces the cap, so a configured bound
6081    /// overrides the performance preference.
6082    #[must_use]
6083    pub const fn has_rule_depth_cap(&self) -> bool {
6084        self.max_rule_depth.is_some()
6085    }
6086
6087    /// Registers a listener for committed rule enter/exit events during
6088    /// recognition (ANTLR's `addParseListener`). See [`ParseListener`] for
6089    /// the delivery contract.
6090    pub fn add_parse_listener<L>(&mut self, listener: L)
6091    where
6092        L: ParseListener + 'static,
6093    {
6094        self.parse_listeners
6095            .push(ParseListenerSlot(Box::new(listener)));
6096    }
6097
6098    /// Removes every registered parse listener and returns them, dropping any
6099    /// sticky abort a removed listener had requested.
6100    ///
6101    /// Returning the boxed listeners gives callers back the state they
6102    /// accumulated (depth counters, collected events) without threading
6103    /// shared handles through the listener.
6104    pub fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
6105        self.parse_listener_abort = None;
6106        self.parse_listeners.drain(..).map(|slot| slot.0).collect()
6107    }
6108
6109    /// Reports whether any parse listener is registered.
6110    ///
6111    /// Generated dispatch consults this alongside [`Self::has_rule_depth_cap`]
6112    /// when choosing between the generated body (which fires events) and the
6113    /// ATN-preferred interpreted fast path (which does not).
6114    #[must_use]
6115    pub const fn has_parse_listeners(&self) -> bool {
6116        !self.parse_listeners.is_empty()
6117    }
6118
6119    /// Reports whether semantic hooks may override interpreted decisions.
6120    ///
6121    /// Generated parsers use this to keep adaptive performance routing from
6122    /// changing parse semantics after a decision DFA becomes warm.
6123    #[doc(hidden)]
6124    #[must_use]
6125    pub fn observes_parser_decisions(&self) -> bool {
6126        self.semantic_hooks.observes_parser_decisions()
6127    }
6128
6129    /// Fires `enter_every_rule` on registered parse listeners, returning the
6130    /// abort error if any listener requested one.
6131    ///
6132    /// Generated rule dispatch calls this after the depth-cap probe and
6133    /// before the rule body runs; the generated left-recursive loop calls it
6134    /// once per operator expansion, mirroring upstream ANTLR's simulated
6135    /// rule-entry event for `pushNewRecursionContext`. A listener abort is
6136    /// sticky exactly like a depth-cap violation: rule-level recovery absorbs
6137    /// the returned error, so the flag holds until the top-level entry drains
6138    /// it via [`Self::take_parse_listener_abort`] and fails the parse.
6139    pub fn parse_listener_enter_rule(&mut self, rule_index: usize) -> Option<AntlrError> {
6140        if self.parse_listeners.is_empty() {
6141            return None;
6142        }
6143        self.parse_listener_enter_rule_dispatch(rule_index)
6144    }
6145
6146    fn parse_listener_enter_rule_dispatch(&mut self, rule_index: usize) -> Option<AntlrError> {
6147        if let Some(error) = &self.parse_listener_abort {
6148            return Some(error.clone());
6149        }
6150        let event = EnterRuleEvent {
6151            rule_index,
6152            current: self.input.lt(1),
6153        };
6154        // Split borrows: the token view borrows the input while listeners
6155        // need `&mut`, so listeners are taken out for the dispatch. Listener
6156        // methods have no parser access and cannot observe the absence.
6157        let mut listeners = std::mem::take(&mut self.parse_listeners);
6158        let mut abort = None;
6159        for slot in &mut listeners {
6160            if let Err(error) = slot.0.enter_every_rule(&event) {
6161                abort = Some(error);
6162                break;
6163            }
6164        }
6165        self.parse_listeners = listeners;
6166        if let Some(error) = abort {
6167            self.parse_listener_abort = Some(error.clone());
6168            return Some(error);
6169        }
6170        None
6171    }
6172
6173    /// Fires `exit_every_rule` on registered parse listeners.
6174    ///
6175    /// Generated rule bodies call this on every exit path — success and
6176    /// recovery alike — keeping enter/exit pairs balanced, and the generated
6177    /// left-recursive loop calls it once per operator expansion when the rule
6178    /// finishes unrolling.
6179    pub fn parse_listener_exit_rule(&mut self, rule_index: usize) {
6180        if self.parse_listeners.is_empty() {
6181            return;
6182        }
6183        // Reverse registration order, matching upstream ANTLR
6184        // (`Parser.triggerExitRuleEvent` walks listeners back to front).
6185        for slot in self.parse_listeners.iter_mut().rev() {
6186            slot.0.exit_every_rule(rule_index);
6187        }
6188    }
6189
6190    /// Drains the sticky parse-listener abort recorded by
6191    /// [`Self::parse_listener_enter_rule`], if any.
6192    ///
6193    /// Generated top-level rule entries call this after recognition so an
6194    /// aborted parse fails even when recovery produced a tree, and so a
6195    /// reused parser starts its next parse clean.
6196    pub const fn take_parse_listener_abort(&mut self) -> Option<AntlrError> {
6197        self.parse_listener_abort.take()
6198    }
6199
6200    /// Drains every sticky parse abort — the depth-cap violation and the
6201    /// parse-listener abort — returning the depth error preferentially.
6202    ///
6203    /// Generated top-level rule entries call this on both exit paths: the
6204    /// recorded abort wins over errors derived from it (recovery may have
6205    /// absorbed the aborted rule and failed differently later), a recovered
6206    /// `Ok` tree still fails when an abort was recorded, and draining leaves
6207    /// the instance clean for the next entry-rule call.
6208    pub fn take_parse_abort(&mut self) -> Option<AntlrError> {
6209        if let Some(error) = self.rule_depth_error.take() {
6210            self.parse_listener_abort = None;
6211            return Some(error);
6212        }
6213        self.parse_listener_abort.take()
6214    }
6215
6216    /// Enters a generated parser rule and returns the context object the
6217    /// generated method should populate.
6218    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
6219        self.set_state(state);
6220        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
6221        self.rule_context_stack.push(RuleContextFrame {
6222            rule_index,
6223            invoking_state,
6224        });
6225        self.advance_rule_context_version();
6226        let start_index = self.current_visible_index();
6227        let mut context = ParserRuleContext::new(rule_index, invoking_state);
6228        if let Some(token) = self.token_id_at(start_index) {
6229            self.set_context_start(&mut context, token);
6230        }
6231        context
6232    }
6233
6234    /// Records the ATN source state for the next generated rule invocation.
6235    ///
6236    /// ANTLR's full-context prediction reconstructs caller follow states from
6237    /// each active rule context's invoking state. Generated Rust rule methods are
6238    /// plain functions, so the caller supplies that ATN state just before making a
6239    /// rule call; `enter_rule` consumes it when the callee starts.
6240    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
6241        let marker = self.pending_invoking_states.len();
6242        self.pending_invoking_states.push(invoking_state);
6243        marker
6244    }
6245
6246    /// Discards an invoking-state marker if the callee did not consume it.
6247    pub fn discard_invoking_state(&mut self, marker: usize) {
6248        self.pending_invoking_states.truncate(marker);
6249    }
6250
6251    /// Exits the current generated parser rule.
6252    pub fn exit_rule(&mut self) {
6253        self.rule_context_stack.pop();
6254        self.advance_rule_context_version();
6255    }
6256
6257    /// Returns caller follow states for interning in a parser ATN simulator's
6258    /// prediction store. States are yielded outermost to innermost.
6259    pub fn prediction_context_return_states<'a>(
6260        &'a self,
6261        atn: &'a Atn,
6262    ) -> impl DoubleEndedIterator<Item = usize> + 'a {
6263        self.rule_context_stack.iter().skip(1).filter_map(|frame| {
6264            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
6265                return None;
6266            };
6267            let Some(Transition::Rule { follow_state, .. }) = atn
6268                .state(state_number)
6269                .and_then(|state| state.transitions().first())
6270                .map(ParserTransition::data)
6271            else {
6272                return None;
6273            };
6274            Some(follow_state)
6275        })
6276    }
6277
6278    /// Returns a generation that changes whenever the active rule stack changes.
6279    ///
6280    /// A parser ATN simulator uses this to reuse an interned outer prediction
6281    /// context while generated predictions remain in the same rule context.
6282    pub const fn rule_context_version(&self) -> usize {
6283        self.rule_context_version
6284    }
6285
6286    const fn advance_rule_context_version(&mut self) {
6287        self.rule_context_version = self.rule_context_version.wrapping_add(1);
6288    }
6289
6290    /// Adds a generated parser child only when parse-tree construction is
6291    /// enabled. The match is recorded on the context either way (via `add_child`,
6292    /// or `note_matched_child` when trees are off) so generated recovery can tell
6293    /// whether the rule has matched anything yet without depending on `children`.
6294    pub fn add_parse_child(&mut self, context: &mut ParserRuleContext, child: ParseTree) {
6295        if self.build_parse_trees {
6296            self.tree.add_child(context, child);
6297        } else {
6298            context.note_matched_child();
6299        }
6300    }
6301
6302    fn release_tree_scratch_if_idle(&mut self) {
6303        if self.rule_context_stack.is_empty() {
6304            self.tree.release_scratch();
6305        }
6306    }
6307
6308    /// Finishes a generated parser rule and returns its parse-tree node.
6309    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
6310        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6311        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
6312            self.set_context_stop(&mut context, token);
6313        }
6314        let node = self.rule_node(context);
6315        self.exit_rule();
6316        self.release_tree_scratch_if_idle();
6317        node
6318    }
6319
6320    /// Recovers a generated rule catch block after a committed mismatch.
6321    ///
6322    /// ANTLR's generated parsers catch recognition errors inside each rule,
6323    /// report the original error, then consume unexpected tokens until the
6324    /// caller's recovery set can resume. Tokens consumed during recovery become
6325    /// error nodes in the current rule context.
6326    pub fn recover_generated_rule(
6327        &mut self,
6328        context: &mut ParserRuleContext,
6329        atn: &Atn,
6330        error: AntlrError,
6331    ) {
6332        let diagnostic = self.generated_rule_error_diagnostic(error);
6333        self.push_generated_parser_diagnostic(diagnostic);
6334        self.generated_sync_expected = None;
6335        let error_index = self.input.index();
6336        let error_state = self.data.state();
6337        // Match ANTLR's lastErrorIndex/lastErrorStates failsafe: a recovery
6338        // token can also be in the caller's follow set, leaving the cursor
6339        // unchanged and allowing generated outer decisions to revisit the same
6340        // failed state forever.
6341        if self.generated_recovery_error_index == Some(error_index)
6342            && self.generated_recovery_error_states.contains(&error_state)
6343            && self.la(1) != TOKEN_EOF
6344            && let Some(token) = self.input.lt_id(1)
6345        {
6346            self.consume();
6347            let child = self.error_tree(token);
6348            self.add_parse_child(context, child);
6349        }
6350        let recovery_index = self.input.index();
6351        if self.generated_recovery_error_index != Some(recovery_index) {
6352            self.generated_recovery_error_index = Some(recovery_index);
6353            self.generated_recovery_error_states.clear();
6354        }
6355        self.generated_recovery_error_states.insert(error_state);
6356        let recovery_symbols = self.context_expected_symbols(atn);
6357        loop {
6358            let symbol = self.la(1);
6359            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
6360                break;
6361            }
6362            let Some(token) = self.input.lt_id(1) else {
6363                break;
6364            };
6365            self.consume();
6366            let child = self.error_tree(token);
6367            self.add_parse_child(context, child);
6368        }
6369        self.record_syntax_errors(1);
6370    }
6371
6372    fn reset_generated_recovery_state(&mut self) {
6373        if self.generated_recovery_error_index.is_some() {
6374            self.generated_recovery_error_index = None;
6375            self.generated_recovery_error_states.clear();
6376        }
6377    }
6378
6379    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
6380        if self
6381            .generated_parser_diagnostics
6382            .iter()
6383            .any(|existing| existing == &diagnostic)
6384        {
6385            return;
6386        }
6387        self.generated_parser_diagnostics.push(diagnostic);
6388    }
6389
6390    fn generated_rule_error_diagnostic(&self, error: AntlrError) -> ParserDiagnostic {
6391        match error {
6392            // The anchor recorded where the error was built wins over the
6393            // current lookahead: prediction restores the cursor, so lt(1)
6394            // here can point at the decision start rather than the error.
6395            AntlrError::ParserError {
6396                line,
6397                column,
6398                message,
6399                offending,
6400            } => ParserDiagnostic {
6401                line,
6402                column,
6403                message,
6404                offending,
6405            },
6406            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
6407                self.input.lt(1),
6408                format!("mismatched input {found} expecting {expected}"),
6409            ),
6410            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
6411                self.input.lt(1),
6412                format!("no viable alternative at input {input}"),
6413            ),
6414            AntlrError::LexerError {
6415                line,
6416                column,
6417                message,
6418            } => ParserDiagnostic {
6419                line,
6420                column,
6421                message,
6422                offending: None,
6423            },
6424            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
6425        }
6426    }
6427
6428    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
6429    pub fn finish_recursion_rule(
6430        &mut self,
6431        mut context: ParserRuleContext,
6432        consumed_eof: bool,
6433    ) -> ParseTree {
6434        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6435        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
6436            self.set_context_stop(&mut context, token);
6437        }
6438        let node = self.rule_node(context);
6439        self.unroll_recursion_context();
6440        self.release_tree_scratch_if_idle();
6441        node
6442    }
6443
6444    /// Enters a generated left-recursive rule at `precedence`.
6445    pub fn enter_recursion_rule(
6446        &mut self,
6447        state: isize,
6448        rule_index: usize,
6449        precedence: i32,
6450    ) -> ParserRuleContext {
6451        self.precedence_stack.push(precedence);
6452        self.recursion_expansion_marks
6453            .push(self.recursion_expansions);
6454        self.enter_rule(state, rule_index)
6455    }
6456
6457    /// Replaces the current context while expanding a left-recursive rule.
6458    pub fn push_new_recursion_context(
6459        &mut self,
6460        state: isize,
6461        rule_index: usize,
6462    ) -> ParserRuleContext {
6463        self.set_state(state);
6464        // Counts toward the depth cap: upstream treats this as rule entry
6465        // (`Parser.pushNewRecursionContext` fires `triggerEnterRuleEvent`).
6466        self.recursion_expansions += 1;
6467        ParserRuleContext::new(rule_index, state)
6468    }
6469
6470    /// Wraps the previous left-recursive context before parsing the next
6471    /// recursive operator alternative.
6472    pub fn push_new_recursion_context_with_previous(
6473        &mut self,
6474        state: isize,
6475        rule_index: usize,
6476        current: &mut ParserRuleContext,
6477    ) {
6478        self.set_state(state);
6479        // Counts toward the depth cap: each operator iteration deepens the
6480        // parse tree one level without pushing a rule frame, and upstream
6481        // fires a rule-entry listener event for it. The parse-listener enter
6482        // event for this expansion fires from the generated loop's probe
6483        // just before this call, where a listener abort can propagate.
6484        self.recursion_expansions += 1;
6485        if let Some(stop) = self
6486            .rule_stop_token_index(self.input.index(), false)
6487            .and_then(|index| self.token_id_at(index))
6488        {
6489            self.set_context_stop(current, stop);
6490        }
6491        let invoking_state = current.invoking_state();
6492        let start = current.start_id();
6493        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
6494        if start.is_some() {
6495            replacement.set_start_from_context(current);
6496        }
6497        let previous = std::mem::replace(current, replacement);
6498        if self.build_parse_trees {
6499            let previous = self.rule_node(previous);
6500            self.tree.add_child(current, previous);
6501        }
6502    }
6503
6504    /// Leaves a generated left-recursive rule.
6505    pub fn unroll_recursion_context(&mut self) {
6506        if self.precedence_stack.len() > 1 {
6507            self.precedence_stack.pop();
6508        }
6509        // Parse-listener exits for expansions fire inside the generated
6510        // operator loop (top of each pass, upstream's `recRuleSetPrevCtx`),
6511        // and the dispatch wrapper's exit covers the final live context —
6512        // upstream's `unrollRecursionContexts` walks exactly one link, so no
6513        // batched exits happen here. Only the depth-cap accounting rewinds.
6514        if let Some(mark) = self.recursion_expansion_marks.pop() {
6515            self.recursion_expansions = mark;
6516        }
6517        self.exit_rule();
6518    }
6519
6520    /// Predicts a generated left-recursive loop from one-token lookahead.
6521    ///
6522    /// `Some(true)` enters the operator alternative, `Some(false)` exits, and
6523    /// `None` means caller overlap, a dangerous multi-token prefix, or an
6524    /// unresolved semantic predicate requires full `StarLoopEntry` adaptive
6525    /// prediction (which includes the exit alt and precedence filtering).
6526    ///
6527    /// Single-token operators and multi-token prefixes that do not shadow a
6528    /// lower-precedence single-token operator keep the one-token enter fast path.
6529    ///
6530    /// Multi-token prefixes that **do** shadow a lower-precedence single-token
6531    /// operator must not force enter; the adaptive decision may need to select
6532    /// the loop exit instead.
6533    pub fn left_recursive_loop_enter_prediction(
6534        &mut self,
6535        atn: &Atn,
6536        state_number: usize,
6537        precedence: i32,
6538    ) -> Option<bool> {
6539        let symbol = self.la(1);
6540        if symbol == TOKEN_EOF {
6541            return Some(false);
6542        }
6543        let operator_lookahead =
6544            Self::cached_left_recursive_operator_lookahead(atn, state_number, precedence);
6545        let can_single = operator_lookahead.single_token.contains(symbol);
6546        let can_multi = operator_lookahead.multi_token_prefix.contains(symbol);
6547        let can_predicate = operator_lookahead.predicate_dependent.contains(symbol);
6548        if !can_single && !can_multi && !can_predicate {
6549            return Some(false);
6550        }
6551        if can_predicate && !can_single {
6552            return None;
6553        }
6554        // Multi-token-only at this precedence, but the same symbol is a
6555        // single-token operator at precedence 0: defer so exit can win when the
6556        // multi-token sequence does not actually match (e.g. `>` vs `>>`).
6557        if !can_single && can_multi && precedence > 0 {
6558            let baseline = Self::cached_left_recursive_operator_lookahead(atn, state_number, 0);
6559            if baseline.single_token.contains(symbol) {
6560                return None;
6561            }
6562        }
6563        let atn_key = SharedAtnCacheKey::for_atn(atn);
6564        let cached_overlap = self
6565            .left_recursive_caller_overlap_cache
6566            .iter()
6567            .flatten()
6568            .find(|entry| {
6569                entry.atn_key == atn_key
6570                    && entry.state_number == state_number
6571                    && entry.symbol == symbol
6572                    && entry.context_version == self.rule_context_version
6573            })
6574            .map(|entry| entry.overlaps);
6575        let caller_overlaps = cached_overlap.unwrap_or_else(|| {
6576            let overlaps = caller_context_can_match_symbol_before_state(
6577                atn,
6578                self.prediction_context_return_states(atn),
6579                state_number,
6580                symbol,
6581            );
6582            if let Some(slot) = self
6583                .left_recursive_caller_overlap_cache
6584                .iter_mut()
6585                .find(|slot| slot.is_none())
6586            {
6587                *slot = Some(LeftRecursiveCallerOverlap {
6588                    atn_key,
6589                    state_number,
6590                    symbol,
6591                    context_version: self.rule_context_version,
6592                    overlaps,
6593                });
6594            }
6595            overlaps
6596        });
6597        if caller_overlaps {
6598            return None;
6599        }
6600        Some(true)
6601    }
6602
6603    fn cached_left_recursive_operator_lookahead(
6604        atn: &Atn,
6605        state_number: usize,
6606        precedence: i32,
6607    ) -> Rc<LeftRecursiveOperatorLookahead> {
6608        with_shared_atn_caches(atn, |cache| {
6609            let key = (state_number, precedence);
6610            if let Some(cached) = cache.left_recursive_operator_lookahead.get(&key) {
6611                return Rc::clone(cached);
6612            }
6613            let lookahead = Rc::new(left_recursive_operator_lookahead(
6614                atn,
6615                state_number,
6616                precedence,
6617            ));
6618            cache
6619                .left_recursive_operator_lookahead
6620                .insert(key, Rc::clone(&lookahead));
6621            lookahead
6622        })
6623    }
6624
6625    /// Checks whether a generated left-recursive loop can unambiguously enter
6626    /// its operator alternative from one-token lookahead.
6627    pub fn left_recursive_loop_enter_matches(
6628        &mut self,
6629        atn: &Atn,
6630        state_number: usize,
6631        precedence: i32,
6632    ) -> bool {
6633        self.left_recursive_loop_enter_prediction(atn, state_number, precedence) == Some(true)
6634    }
6635
6636    /// Implements generated `precpred(_ctx, k)` checks.
6637    pub fn precpred(&self, precedence: i32) -> bool {
6638        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
6639    }
6640
6641    /// Evaluates a generated parser semantic predicate at the current input
6642    /// position.
6643    pub fn parser_semantic_predicate_matches(
6644        &mut self,
6645        predicates: &[(usize, usize, ParserPredicate)],
6646        rule_index: usize,
6647        pred_index: usize,
6648    ) -> bool {
6649        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
6650    }
6651
6652    /// Evaluates a generated parser semantic predicate with the current integer
6653    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
6654    pub fn parser_semantic_predicate_matches_with_local(
6655        &mut self,
6656        predicates: &[(usize, usize, ParserPredicate)],
6657        rule_index: usize,
6658        pred_index: usize,
6659        local_int_arg: i32,
6660    ) -> bool {
6661        self.parser_semantic_predicate_matches_inner(
6662            predicates,
6663            rule_index,
6664            pred_index,
6665            Some((rule_index, i64::from(local_int_arg))),
6666        )
6667    }
6668
6669    fn parser_semantic_predicate_matches_inner(
6670        &mut self,
6671        predicates: &[(usize, usize, ParserPredicate)],
6672        rule_index: usize,
6673        pred_index: usize,
6674        local_int_arg: Option<(usize, i64)>,
6675    ) -> bool {
6676        let index = self.input.index();
6677        let member_values = self.int_members.clone();
6678        self.parser_predicate_matches(PredicateEval {
6679            index,
6680            rule_index,
6681            pred_index,
6682            predicates,
6683            semantics: None,
6684            context: None,
6685            local_int_arg,
6686            member_values: &member_values,
6687        })
6688    }
6689
6690    /// Evaluates a generated parser semantic predicate with access to the
6691    /// current generated rule context.
6692    pub fn parser_semantic_predicate_matches_with_context_and_local(
6693        &mut self,
6694        predicates: &[(usize, usize, ParserPredicate)],
6695        rule_index: usize,
6696        pred_index: usize,
6697        context: &ParserRuleContext,
6698        local_int_arg: i32,
6699    ) -> bool {
6700        let index = self.input.index();
6701        let member_values = self.int_members.clone();
6702        self.parser_predicate_matches(PredicateEval {
6703            index,
6704            rule_index,
6705            pred_index,
6706            predicates,
6707            semantics: None,
6708            context: Some(context),
6709            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6710            member_values: &member_values,
6711        })
6712    }
6713
6714    /// Evaluates a generated `SemIR` parser predicate with access to the current
6715    /// generated rule context.
6716    pub fn parser_semantic_ir_predicate_matches_with_context_and_local(
6717        &mut self,
6718        semantics: &ParserSemantics,
6719        rule_index: usize,
6720        pred_index: usize,
6721        context: &ParserRuleContext,
6722        local_int_arg: i32,
6723    ) -> bool {
6724        let index = self.input.index();
6725        let member_values = self.int_members.clone();
6726        self.parser_predicate_matches(PredicateEval {
6727            index,
6728            rule_index,
6729            pred_index,
6730            predicates: &[],
6731            semantics: Some(semantics),
6732            context: Some(context),
6733            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
6734            member_values: &member_values,
6735        })
6736    }
6737
6738    /// Returns a generated fail-option message for a parser semantic
6739    /// predicate coordinate.
6740    pub fn parser_semantic_predicate_failure_message(
6741        &self,
6742        rule_index: usize,
6743        pred_index: usize,
6744        predicates: &[(usize, usize, ParserPredicate)],
6745    ) -> Option<&'static str> {
6746        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
6747    }
6748
6749    /// Matches any non-EOF token.
6750    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
6751        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
6752            line: 0,
6753            column: 0,
6754            message: "missing current token".to_owned(),
6755            offending: None,
6756        })?;
6757        if self.token_type_for_id(current) == TOKEN_EOF {
6758            return Err(AntlrError::MismatchedInput {
6759                expected: "wildcard".to_owned(),
6760                found: self.vocabulary().display_name(TOKEN_EOF),
6761            });
6762        }
6763        self.reset_generated_recovery_state();
6764        self.consume();
6765        Ok(self.terminal_tree(current))
6766    }
6767
6768    /// Generated parser synchronization hook. The current interpreter owns
6769    /// recovery; direct generated methods can call this as a no-op until the
6770    /// generated recovery strategy is expanded.
6771    #[allow(clippy::unnecessary_wraps)]
6772    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
6773        self.set_state(state);
6774        Ok(())
6775    }
6776
6777    /// Synchronizes a generated parser decision against the ATN lookahead set.
6778    ///
6779    /// ANTLR generated parsers call the error strategy before optional and loop
6780    /// decisions. When the current token cannot start any alternative, follow a
6781    /// nullable exit, or be deleted before a later synchronization token, the
6782    /// generated Rust method reports that decision-level mismatch instead of
6783    /// descending into a child rule that cannot start at the current token.
6784    pub fn sync_decision(
6785        &mut self,
6786        atn: &Atn,
6787        state_number: usize,
6788        current_context_empty: bool,
6789        loop_back: bool,
6790    ) -> Result<Vec<ParseTree>, AntlrError> {
6791        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
6792        self.generated_sync_expected = None;
6793        let Some(state) = atn.state(state_number) else {
6794            return Ok(Vec::new());
6795        };
6796        let Some(rule_index) = state.rule_index() else {
6797            return Ok(Vec::new());
6798        };
6799        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index) else {
6800            return Ok(Vec::new());
6801        };
6802        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6803        let symbol = self.la(1);
6804        let mut has_expected_symbols = false;
6805        let mut nullable = false;
6806        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
6807        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
6808        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
6809        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
6810        // and worth deleting; an implicit-follow EOF means the loop should simply
6811        // exit and leave the token for the (absent) caller — matching ANTLR, which
6812        // exits the loop via prediction rather than consuming up to a synthetic EOF.
6813        let mut explicit_eof_expected = false;
6814        for transition in &entry.transitions {
6815            if transition.symbols.contains(symbol) {
6816                return Ok(Vec::new());
6817            }
6818            has_expected_symbols |= !transition.symbols.is_empty();
6819            nullable |= transition.nullable;
6820            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
6821        }
6822        // Happy path: a nullable decision exits when the symbol is in the
6823        // rule-stack follow set. Answer the membership question with an
6824        // early-exit walk; the full union below is only needed for the
6825        // mismatch/deletion diagnostics.
6826        if nullable && self.context_expected_contains(atn, symbol) {
6827            return Ok(Vec::new());
6828        }
6829        let context_expected = nullable.then(|| self.context_expected_token_set(atn));
6830        if !has_expected_symbols && context_expected.as_ref().is_none_or(TokenBitSet::is_empty) {
6831            return Ok(Vec::new());
6832        }
6833        let mut expected = TokenBitSet::default();
6834        for transition in &entry.transitions {
6835            expected.extend_from(&transition.symbols);
6836        }
6837        if let Some(context_expected) = context_expected {
6838            expected.extend_from(&context_expected);
6839        }
6840        let can_delete_in_place =
6841            !(nullable && current_context_empty && self.rule_context_stack.len() > 1);
6842        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
6843        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
6844        // least one iteration) does `consumeUntil` the follow set — multi-token
6845        // deletion, one error per skipped token across iterations; a loop ENTRY
6846        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
6847        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
6848        // unexpected token only when LA(2) is expected, otherwise report a mismatch
6849        // and leave recovery to the rule.
6850        //
6851        // The generated loop always presents the loop-ENTRY state to this method on
6852        // every pass, so `state.kind()` cannot distinguish entry from back; the caller
6853        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
6854        // an iteration has been taken, and true on a `+` loop's first sync since its
6855        // mandatory first element is iteration 1). Treating a loop entry as a
6856        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
6857        // `c`s, which ANTLR rejects with `mismatched input`).
6858        let loop_sync = loop_back;
6859        if symbol != TOKEN_EOF && can_delete_in_place {
6860            let mut cursor = self.input.index();
6861            let mut skipped = Vec::new();
6862            loop {
6863                let current = self.token_type_at(cursor);
6864                if current == TOKEN_EOF {
6865                    break;
6866                }
6867                skipped.push(cursor);
6868                let next = self.consume_index(cursor, current);
6869                if next == cursor {
6870                    break;
6871                }
6872                let next_symbol = self.token_type_at(next);
6873                // Stop (and delete the skipped tokens as error nodes) when the next
6874                // token is a real expected continuation. EOF counts only when it is
6875                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
6876                // genuinely extraneous and the generated EOF match consumes the real
6877                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
6878                // rule-follow) does NOT count — the loop must exit and leave the
6879                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
6880                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
6881                    explicit_eof_expected
6882                } else {
6883                    expected.contains(next_symbol)
6884                };
6885                if next_is_expected_stop {
6886                    let current_token = self.input.lt(1);
6887                    let expected_symbols = expected.to_btree_set();
6888                    let message = format!(
6889                        "extraneous input {} expecting {}",
6890                        current_token
6891                            .as_ref()
6892                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6893                        self.expected_symbols_display(&expected_symbols)
6894                    );
6895                    self.push_generated_parser_diagnostic(diagnostic_for_token(
6896                        current_token,
6897                        message,
6898                    ));
6899                    self.record_syntax_errors(1);
6900                    let mut children = Vec::with_capacity(skipped.len());
6901                    for index in skipped {
6902                        if let Some(token) = self.token_id_at(index) {
6903                            self.consume();
6904                            children.push(self.error_tree(token));
6905                        }
6906                    }
6907                    if !loop_sync {
6908                        self.reset_generated_recovery_state();
6909                    }
6910                    return Ok(children);
6911                }
6912                // A non-loop block entry deletes at most one token (single-token
6913                // deletion): if LA(2) is not expected, stop scanning so the mismatch
6914                // is reported at the first token instead of skipping ahead.
6915                if !loop_sync {
6916                    break;
6917                }
6918                cursor = next;
6919            }
6920        }
6921        if nullable {
6922            self.generated_sync_expected = Some(expected);
6923            return Ok(Vec::new());
6924        }
6925        let current = self.input.lt(1);
6926        let expected_symbols = expected.to_btree_set();
6927        Err(AntlrError::ParserError {
6928            line: current.as_ref().map(Token::line).unwrap_or_default(),
6929            column: current.as_ref().map(Token::column).unwrap_or_default(),
6930            message: format!(
6931                "mismatched input {} expecting {}",
6932                current
6933                    .as_ref()
6934                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
6935                self.expected_symbols_display(&expected_symbols)
6936            ),
6937            offending: current.as_ref().map(Token::token_id),
6938        })
6939    }
6940
6941    /// Returns a generated-parser prediction when one token of lookahead
6942    /// uniquely selects an alternative for `state_number`.
6943    ///
6944    /// This mirrors the interpreter's LL(1) commit point and lets generated
6945    /// recursive-descent methods avoid invoking the adaptive simulator for
6946    /// simple optional/block/loop decisions.
6947    pub fn ll1_decision_prediction(
6948        &mut self,
6949        atn: &Atn,
6950        state_number: usize,
6951    ) -> Option<ParserAtnPrediction> {
6952        let state = atn.state(state_number)?;
6953        if state.precedence_rule_decision() {
6954            return None;
6955        }
6956        let rule_stop = state
6957            .rule_index()
6958            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))?;
6959        let symbol = self.la(1);
6960        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
6961        ll1_greedy_alt(&entry, symbol, state.non_greedy()).map(|alt| ParserAtnPrediction {
6962            alt: alt + 1,
6963            requires_full_context: false,
6964            has_semantic_context: false,
6965            diagnostic: None,
6966        })
6967    }
6968
6969    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
6970        let mut expected = BTreeSet::new();
6971        for index in (1..self.rule_context_stack.len()).rev() {
6972            let invoking_state = self.rule_context_stack[index].invoking_state;
6973            let Ok(state_number) = usize::try_from(invoking_state) else {
6974                continue;
6975            };
6976            let Some(Transition::Rule { follow_state, .. }) = atn
6977                .state(state_number)
6978                .and_then(|state| state.transitions().first())
6979                .map(ParserTransition::data)
6980            else {
6981                continue;
6982            };
6983            let return_state = follow_state;
6984            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
6985            if !self.cached_state_can_reach_rule_stop(atn, return_state) {
6986                return expected;
6987            }
6988        }
6989        expected.insert(TOKEN_EOF);
6990        expected
6991    }
6992
6993    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
6994        let mut expected = TokenBitSet::default();
6995        for index in (1..self.rule_context_stack.len()).rev() {
6996            let invoking_state = self.rule_context_stack[index].invoking_state;
6997            let Ok(state_number) = usize::try_from(invoking_state) else {
6998                continue;
6999            };
7000            let Some(Transition::Rule { follow_state, .. }) = atn
7001                .state(state_number)
7002                .and_then(|state| state.transitions().first())
7003                .map(ParserTransition::data)
7004            else {
7005                continue;
7006            };
7007            expected.extend_from(&self.cached_state_expected_token_set(atn, follow_state));
7008            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
7009                return expected;
7010            }
7011        }
7012        expected.insert(TOKEN_EOF);
7013        expected
7014    }
7015
7016    /// Reports whether `symbol` is in `context_expected_token_set(atn)`
7017    /// without materializing the union.
7018    ///
7019    /// This walks the rule-invocation stack directly, innermost frame first —
7020    /// the same frames, in the same order, with the same rule-stop gating as
7021    /// the same outer-context return-state chain used by adaptive prediction.
7022    /// The nullable
7023    /// exit in `sync_decision` asks only this membership question, and on
7024    /// valid input the innermost frame answers it, so the early exit replaces
7025    /// an O(stack-depth) set union per loop/optional exit with one probe.
7026    fn context_expected_contains(&mut self, atn: &Atn, symbol: i32) -> bool {
7027        for index in (1..self.rule_context_stack.len()).rev() {
7028            let invoking_state = self.rule_context_stack[index].invoking_state;
7029            let Ok(state_number) = usize::try_from(invoking_state) else {
7030                continue;
7031            };
7032            let Some(Transition::Rule { follow_state, .. }) = atn
7033                .state(state_number)
7034                .and_then(|state| state.transitions().first())
7035                .map(ParserTransition::data)
7036            else {
7037                continue;
7038            };
7039            if self
7040                .cached_state_expected_token_set(atn, follow_state)
7041                .contains(symbol)
7042            {
7043                return true;
7044            }
7045            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
7046                return false;
7047            }
7048        }
7049        symbol == TOKEN_EOF
7050    }
7051
7052    /// Builds a generated no-viable-alternative parser error.
7053    pub fn no_viable_alternative_error(&self, start_index: usize) -> AntlrError {
7054        let error_index = self.input.index();
7055        self.no_viable_alternative_error_at(start_index, error_index)
7056    }
7057
7058    /// Builds a generated no-viable-alternative parser error at the simulator's
7059    /// failing lookahead index. `adaptive_predict` restores the input cursor
7060    /// before returning, so generated parsers have to pass the recorded index
7061    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
7062    pub fn no_viable_alternative_error_at(
7063        &self,
7064        start_index: usize,
7065        error_index: usize,
7066    ) -> AntlrError {
7067        let diagnostic = self.no_viable_alternative(start_index, error_index);
7068        AntlrError::ParserError {
7069            line: diagnostic.line,
7070            column: diagnostic.column,
7071            message: diagnostic.message,
7072            offending: diagnostic.offending,
7073        }
7074    }
7075
7076    /// Builds a generated failed-predicate parser error.
7077    pub fn failed_predicate_error(&self, message: impl Into<String>) -> AntlrError {
7078        let current = self.input.lt(1);
7079        AntlrError::ParserError {
7080            line: current.as_ref().map(Token::line).unwrap_or_default(),
7081            column: current.as_ref().map(Token::column).unwrap_or_default(),
7082            message: format!("rule failed predicate: {}", message.into()),
7083            offending: current.as_ref().map(Token::token_id),
7084        }
7085    }
7086
7087    /// Builds a generated parser error for a semantic predicate with ANTLR's
7088    /// `<fail='...'>` option.
7089    pub fn failed_predicate_option_error(
7090        &self,
7091        rule_index: usize,
7092        message: impl Into<String>,
7093    ) -> AntlrError {
7094        let current = self.input.lt(1);
7095        let rule_name = self
7096            .rule_names()
7097            .get(rule_index)
7098            .map_or_else(|| rule_index.to_string(), Clone::clone);
7099        AntlrError::ParserError {
7100            line: current.as_ref().map(Token::line).unwrap_or_default(),
7101            column: current.as_ref().map(Token::column).unwrap_or_default(),
7102            message: format!("rule {rule_name} {}", message.into()),
7103            offending: current.as_ref().map(Token::token_id),
7104        }
7105    }
7106
7107    /// Builds a generated parser-action event at the current input position.
7108    pub fn parser_action_at_current(
7109        &mut self,
7110        source_state: usize,
7111        rule_index: usize,
7112        start_index: usize,
7113        consumed_eof: bool,
7114    ) -> ParserAction {
7115        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
7116        ParserAction::new(source_state, rule_index, start_index, stop_index)
7117    }
7118
7119    /// Offers a committed parser action event to the user semantic hook.
7120    ///
7121    /// Generated parsers call this for action source states that were present
7122    /// in the ATN but not translated into a built-in Rust action template.
7123    pub fn parser_action_hook(&mut self, action: ParserAction, tree: ParseTree) -> bool {
7124        let rule_index = action.rule_index();
7125        let rule_name = self.rule_names().get(rule_index).cloned();
7126        let context = None;
7127        let input = &mut self.input;
7128        let semantic_hooks = &mut self.semantic_hooks;
7129        let member_values = &self.int_members;
7130        let mut ctx = ParserSemCtx {
7131            input,
7132            tree_storage: &self.tree,
7133            rule_index,
7134            coordinate_index: usize::MAX,
7135            rule_name,
7136            context,
7137            tree: Some(tree),
7138            local_int_arg: None,
7139            member_values,
7140            action: Some(action),
7141        };
7142        let handled = semantic_hooks.action(&mut ctx, action);
7143        // This action reached the hook because it had no translated arm. If no
7144        // hook handled it either (`SemanticHooks::action` returns `false`), the
7145        // committed action is silently dropped — record it so the parse entry
7146        // can fail loud under the fail-loud boundary, mirroring unknown
7147        // predicates. `assume-*` policies opt out of the fail-loud recording.
7148        if !handled && matches!(self.unknown_predicate_policy, UnknownSemanticPolicy::Error) {
7149            let coordinate = (rule_index, action.source_state());
7150            if !self.unhandled_action_hits.contains(&coordinate) {
7151                self.unhandled_action_hits.push(coordinate);
7152            }
7153        }
7154        handled
7155    }
7156
7157    /// Attempts to execute a whole generated rule by committing simulator
7158    /// decisions directly. Unsupported constructs or decisions that need
7159    /// full-context / predicate evaluation restore the input cursor and fall
7160    /// back to [`Self::parse_atn_rule`].
7161    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
7162        &mut self,
7163        atn: &'atn Atn,
7164        simulator: &mut ParserAtnSimulator<'atn>,
7165        rule_index: usize,
7166    ) -> Result<ParseTree, AntlrError> {
7167        let start_index = self.current_visible_index();
7168        self.clear_prediction_diagnostics();
7169        self.reset_per_parse_caches();
7170        self.reset_recognition_arena();
7171        let tree_checkpoint = self.tree.checkpoint();
7172        let mut decision_by_state = vec![None; atn.states().len()];
7173        for (decision, state_number) in atn.decision_to_state().iter().enumerate() {
7174            if let Some(slot) = decision_by_state.get_mut(state_number) {
7175                *slot = Some(decision);
7176            }
7177        }
7178
7179        let result = DirectAdaptiveParser {
7180            parser: self,
7181            atn,
7182            simulator,
7183            decision_by_state,
7184            steps: 0,
7185        }
7186        .parse_rule(rule_index, -1, 0);
7187
7188        match result {
7189            Ok(tree) => {
7190                self.report_token_source_errors();
7191                self.release_tree_scratch_if_idle();
7192                Ok(tree)
7193            }
7194            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
7195                let _ = reason;
7196                self.tree.rollback(tree_checkpoint);
7197                self.input.seek(start_index);
7198                self.parse_atn_rule(atn, rule_index)
7199            }
7200        }
7201    }
7202
7203    /// Parses a generated rule by interpreting the parser ATN from the rule's
7204    /// start state to its stop state.
7205    ///
7206    /// The recognizer backtracks across alternatives and loop exits using token
7207    /// stream indices instead of committing to input consumption immediately.
7208    /// Once a viable ATN path is found, the parser commits the accepted token
7209    /// interval and returns a rule node whose children mirror every grammar
7210    /// rule invocation reached on that path, matching ANTLR's parse-tree
7211    /// shape.
7212    pub fn parse_atn_rule(
7213        &mut self,
7214        atn: &Atn,
7215        rule_index: usize,
7216    ) -> Result<ParseTree, AntlrError> {
7217        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
7218    }
7219
7220    /// Parses a generated rule by interpreting the parser ATN with an initial
7221    /// left-recursive precedence threshold.
7222    pub fn parse_atn_rule_with_precedence(
7223        &mut self,
7224        atn: &Atn,
7225        rule_index: usize,
7226        precedence: i32,
7227    ) -> Result<ParseTree, AntlrError> {
7228        self.parse_atn_rule_with_precedence_inner(
7229            atn,
7230            rule_index,
7231            precedence,
7232            None,
7233            AltNumberTracking::default(),
7234        )
7235    }
7236
7237    fn parse_atn_rule_with_precedence_inner(
7238        &mut self,
7239        atn: &Atn,
7240        rule_index: usize,
7241        precedence: i32,
7242        predicate_context: Option<FastPredicateContext<'_>>,
7243        alt_tracking: AltNumberTracking,
7244    ) -> Result<ParseTree, AntlrError> {
7245        let report_unrecovered_error = self.is_top_level_entry();
7246        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7247            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7248        })?;
7249        let stop_state = atn
7250            .rule_to_stop_state()
7251            .get(rule_index)
7252            .filter(|state| *state != usize::MAX)
7253            .ok_or_else(|| {
7254                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7255            })?;
7256
7257        let start_index = self.current_visible_index();
7258        self.clear_prediction_diagnostics();
7259        self.reset_per_parse_caches();
7260        self.reset_recognition_arena();
7261        let caller_follow_state = self.pending_invoking_follow_state(atn);
7262        self.fast_recovery_enabled = false;
7263        self.fast_token_nodes_enabled = false;
7264        self.fast_track_alt_numbers = alt_tracking.any();
7265        let top_request = FastRecognizeTopRequest {
7266            start_state,
7267            stop_state,
7268            start_index,
7269            precedence,
7270            caller_follow_state,
7271        };
7272        let first_pass = self.fast_recognize_top(atn, top_request, predicate_context);
7273        self.fast_token_nodes_enabled = self.build_parse_trees;
7274        let needs_tree_retry = matches!(
7275            &first_pass,
7276            Ok((outcome, _, _))
7277                if self.build_parse_trees
7278                    && self
7279                        .recognition_arena
7280                        .sequence_has_left_recursive_boundary(outcome.nodes)
7281        );
7282        let needs_retry = match &first_pass {
7283            // The FIRST-set prefilter trims speculative rule calls that can't
7284            // match the current lookahead — useful for perf on grammars with
7285            // many epsilon-reachable rules, but the trim also bypasses
7286            // single-token insertion / deletion recovery that ANTLR's
7287            // reference parser runs at the child rule's first consuming
7288            // transition. Retry without the prefilter whenever the first pass
7289            // either produced no outcome at all or produced a recovered
7290            // outcome (diagnostics non-empty), since the second pass might
7291            // surface a child-level recovery with cleaner diagnostics or
7292            // closer parity to ANTLR's tree shape. Left-recursive tree
7293            // boundaries also need the token-node pass; otherwise the fold has
7294            // no concrete left operand to wrap into ANTLR's recursive context.
7295            Err(_) => true,
7296            Ok((outcome, _, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
7297        };
7298        let (outcome, _expected, alt_number) = if needs_retry {
7299            self.fast_first_set_prefilter = false;
7300            self.fast_recovery_enabled = false;
7301            let clean_retry = self.fast_recognize_top(atn, top_request, predicate_context);
7302            let clean_selected = if needs_tree_retry {
7303                match clean_retry {
7304                    ok @ Ok(_) => ok,
7305                    Err(_) => first_pass,
7306                }
7307            } else {
7308                select_better_top_outcome(first_pass, clean_retry, &self.recognition_arena)
7309            };
7310            let selected = if clean_selected.is_err()
7311                || matches!(&clean_selected, Ok((outcome, _, _)) if !outcome.diagnostics.is_empty())
7312            {
7313                self.fast_recovery_enabled = true;
7314                let recovery_retry = self.fast_recognize_top(atn, top_request, predicate_context);
7315                select_better_top_outcome(clean_selected, recovery_retry, &self.recognition_arena)
7316            } else {
7317                clean_selected
7318            };
7319            self.fast_first_set_prefilter = true;
7320            self.fast_recovery_enabled = true;
7321            selected.map_err(|expected| {
7322                if predicate_context.is_some()
7323                    && let Some(error) = self.unknown_semantic_error()
7324                {
7325                    self.report_token_source_errors();
7326                    return error;
7327                }
7328                let error = self.recognition_error(rule_index, start_index, &expected);
7329                self.record_syntax_errors(1);
7330                self.report_token_source_errors();
7331                if report_unrecovered_error {
7332                    self.report_unrecovered_parser_error(&error);
7333                }
7334                error
7335            })?
7336        } else {
7337            first_pass.expect("first_pass is Ok in the no-retry branch")
7338        };
7339        if predicate_context.is_some()
7340            && let Some(error) = self.unknown_semantic_error()
7341        {
7342            self.report_token_source_errors();
7343            return Err(error);
7344        }
7345        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7346        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7347        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7348        self.report_token_source_errors();
7349        let mut context = ParserRuleContext::with_child_capacity(
7350            rule_index,
7351            self.state(),
7352            if self.build_parse_trees {
7353                self.recognition_arena.sequence_len(outcome.nodes)
7354            } else {
7355                0
7356            },
7357        );
7358        if alt_tracking.public {
7359            context.set_alt_number(alt_number.max(1));
7360        }
7361        if alt_tracking.context {
7362            context.set_context_alt_number(alt_number);
7363        }
7364        if let Some(token) = self.token_id_at(start_index) {
7365            self.set_context_start(&mut context, token);
7366        }
7367        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
7368        if let Some(token) = stop_index.and_then(|token_index| self.token_id_at(token_index)) {
7369            self.set_context_stop(&mut context, token);
7370        }
7371        let live_root = if self.build_parse_trees {
7372            self.recognition_arena
7373                .fold_left_recursive_boundaries(outcome.nodes)
7374        } else {
7375            outcome.nodes
7376        };
7377        if self.build_parse_trees {
7378            if self
7379                .recognition_arena
7380                .sequence_has_explicit_token(live_root)
7381            {
7382                let mut cursor = live_root;
7383                while let Some(link) = self.recognition_arena.link(cursor) {
7384                    let child = self.arena_recognized_node_tree(
7385                        link.head,
7386                        alt_tracking.public,
7387                        alt_tracking.context,
7388                    )?;
7389                    self.tree.add_child(&mut context, child);
7390                    cursor = link.tail;
7391                }
7392            } else {
7393                self.add_arena_implicit_token_children(
7394                    &mut context,
7395                    start_index,
7396                    stop_index,
7397                    live_root,
7398                    alt_tracking,
7399                )?;
7400            }
7401        }
7402        self.finish_recognition_arena(live_root, outcome.diagnostics);
7403        self.input.seek(outcome.index);
7404
7405        let tree = self.rule_node(context);
7406        self.release_tree_scratch_if_idle();
7407        Ok(tree)
7408    }
7409
7410    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
7411        let invoking_state = self.pending_invoking_states.last().copied()?;
7412        let state_number = usize::try_from(invoking_state).ok()?;
7413        match atn.state(state_number)?.transitions().first()?.data() {
7414            Transition::Rule { follow_state, .. } => Some(follow_state),
7415            _ => None,
7416        }
7417    }
7418
7419    #[cfg(test)]
7420    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
7421        caller_follow_token_info_for_stream(&mut self.input, index)
7422    }
7423
7424    /// Runs the fast recognizer once from the rule's start state and returns
7425    /// the best outcome or the per-attempt expected-token accumulator. The
7426    /// caller flips `fast_first_set_prefilter` between calls when a retry is
7427    /// needed, so the FIRST-set cache is left intact across both passes.
7428    fn fast_recognize_top(
7429        &mut self,
7430        atn: &Atn,
7431        request: FastRecognizeTopRequest,
7432        predicate_context: Option<FastPredicateContext<'_>>,
7433    ) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
7434        let FastRecognizeTopRequest {
7435            start_state,
7436            stop_state,
7437            start_index,
7438            precedence,
7439            caller_follow_state,
7440        } = request;
7441        // `input.size()` is intentionally only the currently buffered token
7442        // count here. Do not restore an up-front fill just to size this map:
7443        // a small floor avoids tiny-input churn, and larger inputs reserve from
7444        // the buffered token count without forcing startup tokenization. The
7445        // 8x multiplier matches the empirical
7446        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
7447        // Kotlin ~12× memo entries per token), so the table avoids one
7448        // rehash on the typical hot path.
7449        let memo_capacity = fast_recognize_memo_capacity(self.input.size());
7450        let mut recognize_scratch = std::mem::take(&mut self.fast_recognize_scratch);
7451        recognize_scratch.prepare(memo_capacity);
7452        let mut expected = ExpectedTokens::default();
7453        let empty_recovery = self.empty_recovery_symbols();
7454        let outcomes = self.recognize_state_fast(
7455            atn,
7456            FastRecognizeRequest {
7457                state_number: start_state,
7458                stop_state,
7459                index: start_index,
7460                rule_start_index: start_index,
7461                decision_start_index: None,
7462                precedence,
7463                depth: 0,
7464                recovery_symbols: empty_recovery,
7465                recovery_state: None,
7466            },
7467            FastRecognizeScratch {
7468                predicate_context,
7469                visiting: &mut recognize_scratch.visiting,
7470                memo: &mut recognize_scratch.memo,
7471                expected: &mut expected,
7472                native_depth: 0,
7473            },
7474        );
7475        recognize_scratch.release_oversized_memo();
7476        self.fast_recognize_scratch = recognize_scratch;
7477        #[cfg(feature = "perf-counters")]
7478        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
7479            perf_counters::dump();
7480            perf_counters::reset();
7481        }
7482        let caller_follow =
7483            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
7484        let selected = {
7485            let arena = &self.recognition_arena;
7486            let input = &mut self.input;
7487            select_best_fast_outcome(
7488                outcomes.into_iter(),
7489                self.prediction_mode,
7490                caller_follow.as_deref(),
7491                |index| caller_follow_token_info_for_stream(input, index),
7492                arena,
7493            )
7494        };
7495        match selected {
7496            Some(mut outcome) => {
7497                let alt_number = if self.build_parse_trees || self.fast_track_alt_numbers {
7498                    self.materialize_fast_outcome_nodes(&mut outcome)
7499                } else {
7500                    0
7501                };
7502                Ok((outcome, expected, alt_number))
7503            }
7504            None => Err(expected),
7505        }
7506    }
7507
7508    /// Converts one speculative arena record into the flat public CST.
7509    fn arena_recognized_node_tree(
7510        &mut self,
7511        node_id: RecognizedNodeId,
7512        track_alt_numbers: bool,
7513        track_context_alt_numbers: bool,
7514    ) -> Result<ParseTree, AntlrError> {
7515        let node = self.recognition_arena.node(node_id);
7516        match node {
7517            ArenaRecognizedNode::Token { token } => Ok(self.terminal_tree(token)),
7518            ArenaRecognizedNode::ErrorToken { token } => Ok(self.error_tree(token)),
7519            ArenaRecognizedNode::MissingToken { extra } => {
7520                let (token_type, at_index, text) = match self.recognition_arena.extra(extra) {
7521                    RecognitionExtra::MissingToken {
7522                        token_type,
7523                        at_index,
7524                        text,
7525                    } => (*token_type, *at_index as usize, text.clone()),
7526                    RecognitionExtra::ReturnValues(_) | RecognitionExtra::Diagnostic(_) => {
7527                        unreachable!("missing-token node must reference missing-token extra")
7528                    }
7529                };
7530                let (line, column) = self
7531                    .token_at(at_index)
7532                    .map_or((0, 0), |token| (token.line(), token.column()));
7533                let token = self.insert_synthetic_token(token_type, text, line, column)?;
7534                Ok(self.error_tree(token))
7535            }
7536            ArenaRecognizedNode::Rule {
7537                rule_index,
7538                invoking_state,
7539                alt_number,
7540                start_index,
7541                stop_index,
7542                return_values,
7543                children,
7544            } => {
7545                let mut context = ParserRuleContext::with_child_capacity(
7546                    rule_index as usize,
7547                    invoking_state as isize,
7548                    self.recognition_arena.sequence_len(children),
7549                );
7550                if track_alt_numbers {
7551                    context.set_alt_number((alt_number as usize).max(1));
7552                }
7553                if track_context_alt_numbers {
7554                    context.set_context_alt_number(alt_number as usize);
7555                }
7556                if let Some(extra) = return_values {
7557                    let RecognitionExtra::ReturnValues(values) =
7558                        self.recognition_arena.extra(extra)
7559                    else {
7560                        unreachable!("rule node must reference return-values extra");
7561                    };
7562                    for (name, value) in values {
7563                        context.set_int_return(name.clone(), *value);
7564                    }
7565                }
7566                if let Some(token) = self.token_id_at(start_index as usize) {
7567                    self.set_context_start(&mut context, token);
7568                }
7569                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
7570                    self.set_context_stop(&mut context, token);
7571                }
7572                let mut cursor = self
7573                    .recognition_arena
7574                    .fold_left_recursive_boundaries(children);
7575                while let Some(link) = self.recognition_arena.link(cursor) {
7576                    let child = self.arena_recognized_node_tree(
7577                        link.head,
7578                        track_alt_numbers,
7579                        track_context_alt_numbers,
7580                    )?;
7581                    self.tree.add_child(&mut context, child);
7582                    cursor = link.tail;
7583                }
7584                Ok(self.rule_node(context))
7585            }
7586            ArenaRecognizedNode::LeftRecursiveBoundary { rule_index, .. } => {
7587                Err(AntlrError::Unsupported(format!(
7588                    "unfolded left-recursive boundary for rule {rule_index}"
7589                )))
7590            }
7591        }
7592    }
7593
7594    fn arena_recognized_node_tree_with_implicit_tokens(
7595        &mut self,
7596        node_id: RecognizedNodeId,
7597        alt_tracking: AltNumberTracking,
7598    ) -> Result<ParseTree, AntlrError> {
7599        let node = self.recognition_arena.node(node_id);
7600        match node {
7601            ArenaRecognizedNode::Rule {
7602                rule_index,
7603                invoking_state,
7604                alt_number,
7605                start_index,
7606                stop_index,
7607                children,
7608                ..
7609            } => {
7610                let mut context = ParserRuleContext::with_child_capacity(
7611                    rule_index as usize,
7612                    invoking_state as isize,
7613                    self.recognition_arena.sequence_len(children),
7614                );
7615                if alt_tracking.public {
7616                    context.set_alt_number((alt_number as usize).max(1));
7617                }
7618                if alt_tracking.context {
7619                    context.set_context_alt_number(alt_number as usize);
7620                }
7621                if let Some(token) = self.token_id_at(start_index as usize) {
7622                    self.set_context_start(&mut context, token);
7623                }
7624                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
7625                    self.set_context_stop(&mut context, token);
7626                }
7627                let children = self
7628                    .recognition_arena
7629                    .fold_left_recursive_boundaries(children);
7630                self.add_arena_implicit_token_children(
7631                    &mut context,
7632                    start_index as usize,
7633                    stop_index.map(|index| index as usize),
7634                    children,
7635                    alt_tracking,
7636                )?;
7637                Ok(self.rule_node(context))
7638            }
7639            _ => {
7640                self.arena_recognized_node_tree(node_id, alt_tracking.public, alt_tracking.context)
7641            }
7642        }
7643    }
7644
7645    fn add_arena_implicit_token_children(
7646        &mut self,
7647        context: &mut ParserRuleContext,
7648        start_index: usize,
7649        stop_index: Option<usize>,
7650        mut children: NodeSeqId,
7651        alt_tracking: AltNumberTracking,
7652    ) -> Result<(), AntlrError> {
7653        let mut cursor = Some(start_index);
7654        while let Some(link) = self.recognition_arena.link(children) {
7655            if let Some((child_start, child_stop)) = self.recognition_arena.node_span(link.head) {
7656                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
7657                let child =
7658                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
7659                self.tree.add_child(context, child);
7660                if let Some(child_stop) = child_stop {
7661                    let next = self.next_visible_after_token(child_stop);
7662                    cursor = match (cursor, next) {
7663                        (None, _) | (_, None) => None,
7664                        (Some(current), Some(next)) => Some(current.max(next)),
7665                    };
7666                }
7667            } else {
7668                let child =
7669                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
7670                self.tree.add_child(context, child);
7671            }
7672            children = link.tail;
7673        }
7674        if let Some(stop) = stop_index {
7675            self.add_visible_terminals_through(context, cursor, stop)?;
7676        }
7677        Ok(())
7678    }
7679
7680    fn add_visible_terminals_before(
7681        &mut self,
7682        context: &mut ParserRuleContext,
7683        cursor: &mut Option<usize>,
7684        before: usize,
7685    ) -> Result<(), AntlrError> {
7686        let Some(stop) = before.checked_sub(1) else {
7687            return Ok(());
7688        };
7689        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
7690        *cursor = next;
7691        Ok(())
7692    }
7693
7694    fn add_visible_terminals_through(
7695        &mut self,
7696        context: &mut ParserRuleContext,
7697        mut cursor: Option<usize>,
7698        stop: usize,
7699    ) -> Result<Option<usize>, AntlrError> {
7700        while let Some(index) = cursor {
7701            if index > stop {
7702                return Ok(Some(index));
7703            }
7704            let token = self
7705                .input
7706                .get_id(index)
7707                .ok_or_else(|| AntlrError::ParserError {
7708                    line: 0,
7709                    column: 0,
7710                    message: format!("missing token at index {index}"),
7711                    offending: None,
7712                })?;
7713            let is_eof = self.token_type_for_id(token) == TOKEN_EOF;
7714            let child = self.terminal_tree(token);
7715            self.tree.add_child(context, child);
7716            if is_eof {
7717                return Ok(None);
7718            }
7719            cursor = self.next_visible_after_token(index);
7720        }
7721        Ok(None)
7722    }
7723
7724    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
7725        let next = self.input.next_visible_after(index);
7726        (next != index).then_some(next)
7727    }
7728
7729    /// Parses a generated rule and returns semantic actions reached on the
7730    /// selected ATN path.
7731    ///
7732    /// This slower path preserves action ordering and token intervals for
7733    /// generated code that replays target-specific action templates after the
7734    /// recognizer has chosen one viable parse path.
7735    pub fn parse_atn_rule_with_actions(
7736        &mut self,
7737        atn: &Atn,
7738        rule_index: usize,
7739    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7740        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
7741    }
7742
7743    /// Parses a generated rule and emits ATN actions plus selected rule-init
7744    /// actions reached on the chosen path.
7745    ///
7746    /// Generated parsers use this when a grammar contains rule-level `@init`
7747    /// templates that must run for nested rule invocations. The runtime keeps
7748    /// the action list path-sensitive, so init templates are replayed only for
7749    /// rules that were actually entered by the selected parse.
7750    pub fn parse_atn_rule_with_action_inits(
7751        &mut self,
7752        atn: &Atn,
7753        rule_index: usize,
7754        init_action_rules: &[usize],
7755    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7756        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
7757    }
7758
7759    /// Parses a generated rule with optional semantic-action replay features.
7760    ///
7761    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
7762    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
7763    /// unchanged for grammars that do not request that target template.
7764    pub fn parse_atn_rule_with_action_options(
7765        &mut self,
7766        atn: &Atn,
7767        rule_index: usize,
7768        init_action_rules: &[usize],
7769        track_alt_numbers: bool,
7770    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7771        self.parse_atn_rule_with_runtime_options(
7772            atn,
7773            rule_index,
7774            ParserRuntimeOptions {
7775                init_action_rules,
7776                track_alt_numbers,
7777                ..ParserRuntimeOptions::default()
7778            },
7779        )
7780    }
7781
7782    /// Parses a generated rule with action replay and parser predicate support.
7783    ///
7784    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
7785    /// target-template predicate semantics emitted by the generator. Missing
7786    /// entries are treated as true so unsupported predicate-free grammars keep
7787    /// the previous unconditional transition behavior.
7788    pub fn parse_atn_rule_with_runtime_options(
7789        &mut self,
7790        atn: &Atn,
7791        rule_index: usize,
7792        options: ParserRuntimeOptions<'_>,
7793    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7794        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
7795    }
7796
7797    /// Parses a generated rule with action replay, parser predicate support,
7798    /// and an initial left-recursive precedence threshold.
7799    pub fn parse_atn_rule_with_runtime_options_and_precedence(
7800        &mut self,
7801        atn: &Atn,
7802        rule_index: usize,
7803        precedence: i32,
7804        options: ParserRuntimeOptions<'_>,
7805    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
7806        let report_unrecovered_error = self.is_top_level_entry();
7807        let ParserRuntimeOptions {
7808            init_action_rules,
7809            track_alt_numbers,
7810            track_context_alt_numbers,
7811            predicates,
7812            semantics,
7813            rule_args,
7814            member_actions,
7815            return_actions,
7816            unknown_predicate_policy,
7817        } = options;
7818        let capture_alt_numbers = track_alt_numbers || track_context_alt_numbers;
7819        if init_action_rules.is_empty()
7820            && !capture_alt_numbers
7821            && predicates.is_empty()
7822            && semantics.is_none()
7823            && rule_args.is_empty()
7824            && member_actions.is_empty()
7825            && return_actions.is_empty()
7826            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
7827            && !atn_has_observable_action_transitions(atn)
7828            && !self.semantic_hooks.observes_parser_decisions()
7829            && (!self.semantic_hooks.observes_parser_predicates()
7830                || !atn_has_predicate_transitions(atn))
7831        {
7832            return self
7833                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
7834                .map(|tree| (tree, Vec::new()));
7835        }
7836        if !self.semantic_hooks.observes_parser_decisions()
7837            && can_use_fast_predicate_recognizer(atn, &options)
7838        {
7839            self.unknown_predicate_policy = unknown_predicate_policy;
7840            let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7841            let member_values = self.int_members.clone();
7842            let result = self
7843                .parse_atn_rule_with_precedence_inner(
7844                    atn,
7845                    rule_index,
7846                    precedence,
7847                    Some(FastPredicateContext {
7848                        predicates,
7849                        semantics,
7850                        member_values: &member_values,
7851                    }),
7852                    AltNumberTracking {
7853                        public: track_alt_numbers,
7854                        context: track_context_alt_numbers,
7855                    },
7856                )
7857                .map(|tree| (tree, Vec::new()));
7858            if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
7859                self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7860            }
7861            return result;
7862        }
7863        self.unknown_predicate_policy = unknown_predicate_policy;
7864        // A generated parent may have already recorded unknown-predicate
7865        // coordinates before descending into this (interpreted) child. Clearing
7866        // unconditionally would drop them before the parent's public entry
7867        // surfaces them, so stash and restore around this call: recognition sees
7868        // only the hits it records itself (so the fail-loud check below reflects
7869        // this rule), and the parent's prior hits are merged back afterward.
7870        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
7871        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7872            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7873        })?;
7874        let stop_state = atn
7875            .rule_to_stop_state()
7876            .get(rule_index)
7877            .filter(|state| *state != usize::MAX)
7878            .ok_or_else(|| {
7879                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7880            })?;
7881
7882        let start_index = self.current_visible_index();
7883        self.clear_prediction_diagnostics();
7884        self.reset_per_parse_caches();
7885        self.reset_recognition_arena();
7886        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
7887        let invoking_state = self.pending_invoking_states.pop();
7888        let local_int_arg = invoking_state
7889            .and_then(|state| usize::try_from(state).ok())
7890            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
7891        let mut visiting = BTreeSet::new();
7892        let mut memo = BTreeMap::new();
7893        let mut expected = ExpectedTokens::default();
7894        let member_values = self.int_members.clone();
7895        let return_values = BTreeMap::new();
7896        let outcomes = self.recognize_state(
7897            atn,
7898            RecognizeRequest {
7899                state_number: start_state,
7900                stop_state,
7901                index: start_index,
7902                rule_start_index: start_index,
7903                decision_start_index: None,
7904                init_action_rules: &init_action_rules,
7905                predicates,
7906                semantics,
7907                rule_args,
7908                member_actions,
7909                return_actions,
7910                local_int_arg,
7911                member_values,
7912                return_values,
7913                rule_alt_number: 0,
7914                track_alt_numbers: capture_alt_numbers,
7915                consumed_eof: false,
7916                committed_decision: false,
7917                precedence,
7918                depth: 0,
7919                recovery_symbols: BTreeSet::new(),
7920                recovery_state: None,
7921            },
7922            &mut visiting,
7923            &mut memo,
7924            &mut expected,
7925        );
7926        if let Some(error) = self.unknown_semantic_error() {
7927            self.report_token_source_errors();
7928            // Keep the recorded coordinates: when this interpreted rule is a
7929            // child of a generated parent, the parent's catch block recovers an
7930            // ordinary `AntlrError` into a partial subtree, so the fail-loud
7931            // coordinate must survive on the parser for the top-level entry's
7932            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
7933            // is handled by clearing at the top-level generated entry instead.
7934            return Err(error);
7935        }
7936        // Recognition recorded no unresolved coordinate of its own; merge the
7937        // parent's prior hits back so its public entry can still surface them.
7938        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
7939        let Some(outcome) = select_best_outcome(
7940            outcomes.into_iter(),
7941            self.prediction_mode,
7942            &self.recognition_arena,
7943        ) else {
7944            let error = self.recognition_error(rule_index, start_index, &expected);
7945            self.record_syntax_errors(1);
7946            self.report_token_source_errors();
7947            if report_unrecovered_error {
7948                self.report_unrecovered_parser_error(&error);
7949            }
7950            return Err(error);
7951        };
7952
7953        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7954        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7955        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7956        self.report_token_source_errors();
7957        let mut actions = outcome.actions;
7958        if init_action_rules.contains(&rule_index) {
7959            actions.insert(
7960                0,
7961                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
7962            );
7963        }
7964        let mut context =
7965            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
7966        if track_alt_numbers {
7967            context.set_alt_number(outcome.alt_number.max(1));
7968        }
7969        if track_context_alt_numbers {
7970            context.set_context_alt_number(outcome.alt_number);
7971        }
7972        for (name, value) in outcome.return_values {
7973            context.set_int_return(name, value);
7974        }
7975        if let Some(token) = self.token_id_at(start_index) {
7976            self.set_context_start(&mut context, token);
7977        }
7978        if let Some(token) = self.rule_stop_token_id(outcome.index, outcome.consumed_eof) {
7979            self.set_context_stop(&mut context, token);
7980        }
7981        let live_root = if self.build_parse_trees {
7982            self.recognition_arena
7983                .fold_left_recursive_boundaries(outcome.nodes)
7984        } else {
7985            outcome.nodes
7986        };
7987        if self.build_parse_trees {
7988            let mut nodes = live_root;
7989            while let Some(link) = self.recognition_arena.link(nodes) {
7990                let child = self.arena_recognized_node_tree(
7991                    link.head,
7992                    track_alt_numbers,
7993                    track_context_alt_numbers,
7994                )?;
7995                self.tree.add_child(&mut context, child);
7996                nodes = link.tail;
7997            }
7998        }
7999        self.finish_recognition_arena(live_root, outcome.diagnostics);
8000        self.input.seek(outcome.index);
8001
8002        let tree = self.rule_node(context);
8003        self.release_tree_scratch_if_idle();
8004        Ok((tree, actions))
8005    }
8006
8007    /// Temporary parser entry used by generated parser methods while the parser
8008    /// ATN simulator is being implemented.
8009    ///
8010    /// This keeps generated parser crates buildable and gives us a stable method
8011    /// surface for every grammar rule. It intentionally accepts all remaining
8012    /// tokens into one rule context; it is not the final parser semantics.
8013    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
8014        let mut context = ParserRuleContext::new(rule_index, self.state());
8015        while self.la(1) != TOKEN_EOF {
8016            let token_type = self.la(1);
8017            let child = self.match_token(token_type)?;
8018            if self.build_parse_trees {
8019                self.tree.add_child(&mut context, child);
8020            }
8021        }
8022        if self.build_parse_trees {
8023            let child = self.match_eof()?;
8024            self.tree.add_child(&mut context, child);
8025        }
8026        let tree = self.rule_node(context);
8027        self.release_tree_scratch_if_idle();
8028        Ok(tree)
8029    }
8030
8031    /// Builds the parser error reported when no ATN path can reach the active
8032    /// rule stop state.
8033    fn recognition_error(
8034        &mut self,
8035        rule_index: usize,
8036        start_index: usize,
8037        expected: &ExpectedTokens,
8038    ) -> AntlrError {
8039        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
8040        self.input.seek(index);
8041        let current = self.input.lt(1);
8042        let line = current.as_ref().map(Token::line).unwrap_or_default();
8043        let column = current.as_ref().map(Token::column).unwrap_or_default();
8044        AntlrError::ParserError {
8045            line,
8046            column,
8047            message,
8048            offending: current.as_ref().map(Token::token_id),
8049        }
8050    }
8051
8052    /// Builds the token index and ANTLR-compatible message for a failed rule.
8053    fn expected_error_message(
8054        &mut self,
8055        rule_index: usize,
8056        start_index: usize,
8057        expected: &ExpectedTokens,
8058    ) -> (usize, String) {
8059        let index = expected
8060            .index
8061            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
8062            .unwrap_or_else(|| self.input.index());
8063        self.input.seek(index);
8064        let current = self.input.lt(1);
8065        let message = if expected
8066            .no_viable
8067            .as_ref()
8068            .is_some_and(|no_viable| no_viable.error_index == index)
8069        {
8070            let start = expected
8071                .no_viable
8072                .as_ref()
8073                .map_or(start_index, |no_viable| no_viable.start_index);
8074            let text = display_input_text(&self.input.text(start, index));
8075            format!("no viable alternative at input '{text}'")
8076        } else if expected.symbols.is_empty() {
8077            if expected.index.is_some() {
8078                let found = current
8079                    .as_ref()
8080                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
8081                if current
8082                    .as_ref()
8083                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
8084                {
8085                    format!(
8086                        "missing {} at {found}",
8087                        self.expected_symbols_display(&expected.symbols)
8088                    )
8089                } else {
8090                    format!("mismatched input {found}")
8091                }
8092            } else {
8093                format!("no viable alternative while parsing rule {rule_index}")
8094            }
8095        } else {
8096            format!(
8097                "mismatched input {} expecting {}",
8098                current
8099                    .as_ref()
8100                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8101                self.expected_symbols_display(&expected.symbols)
8102            )
8103        };
8104        (index, message)
8105    }
8106
8107    /// Converts a failed child rule into a recovered outcome so the parent can
8108    /// continue after reporting the child diagnostic.
8109    fn child_rule_failure_recovery(
8110        &mut self,
8111        rule_index: usize,
8112        start_index: usize,
8113        sync_symbols: &BTreeSet<i32>,
8114        member_values: MemberEnv,
8115        expected: &ExpectedTokens,
8116    ) -> Option<RecognizeOutcome> {
8117        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
8118        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
8119        let mut next_index = error_index;
8120        loop {
8121            let symbol = self.token_type_at(next_index);
8122            if sync_symbols.contains(&symbol) {
8123                if next_index == error_index {
8124                    return None;
8125                }
8126                break;
8127            }
8128            if symbol == TOKEN_EOF {
8129                break;
8130            }
8131            let after = self.consume_index(next_index, symbol);
8132            if after == next_index {
8133                break;
8134            }
8135            next_index = after;
8136        }
8137        let mut nodes = NodeSeqId::EMPTY;
8138        let error = self.arena_token_node(error_index, true);
8139        self.arena_prepend(&mut nodes, error);
8140        let diagnostics = self
8141            .recognition_arena
8142            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
8143        Some(RecognizeOutcome {
8144            index: next_index,
8145            consumed_eof: false,
8146            alt_number: 0,
8147            member_values,
8148            return_values: BTreeMap::new(),
8149            diagnostics,
8150            decisions: Vec::new(),
8151            actions: Vec::new(),
8152            nodes,
8153        })
8154    }
8155
8156    /// Adapts the optional recovery result to the normal outcome list used by
8157    /// rule-call transitions.
8158    fn child_rule_failure_recovery_outcomes(
8159        &mut self,
8160        request: ChildRuleFailureRecovery<'_>,
8161    ) -> Vec<RecognizeOutcome> {
8162        let sync_symbols =
8163            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
8164        self.child_rule_failure_recovery(
8165            request.rule_index,
8166            request.start_index,
8167            &sync_symbols,
8168            request.member_values,
8169            request.expected,
8170        )
8171        .into_iter()
8172        .collect()
8173    }
8174
8175    /// Formats expected token types using ANTLR's single-token or set syntax.
8176    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
8177        expected_symbols_display(symbols, self.vocabulary())
8178    }
8179
8180    /// Returns the single-token deletion repair if the token after `index`
8181    /// satisfies the failed consuming transition.
8182    fn single_token_deletion(
8183        &mut self,
8184        transition: ParserTransition<'_>,
8185        index: usize,
8186        max_token_type: i32,
8187        expected_symbols: &BTreeSet<i32>,
8188    ) -> Option<(ParserDiagnostic, usize, i32)> {
8189        let current_symbol = self.token_type_at(index);
8190        if current_symbol == TOKEN_EOF {
8191            return None;
8192        }
8193        let next_index = self.consume_index(index, current_symbol);
8194        if next_index == index {
8195            return None;
8196        }
8197        let next_symbol = self.token_type_at(next_index);
8198        if !transition.matches(next_symbol, 1, max_token_type) {
8199            return None;
8200        }
8201        let transition_expected = transition_expected_symbols(transition, max_token_type);
8202        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
8203            &transition_expected
8204        } else {
8205            expected_symbols
8206        });
8207        let current = self.token_at(index);
8208        let message = format!(
8209            "extraneous input {} expecting {expected_display}",
8210            current
8211                .as_ref()
8212                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
8213        );
8214        Some((
8215            diagnostic_for_token(current, message),
8216            next_index,
8217            next_symbol,
8218        ))
8219    }
8220
8221    /// Returns the repair used when deleting the current token lets a recovery
8222    /// state continue with the following token.
8223    fn current_token_deletion(
8224        &mut self,
8225        index: usize,
8226        expected_symbols: &BTreeSet<i32>,
8227    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
8228        if expected_symbols.is_empty() {
8229            return None;
8230        }
8231        let current_symbol = self.token_type_at(index);
8232        if current_symbol == TOKEN_EOF {
8233            return None;
8234        }
8235        let current = self.token_at(index);
8236        let message = format!(
8237            "extraneous input {} expecting {}",
8238            current
8239                .as_ref()
8240                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8241            self.expected_symbols_display(expected_symbols)
8242        );
8243        let diagnostic = diagnostic_for_token(current, message);
8244        let mut skipped = Vec::new();
8245        let mut cursor = index;
8246        loop {
8247            let symbol = self.token_type_at(cursor);
8248            if symbol == TOKEN_EOF {
8249                return None;
8250            }
8251            skipped.push(cursor);
8252            let next_index = self.consume_index(cursor, symbol);
8253            if next_index == cursor {
8254                return None;
8255            }
8256            let next_symbol = self.token_type_at(next_index);
8257            if expected_symbols.contains(&next_symbol) {
8258                return Some((diagnostic, next_index, skipped));
8259            }
8260            cursor = next_index;
8261        }
8262    }
8263
8264    /// Returns the single-token insertion repair for a failed consuming
8265    /// transition. The caller validates the repair by continuing from the
8266    /// transition target at the same input index.
8267    fn single_token_insertion(
8268        &mut self,
8269        transition: ParserTransition<'_>,
8270        index: usize,
8271        max_token_type: i32,
8272        expected_symbols: &BTreeSet<i32>,
8273        follow_symbols: &BTreeSet<i32>,
8274    ) -> Option<(ParserDiagnostic, i32, String)> {
8275        let current_symbol = self.token_type_at(index);
8276        if !follow_symbols.contains(&current_symbol) {
8277            return None;
8278        }
8279        let transition_expected = transition_expected_symbols(transition, max_token_type);
8280        let token_type = transition_expected.iter().next().copied()?;
8281        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
8282            &transition_expected
8283        } else {
8284            expected_symbols
8285        });
8286        let mut token_symbols = BTreeSet::new();
8287        token_symbols.insert(token_type);
8288        let missing_token_display = self.expected_symbols_display(&token_symbols);
8289        let current = self.token_at(index);
8290        let message = format!(
8291            "missing {expected_display} at {}",
8292            current
8293                .as_ref()
8294                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
8295        );
8296        let text = format!("<missing {missing_token_display}>");
8297        Some((
8298            diagnostic_for_token(current.as_ref(), message),
8299            token_type,
8300            text,
8301        ))
8302    }
8303
8304    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
8305    /// skip the unexpected current token when the following token satisfies the
8306    /// transition that failed.
8307    fn fast_single_token_deletion_recovery(
8308        &mut self,
8309        recovery: FastRecoveryRequest<'_, '_>,
8310        predicate_context: Option<FastPredicateContext<'_>>,
8311    ) -> Vec<FastRecognizeOutcome> {
8312        let FastRecoveryRequest {
8313            atn,
8314            transition,
8315            expected_symbols,
8316            target,
8317            request,
8318            visiting,
8319            memo,
8320            expected,
8321        } = recovery;
8322        let FastRecognizeRequest {
8323            stop_state,
8324            index,
8325            rule_start_index,
8326            decision_start_index,
8327            precedence,
8328            depth,
8329            ..
8330        } = request;
8331        let Some((diagnostic, next_index, next_symbol)) =
8332            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
8333        else {
8334            return Vec::new();
8335        };
8336        let after_next = self.consume_index(next_index, next_symbol);
8337        let empty_recovery = self.empty_recovery_symbols();
8338        self.recognize_state_fast(
8339            atn,
8340            FastRecognizeRequest {
8341                state_number: target,
8342                stop_state,
8343                index: after_next,
8344                rule_start_index,
8345                decision_start_index,
8346                precedence,
8347                depth: depth + 1,
8348                recovery_symbols: empty_recovery,
8349                recovery_state: None,
8350            },
8351            FastRecognizeScratch {
8352                predicate_context,
8353                visiting,
8354                memo,
8355                expected,
8356                native_depth: 0,
8357            },
8358        )
8359        .into_iter()
8360        .map(|mut outcome| {
8361            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
8362            outcome.diagnostics = self
8363                .recognition_arena
8364                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8365            if self.fast_token_nodes_enabled {
8366                let token = self.arena_token_node(next_index, false);
8367                self.defer_fast_outcome_node(&mut outcome, token);
8368                let error = self.arena_token_node(index, true);
8369                self.defer_fast_outcome_node(&mut outcome, error);
8370            }
8371            outcome
8372        })
8373        .collect()
8374    }
8375
8376    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
8377    /// pretend the expected transition token was present and continue without
8378    /// consuming the current token.
8379    fn fast_single_token_insertion_recovery(
8380        &mut self,
8381        recovery: FastRecoveryRequest<'_, '_>,
8382        predicate_context: Option<FastPredicateContext<'_>>,
8383    ) -> Vec<FastRecognizeOutcome> {
8384        let FastRecoveryRequest {
8385            atn,
8386            transition,
8387            expected_symbols,
8388            target,
8389            request,
8390            visiting,
8391            memo,
8392            expected,
8393        } = recovery;
8394        let FastRecognizeRequest {
8395            stop_state,
8396            index,
8397            rule_start_index,
8398            decision_start_index,
8399            precedence,
8400            depth,
8401            ..
8402        } = request;
8403        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
8404        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
8405            transition,
8406            index,
8407            atn.max_token_type(),
8408            &expected_symbols,
8409            &follow_symbols,
8410        ) else {
8411            return Vec::new();
8412        };
8413        let empty_recovery = self.empty_recovery_symbols();
8414        self.recognize_state_fast(
8415            atn,
8416            FastRecognizeRequest {
8417                state_number: target,
8418                stop_state,
8419                index,
8420                rule_start_index,
8421                decision_start_index,
8422                precedence,
8423                depth: depth + 1,
8424                recovery_symbols: empty_recovery,
8425                recovery_state: None,
8426            },
8427            FastRecognizeScratch {
8428                predicate_context,
8429                visiting,
8430                memo,
8431                expected,
8432                native_depth: 0,
8433            },
8434        )
8435        .into_iter()
8436        .map(|mut outcome| {
8437            outcome.diagnostics = self
8438                .recognition_arena
8439                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8440            let missing = self.arena_missing_token_node(token_type, index, text.clone());
8441            self.defer_fast_outcome_node(&mut outcome, missing);
8442            outcome
8443        })
8444        .collect()
8445    }
8446
8447    /// Retries the current fast-recognition state after deleting one
8448    /// unexpected token that precedes a valid loop or block continuation.
8449    fn fast_current_token_deletion_recovery(
8450        &mut self,
8451        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
8452        predicate_context: Option<FastPredicateContext<'_>>,
8453    ) -> Vec<FastRecognizeOutcome> {
8454        let FastCurrentTokenDeletionRequest {
8455            atn,
8456            expected_symbols,
8457            mut request,
8458            visiting,
8459            memo,
8460            expected,
8461        } = recovery;
8462        if request.index == request.rule_start_index {
8463            return Vec::new();
8464        }
8465        let Some((diagnostic, next_index, skipped)) =
8466            self.current_token_deletion(request.index, &expected_symbols)
8467        else {
8468            return Vec::new();
8469        };
8470        request.state_number = request.recovery_state.unwrap_or(request.state_number);
8471        request.index = next_index;
8472        request.depth += 1;
8473        request.recovery_state = None;
8474        self.recognize_state_fast(
8475            atn,
8476            request,
8477            FastRecognizeScratch {
8478                predicate_context,
8479                visiting,
8480                memo,
8481                expected,
8482                native_depth: 0,
8483            },
8484        )
8485        .into_iter()
8486        .map(|mut outcome| {
8487            outcome.diagnostics = self
8488                .recognition_arena
8489                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8490            for index in skipped.iter().rev() {
8491                let error = self.arena_token_node(*index, true);
8492                self.defer_fast_outcome_node(&mut outcome, error);
8493            }
8494            outcome
8495        })
8496        .collect()
8497    }
8498
8499    /// Converts a failed child rule into a recovered fast-recognizer outcome so
8500    /// the parent can keep its child rule context and continue at a sync token.
8501    fn fast_child_rule_failure_recovery(
8502        &mut self,
8503        rule_index: usize,
8504        start_index: usize,
8505        sync_symbols: &BTreeSet<i32>,
8506        expected: &ExpectedTokens,
8507    ) -> Option<FastRecognizeOutcome> {
8508        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
8509        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
8510        let mut next_index = error_index;
8511        loop {
8512            let symbol = self.token_type_at(next_index);
8513            if sync_symbols.contains(&symbol) {
8514                if next_index == error_index {
8515                    return None;
8516                }
8517                break;
8518            }
8519            if symbol == TOKEN_EOF {
8520                break;
8521            }
8522            let after = self.consume_index(next_index, symbol);
8523            if after == next_index {
8524                break;
8525            }
8526            next_index = after;
8527        }
8528        let diagnostics = self
8529            .recognition_arena
8530            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
8531        let mut nodes = NodeSeqId::EMPTY;
8532        if self.fast_token_nodes_enabled {
8533            let error = self.arena_token_node(error_index, true);
8534            self.arena_prepend(&mut nodes, error);
8535        }
8536        Some(FastRecognizeOutcome {
8537            index: next_index,
8538            consumed_eof: false,
8539            diagnostics,
8540            deferred_nodes: FastDeferredNodeId::EMPTY,
8541            nodes,
8542        })
8543    }
8544
8545    /// Adapts the optional child-rule recovery result to the fast-recognizer
8546    /// outcome list used by rule-call transitions.
8547    fn fast_child_rule_failure_recovery_outcomes(
8548        &mut self,
8549        request: FastChildRuleFailureRecoveryRequest<'_>,
8550    ) -> Vec<FastRecognizeOutcome> {
8551        let FastChildRuleFailureRecoveryRequest {
8552            atn,
8553            rule_index,
8554            start_index,
8555            follow_state,
8556            stop_state,
8557            expected,
8558        } = request;
8559        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
8560        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
8561            .into_iter()
8562            .collect()
8563    }
8564
8565    fn defer_fast_outcome_node(
8566        &mut self,
8567        outcome: &mut FastRecognizeOutcome,
8568        node: RecognizedNodeId,
8569    ) {
8570        if outcome.deferred_nodes.is_empty() {
8571            self.arena_prepend(&mut outcome.nodes, node);
8572            return;
8573        }
8574        let fragment = self.recognition_arena.prepend(NodeSeqId::EMPTY, node);
8575        let fragment = self.recognition_arena.deferred_fragment(fragment);
8576        outcome.deferred_nodes = self
8577            .recognition_arena
8578            .concat_deferred_nodes(fragment, outcome.deferred_nodes);
8579    }
8580
8581    fn defer_fast_outcome_alternative(
8582        &mut self,
8583        outcome: &mut FastRecognizeOutcome,
8584        alt_number: usize,
8585    ) {
8586        let alternative = self.recognition_arena.deferred_alternative(alt_number);
8587        outcome.deferred_nodes = self
8588            .recognition_arena
8589            .concat_deferred_nodes(alternative, outcome.deferred_nodes);
8590    }
8591
8592    fn defer_fast_outcome_boundary(
8593        &mut self,
8594        outcome: &mut FastRecognizeOutcome,
8595        rule_index: usize,
8596    ) {
8597        let boundary = self
8598            .recognition_arena
8599            .deferred_left_recursive_boundary(rule_index);
8600        outcome.deferred_nodes = self
8601            .recognition_arena
8602            .concat_deferred_nodes(boundary, outcome.deferred_nodes);
8603    }
8604
8605    fn materialize_fast_deferred_nodes(
8606        &mut self,
8607        root: FastDeferredNodeId,
8608        initial_suffix: NodeSeqId,
8609    ) -> (NodeSeqId, usize) {
8610        if root.is_empty() {
8611            return (initial_suffix, 0);
8612        }
8613
8614        enum Frame {
8615            Visit(FastDeferredNodeId),
8616            ContinuePrefix(FastDeferredNodeId),
8617            FinishRule {
8618                rule: FastDeferredRule,
8619                parent_suffix: NodeSeqId,
8620                parent_alt_number: u32,
8621                parent_pending_boundary: Option<RecognizedNodeId>,
8622            },
8623        }
8624
8625        let mut result = initial_suffix;
8626        // The rope is visited suffix-first while nodes are prepended. Later
8627        // alternatives arrive first, so earlier markers overwrite them; a
8628        // boundary redirects those earlier markers to the wrapped context.
8629        let mut alt_number = 0;
8630        let mut pending_boundary = None;
8631        let mut pending = Vec::with_capacity(16);
8632        pending.push(Frame::Visit(root));
8633        let mut fragment_nodes = Vec::new();
8634        while let Some(frame) = pending.pop() {
8635            match frame {
8636                Frame::Visit(deferred) => {
8637                    if deferred.is_empty() {
8638                        continue;
8639                    }
8640
8641                    match self.recognition_arena.deferred_node(deferred) {
8642                        FastDeferredNode::Fragment(sequence) => {
8643                            fragment_nodes.clear();
8644                            fragment_nodes.extend(self.recognition_arena.iter(sequence));
8645                            while let Some(node) = fragment_nodes.pop() {
8646                                self.arena_prepend(&mut result, node);
8647                            }
8648                        }
8649                        FastDeferredNode::Rule(rule) => {
8650                            let rule = self.recognition_arena.deferred_rule(rule);
8651                            let parent_suffix = result;
8652                            let parent_alt_number = alt_number;
8653                            let parent_pending_boundary = pending_boundary;
8654                            result = rule.children;
8655                            alt_number = 0;
8656                            pending_boundary = None;
8657                            pending.push(Frame::FinishRule {
8658                                rule,
8659                                parent_suffix,
8660                                parent_alt_number,
8661                                parent_pending_boundary,
8662                            });
8663                            pending.push(Frame::Visit(rule.deferred_children));
8664                        }
8665                        FastDeferredNode::Alternative(selected) => {
8666                            if let Some(boundary) = pending_boundary {
8667                                self.recognition_arena
8668                                    .set_boundary_alt_number(boundary, selected);
8669                            } else {
8670                                alt_number = selected;
8671                            }
8672                        }
8673                        FastDeferredNode::LeftRecursiveBoundary { rule_index } => {
8674                            let boundary = self.arena_boundary_node(rule_index as usize, 0);
8675                            self.arena_prepend(&mut result, boundary);
8676                            pending_boundary = Some(boundary);
8677                        }
8678                        FastDeferredNode::Concat {
8679                            prefix,
8680                            suffix: deferred_suffix,
8681                        } => {
8682                            pending.push(Frame::ContinuePrefix(prefix));
8683                            pending.push(Frame::Visit(deferred_suffix));
8684                        }
8685                    }
8686                }
8687                Frame::ContinuePrefix(prefix) => pending.push(Frame::Visit(prefix)),
8688                Frame::FinishRule {
8689                    rule,
8690                    parent_suffix,
8691                    parent_alt_number,
8692                    parent_pending_boundary,
8693                } => {
8694                    let node = self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
8695                        rule_index: rule.rule_index,
8696                        invoking_state: rule.invoking_state,
8697                        alt_number,
8698                        start_index: rule.start_index,
8699                        stop_index: rule.stop_index,
8700                        return_values: None,
8701                        children: result,
8702                    });
8703                    result = parent_suffix;
8704                    self.arena_prepend(&mut result, node);
8705                    alt_number = parent_alt_number;
8706                    pending_boundary = parent_pending_boundary;
8707                }
8708            }
8709        }
8710        (result, alt_number as usize)
8711    }
8712
8713    fn materialize_fast_outcome_nodes(&mut self, outcome: &mut FastRecognizeOutcome) -> usize {
8714        let deferred_nodes = std::mem::take(&mut outcome.deferred_nodes);
8715        let (nodes, alt_number) =
8716            self.materialize_fast_deferred_nodes(deferred_nodes, outcome.nodes);
8717        outcome.nodes = nodes;
8718        alt_number
8719    }
8720
8721    /// Walks one ordinary `*`/`+` repetition at a time so input length grows
8722    /// heap work instead of the native call stack.
8723    fn recognize_repetition_fast(
8724        &mut self,
8725        atn: &Atn,
8726        request: &FastRecognizeRequest,
8727        shape: FastRepetitionShape,
8728        scratch: FastRecognizeScratch<'_, '_>,
8729    ) -> Vec<FastRecognizeOutcome> {
8730        let FastRecognizeScratch {
8731            predicate_context,
8732            visiting,
8733            memo,
8734            expected,
8735            native_depth,
8736        } = scratch;
8737        let lookahead = if self.fast_first_set_prefilter {
8738            atn.state(request.state_number).and_then(|state| {
8739                state
8740                    .rule_index()
8741                    .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
8742                    .map(|rule_stop| self.cached_decision_lookahead(atn, state, rule_stop))
8743            })
8744        } else {
8745            None
8746        };
8747        let (enter_alt_number, exit_alt_number) = if self.fast_track_alt_numbers {
8748            let state = atn
8749                .state(request.state_number)
8750                .expect("repetition request state must exist");
8751            (
8752                next_alt_number(state, 2, shape.enter_transition_index, 0, true),
8753                next_alt_number(state, 2, shape.exit_transition_index, 0, true),
8754            )
8755        } else {
8756            (0, 0)
8757        };
8758        let mut work = Vec::with_capacity(2);
8759        push_fast_repetition_work(
8760            &mut work,
8761            shape,
8762            FastRepetitionPath {
8763                index: request.index,
8764                deferred_nodes: FastDeferredNodeId::EMPTY,
8765                diagnostics: DiagnosticSeqId::EMPTY,
8766                consumed_eof: false,
8767            },
8768            lookahead.as_deref(),
8769            self.token_type_at(request.index),
8770        );
8771        let mut coordinates = FastRepetitionCoordinates::new(request.index);
8772        let mut outcomes = Vec::new();
8773        while let Some(item) = work.pop() {
8774            match item {
8775                FastRepetitionWork::Enter(path) => {
8776                    if !coordinates.insert_entered(path) {
8777                        continue;
8778                    }
8779                    let path_nodes = if enter_alt_number == 0 {
8780                        path.deferred_nodes
8781                    } else {
8782                        let alternative = self
8783                            .recognition_arena
8784                            .deferred_alternative(enter_alt_number);
8785                        self.recognition_arena
8786                            .concat_deferred_nodes(path.deferred_nodes, alternative)
8787                    };
8788                    let body_outcomes = self.recognize_state_fast(
8789                        atn,
8790                        FastRecognizeRequest {
8791                            state_number: shape.enter_target,
8792                            stop_state: shape.body_stop_state,
8793                            index: path.index,
8794                            rule_start_index: request.rule_start_index,
8795                            decision_start_index: request.decision_start_index,
8796                            precedence: request.precedence,
8797                            depth: request.depth.saturating_add(1),
8798                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8799                            recovery_state: request.recovery_state,
8800                        },
8801                        FastRecognizeScratch {
8802                            predicate_context,
8803                            visiting: &mut *visiting,
8804                            memo: &mut *memo,
8805                            expected: &mut *expected,
8806                            native_depth: native_depth + 1,
8807                        },
8808                    );
8809                    for body in body_outcomes.into_iter().rev() {
8810                        // ANTLR rejects nullable repetition bodies. Keep the
8811                        // interpreter bounded for malformed or recovered ATNs
8812                        // by mirroring the existing same-coordinate cycle cut.
8813                        if body.index <= path.index {
8814                            continue;
8815                        }
8816                        let body_fragment = self.recognition_arena.deferred_fragment(body.nodes);
8817                        let body_nodes = self
8818                            .recognition_arena
8819                            .concat_deferred_nodes(body.deferred_nodes, body_fragment);
8820                        let deferred_nodes = self
8821                            .recognition_arena
8822                            .concat_deferred_nodes(path_nodes, body_nodes);
8823                        let next_path = FastRepetitionPath {
8824                            index: body.index,
8825                            deferred_nodes,
8826                            diagnostics: self
8827                                .recognition_arena
8828                                .concat_diagnostics(path.diagnostics, body.diagnostics),
8829                            consumed_eof: path.consumed_eof || body.consumed_eof,
8830                        };
8831                        let symbol = self.token_type_at(next_path.index);
8832                        push_fast_repetition_work(
8833                            &mut work,
8834                            shape,
8835                            next_path,
8836                            lookahead.as_deref(),
8837                            symbol,
8838                        );
8839                    }
8840                }
8841                FastRepetitionWork::Exit(path) => {
8842                    if !coordinates.insert_exited(path) {
8843                        continue;
8844                    }
8845                    let path_nodes = if exit_alt_number == 0 {
8846                        path.deferred_nodes
8847                    } else {
8848                        let alternative =
8849                            self.recognition_arena.deferred_alternative(exit_alt_number);
8850                        self.recognition_arena
8851                            .concat_deferred_nodes(path.deferred_nodes, alternative)
8852                    };
8853                    let suffixes = self.recognize_state_fast(
8854                        atn,
8855                        FastRecognizeRequest {
8856                            state_number: shape.exit_target,
8857                            stop_state: request.stop_state,
8858                            index: path.index,
8859                            rule_start_index: request.rule_start_index,
8860                            decision_start_index: request.decision_start_index,
8861                            precedence: request.precedence,
8862                            depth: request.depth.saturating_add(1),
8863                            recovery_symbols: Rc::clone(&request.recovery_symbols),
8864                            recovery_state: request.recovery_state,
8865                        },
8866                        FastRecognizeScratch {
8867                            predicate_context,
8868                            visiting: &mut *visiting,
8869                            memo: &mut *memo,
8870                            expected: &mut *expected,
8871                            native_depth: native_depth + 1,
8872                        },
8873                    );
8874                    for mut outcome in suffixes {
8875                        outcome.deferred_nodes = self
8876                            .recognition_arena
8877                            .concat_deferred_nodes(path_nodes, outcome.deferred_nodes);
8878                        outcome.diagnostics = self
8879                            .recognition_arena
8880                            .concat_diagnostics(path.diagnostics, outcome.diagnostics);
8881                        outcome.consumed_eof |= path.consumed_eof;
8882                        outcomes.push(outcome);
8883                    }
8884                }
8885            }
8886        }
8887        dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
8888        outcomes
8889    }
8890
8891    /// Attempts to reach `stop_state` from `state_number` without committing
8892    /// token consumption to the parser's public stream position.
8893    fn recognize_state_fast(
8894        &mut self,
8895        atn: &Atn,
8896        request: FastRecognizeRequest,
8897        scratch: FastRecognizeScratch<'_, '_>,
8898    ) -> Vec<FastRecognizeOutcome> {
8899        if scratch.native_depth != 0 && scratch.native_depth < FAST_RECOGNIZE_STACK_CHECK_INTERVAL {
8900            return self.recognize_state_fast_inner(atn, request, scratch);
8901        }
8902        self.recognize_state_fast_checked(atn, request, scratch)
8903    }
8904
8905    #[inline(never)]
8906    fn recognize_state_fast_checked(
8907        &mut self,
8908        atn: &Atn,
8909        request: FastRecognizeRequest,
8910        mut scratch: FastRecognizeScratch<'_, '_>,
8911    ) -> Vec<FastRecognizeOutcome> {
8912        scratch.native_depth = 1;
8913        stacker::maybe_grow(FAST_RECOGNIZE_RED_ZONE, FAST_RECOGNIZE_STACK_SIZE, || {
8914            self.recognize_state_fast_inner(atn, request, scratch)
8915        })
8916    }
8917
8918    #[allow(clippy::too_many_lines)]
8919    fn recognize_state_fast_inner(
8920        &mut self,
8921        atn: &Atn,
8922        request: FastRecognizeRequest,
8923        scratch: FastRecognizeScratch<'_, '_>,
8924    ) -> Vec<FastRecognizeOutcome> {
8925        #[cfg(feature = "perf-counters")]
8926        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
8927        let FastRecognizeScratch {
8928            predicate_context,
8929            visiting,
8930            memo,
8931            expected,
8932            native_depth,
8933        } = scratch;
8934        let FastRecognizeRequest {
8935            mut state_number,
8936            stop_state,
8937            mut index,
8938            rule_start_index,
8939            decision_start_index,
8940            precedence,
8941            mut depth,
8942            recovery_symbols,
8943            recovery_state,
8944        } = request;
8945        let max_token_type = atn.max_token_type();
8946        // Walk straight-line epsilon chains in a loop instead of recursing
8947        // into `recognize_state_fast` for each intermediate state. ATN
8948        // serialization places long sequences of `BasicBlock` epsilon
8949        // transitions between decisions: turning that chain into a loop
8950        // collapses many recursive calls (and their memo lookups, vec
8951        // allocations, and visit-set churn) into a single function frame.
8952        // The loop exits as soon as we hit the original state's logic
8953        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
8954        // precedence) so existing fanout, recovery, and memoization still
8955        // apply unchanged.
8956        //
8957        // The inline case also handles single-atom-match states on the
8958        // happy-pass path: when the lone consuming transition matches the
8959        // current lookahead, advance the index and continue without paying
8960        // for a full `recognize_state_fast` recursion. We track tokens we
8961        // consumed inline in `inline_consumed_tokens` so they can be
8962        // prepended onto the eventual outcome list once we hit a state
8963        // whose handling falls outside this fast loop.
8964        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
8965        let mut inline_consumed_eof = false;
8966        loop {
8967            if depth > RECOGNITION_DEPTH_LIMIT {
8968                return Vec::new();
8969            }
8970            if state_number == stop_state {
8971                let mut nodes = NodeSeqId::EMPTY;
8972                if self.fast_token_nodes_enabled {
8973                    for token_index in inline_consumed_tokens.iter().rev() {
8974                        let token = self.arena_token_node(*token_index, false);
8975                        self.arena_prepend(&mut nodes, token);
8976                    }
8977                }
8978                return vec![FastRecognizeOutcome {
8979                    index,
8980                    consumed_eof: inline_consumed_eof,
8981                    diagnostics: DiagnosticSeqId::EMPTY,
8982                    deferred_nodes: FastDeferredNodeId::EMPTY,
8983                    nodes,
8984                }];
8985            }
8986            let Some(state) = atn.state(state_number) else {
8987                return Vec::new();
8988            };
8989            let transitions = state.transitions();
8990            if transitions.len() == 1 && !state.precedence_rule_decision() {
8991                let transition = transitions
8992                    .first()
8993                    .expect("single transition checked above");
8994                let transition_kind = transition.kind();
8995                let target = transition.target();
8996                match transition_kind {
8997                    ParserTransitionKind::Epsilon | ParserTransitionKind::Action
8998                        if left_recursive_boundary(atn, state, target).is_none() =>
8999                    {
9000                        #[cfg(feature = "perf-counters")]
9001                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9002                        state_number = target;
9003                        depth += 1;
9004                        continue;
9005                    }
9006                    ParserTransitionKind::Predicate
9007                        if left_recursive_boundary(atn, state, target).is_none() =>
9008                    {
9009                        #[cfg(feature = "perf-counters")]
9010                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9011                        if !self.fast_parser_predicate_matches(predicate_context, transition, index)
9012                        {
9013                            record_predicate_no_viable(expected, decision_start_index, index);
9014                            return Vec::new();
9015                        }
9016                        state_number = target;
9017                        depth += 1;
9018                        continue;
9019                    }
9020                    ParserTransitionKind::Precedence
9021                        if packed_i32(transition.arg0()) >= precedence
9022                            && left_recursive_boundary(atn, state, target).is_none() =>
9023                    {
9024                        #[cfg(feature = "perf-counters")]
9025                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9026                        state_number = target;
9027                        depth += 1;
9028                        continue;
9029                    }
9030                    // Single-atom / range / set / wildcard / not-set states
9031                    // are common (~17K of ~125K calls on C#) and almost
9032                    // always succeed in pass 1: no fanout, no recovery, no
9033                    // diagnostics. Inline the token match and continue
9034                    // walking instead of recursing — the recursive path
9035                    // would just allocate a Vec, build one outcome, prepend
9036                    // a Token node, and return. Skip pass 2 (recovery
9037                    // enabled): there the failure branch matters and the
9038                    // existing recursive code records expected symbols.
9039                    ParserTransitionKind::Atom
9040                    | ParserTransitionKind::Range
9041                    | ParserTransitionKind::Set
9042                    | ParserTransitionKind::NotSet
9043                    | ParserTransitionKind::Wildcard
9044                        if !self.fast_recovery_enabled =>
9045                    {
9046                        let symbol = self.token_type_at(index);
9047                        if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
9048                            #[cfg(feature = "perf-counters")]
9049                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
9050                            if self.fast_token_nodes_enabled {
9051                                inline_consumed_tokens.push(index);
9052                            }
9053                            inline_consumed_eof |= symbol == TOKEN_EOF;
9054                            index = self.consume_index(index, symbol);
9055                            state_number = target;
9056                            depth += 1;
9057                            continue;
9058                        }
9059                        // Fall through to break and let the regular
9060                        // body handle the no-match case (returns empty).
9061                    }
9062                    _ => {}
9063                }
9064            }
9065            break;
9066        }
9067        // If we collected token nodes inline but bail to the recursive
9068        // body (decision state, rule call, etc.), the outcomes returned
9069        // below will need those token nodes prepended.
9070        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
9071        let Some(state) = atn.state(state_number) else {
9072            return Vec::new();
9073        };
9074        let transitions = state.transitions();
9075        let transition_count = transitions.len();
9076        if !self.fast_recovery_enabled
9077            && let Some(shape) = fast_repetition_shape(atn, state)
9078        {
9079            let mut outcomes = self.recognize_repetition_fast(
9080                atn,
9081                &FastRecognizeRequest {
9082                    state_number,
9083                    stop_state,
9084                    index,
9085                    rule_start_index,
9086                    decision_start_index,
9087                    precedence,
9088                    depth,
9089                    recovery_symbols: Rc::clone(&recovery_symbols),
9090                    recovery_state,
9091                },
9092                shape,
9093                FastRecognizeScratch {
9094                    predicate_context,
9095                    visiting: &mut *visiting,
9096                    memo: &mut *memo,
9097                    expected: &mut *expected,
9098                    native_depth: native_depth + 1,
9099                },
9100            );
9101            if inline_pending {
9102                for outcome in &mut outcomes {
9103                    outcome.consumed_eof |= inline_consumed_eof;
9104                    if self.fast_token_nodes_enabled {
9105                        for token_index in inline_consumed_tokens.iter().rev() {
9106                            let token = self.arena_token_node(*token_index, false);
9107                            self.defer_fast_outcome_node(outcome, token);
9108                        }
9109                    }
9110                }
9111            }
9112            return outcomes;
9113        }
9114        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
9115        // fields and the rule/decision boundary indices are pure plumbing —
9116        // they only affect the recovery branch and the no-viable diagnostic
9117        // recording, neither of which fires when recovery is off. Zeroing
9118        // them in the memo key collapses calls that visit the same
9119        // `(state, index)` from different rule-call sites onto one cache
9120        // entry, which is the dominant cost on large grammars (e.g. C#) where
9121        // many rules eventually delegate into the same `expression` /
9122        // `primary_expression` / `type` branches.
9123        let key = if self.fast_recovery_enabled {
9124            FastRecognizeKey {
9125                state_number,
9126                stop_state,
9127                index,
9128                rule_start_index,
9129                decision_start_index,
9130                precedence,
9131                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
9132                recovery_state,
9133            }
9134        } else {
9135            FastRecognizeKey {
9136                state_number,
9137                stop_state,
9138                index,
9139                rule_start_index: 0,
9140                decision_start_index: None,
9141                precedence,
9142                recovery_symbols_id: 0,
9143                recovery_state: None,
9144            }
9145        };
9146        // Once the clean-pass probe has established that coordinates do not
9147        // repeat, stop paying for the full memo table. Recovery always keeps
9148        // memoization because cached failures carry diagnostics, while
9149        // repeat-heavy clean parses promote before reaching sparse mode.
9150        let memo_lookup_enabled = self.fast_recovery_enabled
9151            || (transition_count > 1 && self.clean_memo_enabled_for_key(&key));
9152        if memo_lookup_enabled {
9153            if let Some(outcomes) = memo.get(&key) {
9154                #[cfg(feature = "perf-counters")]
9155                {
9156                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
9157                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
9158                }
9159                // Materialize a fresh `Vec` from the cached slice; the caller
9160                // mutates per-outcome state (eof flags, prepended nodes) so we
9161                // can't hand them the shared backing.
9162                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
9163                    let inline_eof = inline_consumed_eof;
9164                    let inline_tokens = &inline_consumed_tokens;
9165                    return outcomes
9166                        .iter()
9167                        .copied()
9168                        .map(|mut outcome| {
9169                            if inline_eof {
9170                                outcome.consumed_eof = true;
9171                            }
9172                            if self.fast_token_nodes_enabled {
9173                                for token_index in inline_tokens.iter().rev() {
9174                                    let token = self.arena_token_node(*token_index, false);
9175                                    self.defer_fast_outcome_node(&mut outcome, token);
9176                                }
9177                            }
9178                            outcome
9179                        })
9180                        .collect();
9181                }
9182                return outcomes.to_vec();
9183            }
9184            #[cfg(feature = "perf-counters")]
9185            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
9186        }
9187
9188        // Cycle detection: clean recognition keeps the narrow static cycle
9189        // guard used on hot paths. Recovery needs the broader epsilon-state
9190        // guard because an otherwise non-nullable loop body can recover as an
9191        // empty child at EOF and re-enter the loop at the same token.
9192        let needs_cycle_guard = if self.fast_recovery_enabled {
9193            transitions.iter().any(ParserTransition::is_epsilon)
9194        } else {
9195            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
9196        };
9197        #[cfg(feature = "perf-counters")]
9198        if needs_cycle_guard {
9199            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
9200        } else {
9201            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
9202            match state
9203                .transitions()
9204                .first()
9205                .expect("single-transition path requires one transition")
9206                .data()
9207            {
9208                Transition::Rule { .. } => {
9209                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
9210                }
9211                Transition::Atom { .. }
9212                | Transition::Range { .. }
9213                | Transition::Set { .. }
9214                | Transition::NotSet { .. }
9215                | Transition::Wildcard { .. } => {
9216                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
9217                }
9218                _ => {
9219                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
9220                }
9221            }
9222        }
9223        let has_inserted_cycle_guard = if needs_cycle_guard {
9224            if !visiting.insert(key.clone()) {
9225                #[cfg(feature = "perf-counters")]
9226                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
9227                return Vec::new();
9228            }
9229            true
9230        } else {
9231            false
9232        };
9233        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
9234            Some(index)
9235        } else {
9236            decision_start_index
9237        };
9238        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
9239            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
9240        } else {
9241            (Rc::clone(&recovery_symbols), recovery_state)
9242        };
9243
9244        // Lookahead-based pruning. At a multi-alternative state we cache the
9245        // look-1 set of every outgoing transition; on visit we keep only the
9246        // transitions whose look-1 can accept the current lookahead (or that
9247        // can be reached without consuming and so could legitimately match a
9248        // shorter input). This is the main speedup vs. blind speculative
9249        // recursion: it lets each visit fan out only to the alternatives that
9250        // could possibly contribute a clean parse, mirroring the SLL phase of
9251        // ANTLR's adaptive prediction.
9252        //
9253        // Pruning is skipped at:
9254        //   * rule-start states (a child rule call may need every internal
9255        //     transition to surface single-token recovery diagnostics that
9256        //     ANTLR's reference parser emits at the rule's first consuming
9257        //     transition; the FIRST-set retry path turns the prefilter off
9258        //     entirely so let's keep this lightweight too),
9259        //   * left-recursive precedence loops (the precedence transition's
9260        //     gating is dynamic),
9261        //   * states with too few alternatives to benefit.
9262        let lookahead_filter = if transition_count > 1
9263            && self.fast_first_set_prefilter
9264            && !state.precedence_rule_decision()
9265            && (!self.fast_recovery_enabled || state.kind() != AtnStateKind::RuleStart)
9266        {
9267            state
9268                .rule_index()
9269                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
9270                .map(|rule_stop| {
9271                    let symbol = self.token_type_at(index);
9272                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
9273                    (symbol, entry)
9274                })
9275        } else {
9276            None
9277        };
9278        // LL(1) fast path: when the FIRST sets for the decision are disjoint
9279        // and none is nullable, the lookahead deterministically selects one
9280        // alternative. The recursive recognizer can then commit to that single
9281        // alt without iterating every transition through `should_skip_via_lookahead`
9282        // — saving (transition_count - 1) filter probes per visit.
9283        //
9284        // Result is cached per `(state, lookahead_token)` on the parser
9285        // instance, so subsequent visits skip the FIRST-set scan entirely.
9286        let ll1_only_alt: Option<usize> = if transition_count > 1
9287            && let Some((symbol, entry)) = lookahead_filter.as_ref()
9288        {
9289            let key = (state.state_number(), *symbol);
9290            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
9291                cached
9292            } else {
9293                let result = ll1_unique_alt(entry, *symbol);
9294                self.ll1_decision_cache.insert(key, result);
9295                result
9296            }
9297        } else {
9298            None
9299        };
9300        let lookahead_filter = lookahead_filter.as_ref();
9301        // Pre-size only when we expect at least one outcome to land — most
9302        // single-transition fall-throughs (the loop above didn't catch
9303        // because they're atom/rule/predicate) push at most one entry, so
9304        // reserving one slot avoids a reallocation while keeping the
9305        // unused-slot waste at one element.
9306        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
9307        for (transition_index, transition) in transitions.iter().enumerate() {
9308            if let Some(alt) = ll1_only_alt {
9309                // LL(1) determinism: skip every alt except the chosen one.
9310                if alt != transition_index {
9311                    continue;
9312                }
9313            }
9314            let transition_kind = transition.kind();
9315            if ll1_only_alt.is_none()
9316                && should_skip_via_lookahead(
9317                    transition_kind,
9318                    transition_index,
9319                    lookahead_filter,
9320                    index,
9321                    self.fast_recovery_enabled,
9322                    expected,
9323                )
9324            {
9325                continue;
9326            }
9327            let target = transition.target();
9328            let outcomes_before_transition = outcomes.len();
9329            let left_recursive_boundary = match transition_kind {
9330                ParserTransitionKind::Epsilon
9331                | ParserTransitionKind::Action
9332                | ParserTransitionKind::Predicate
9333                | ParserTransitionKind::Precedence => left_recursive_boundary(atn, state, target),
9334                ParserTransitionKind::Atom
9335                | ParserTransitionKind::Range
9336                | ParserTransitionKind::Set
9337                | ParserTransitionKind::NotSet
9338                | ParserTransitionKind::Wildcard
9339                | ParserTransitionKind::Rule => None,
9340            };
9341            match transition_kind {
9342                ParserTransitionKind::Epsilon | ParserTransitionKind::Action => {
9343                    #[cfg(feature = "perf-counters")]
9344                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9345                    outcomes.extend(self.recognize_state_fast(
9346                        atn,
9347                        FastRecognizeRequest {
9348                            state_number: target,
9349                            stop_state,
9350                            index,
9351                            rule_start_index,
9352                            decision_start_index: next_decision_start_index,
9353                            precedence,
9354                            depth: depth + 1,
9355                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9356                            recovery_state: epsilon_recovery_state,
9357                        },
9358                        FastRecognizeScratch {
9359                            predicate_context,
9360                            visiting,
9361                            memo,
9362                            expected,
9363                            native_depth: native_depth + 1,
9364                        },
9365                    ));
9366                }
9367                ParserTransitionKind::Predicate => {
9368                    #[cfg(feature = "perf-counters")]
9369                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9370                    if self.fast_parser_predicate_matches(predicate_context, transition, index) {
9371                        outcomes.extend(self.recognize_state_fast(
9372                            atn,
9373                            FastRecognizeRequest {
9374                                state_number: target,
9375                                stop_state,
9376                                index,
9377                                rule_start_index,
9378                                decision_start_index: next_decision_start_index,
9379                                precedence,
9380                                depth: depth + 1,
9381                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9382                                recovery_state: epsilon_recovery_state,
9383                            },
9384                            FastRecognizeScratch {
9385                                predicate_context,
9386                                visiting,
9387                                memo,
9388                                expected,
9389                                native_depth: native_depth + 1,
9390                            },
9391                        ));
9392                    } else {
9393                        record_predicate_no_viable(expected, next_decision_start_index, index);
9394                    }
9395                }
9396                ParserTransitionKind::Precedence => {
9397                    let transition_precedence = packed_i32(transition.arg0());
9398                    if transition_precedence >= precedence {
9399                        outcomes.extend(self.recognize_state_fast(
9400                            atn,
9401                            FastRecognizeRequest {
9402                                state_number: target,
9403                                stop_state,
9404                                index,
9405                                rule_start_index,
9406                                decision_start_index: next_decision_start_index,
9407                                precedence,
9408                                depth: depth + 1,
9409                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9410                                recovery_state: epsilon_recovery_state,
9411                            },
9412                            FastRecognizeScratch {
9413                                predicate_context,
9414                                visiting,
9415                                memo,
9416                                expected,
9417                                native_depth: native_depth + 1,
9418                            },
9419                        ));
9420                    }
9421                }
9422                ParserTransitionKind::Rule => {
9423                    let rule_index = transition.arg0() as usize;
9424                    let follow_state = transition.arg1() as usize;
9425                    let rule_precedence = packed_i32(transition.arg2());
9426                    #[cfg(feature = "perf-counters")]
9427                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
9428                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
9429                        continue;
9430                    };
9431                    // Lookahead-based pruning. The recognizer would otherwise
9432                    // explore every speculative rule call, producing exponential
9433                    // work on grammars with many epsilon-reachable rules. When
9434                    // the rule is non-nullable and its FIRST set excludes the
9435                    // current lookahead, recursion can't find a clean path
9436                    // *through this rule*. Skipping is only safe if some sibling
9437                    // transition can still consume the lookahead — otherwise the
9438                    // rule call is the sole continuation and must run so the
9439                    // single-token insertion / deletion recovery inside the
9440                    // called rule can fire (mirroring ANTLR's reference behavior
9441                    // of conjuring a missing token at child-rule entry).
9442                    let symbol = self.token_type_at(index);
9443                    if self.fast_first_set_prefilter {
9444                        // Probe the shared cross-parse cache first; build
9445                        // the entry on miss and intern it there. The
9446                        // computation is purely a function of the ATN, so
9447                        // the cached entry is reused across parses (and
9448                        // freshly-instantiated parser values that share
9449                        // the same `&'static Atn`).
9450                        //
9451                        // `rule_first_set` returns the computed entry
9452                        // directly — it intentionally skips inserting into
9453                        // the cache when the FIRST-set walk hit a cycle, so
9454                        // we cannot assume the entry is in the cache after
9455                        // computing it.
9456                        let first = self.cached_rule_first_set(atn, target, child_stop);
9457                        if should_skip_rule_via_first_set(
9458                            &first,
9459                            symbol,
9460                            self.fast_recovery_enabled,
9461                            index,
9462                            expected,
9463                        ) {
9464                            continue;
9465                        }
9466                    }
9467                    let expected_before_child =
9468                        self.fast_recovery_enabled.then(|| expected.clone());
9469                    let mut children = self.recognize_state_fast(
9470                        atn,
9471                        FastRecognizeRequest {
9472                            state_number: target,
9473                            stop_state: child_stop,
9474                            index,
9475                            rule_start_index: index,
9476                            decision_start_index: None,
9477                            precedence: rule_precedence,
9478                            depth: depth + 1,
9479                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9480                            recovery_state: epsilon_recovery_state,
9481                        },
9482                        FastRecognizeScratch {
9483                            predicate_context,
9484                            visiting,
9485                            memo,
9486                            expected,
9487                            native_depth: native_depth + 1,
9488                        },
9489                    );
9490                    if children.is_empty() && self.fast_recovery_enabled {
9491                        children = self.fast_child_rule_failure_recovery_outcomes(
9492                            FastChildRuleFailureRecoveryRequest {
9493                                atn,
9494                                rule_index,
9495                                start_index: index,
9496                                follow_state,
9497                                stop_state,
9498                                expected,
9499                            },
9500                        );
9501                    }
9502                    if let Some(expected_before_child) = expected_before_child {
9503                        if children
9504                            .iter()
9505                            .any(|child| child.diagnostics.is_empty() && child.index > index)
9506                        {
9507                            *expected = expected_before_child;
9508                        }
9509                    }
9510                    for child in children {
9511                        let child_index = child.index;
9512                        let child_consumed_eof = child.consumed_eof;
9513                        let child_diagnostics = child.diagnostics;
9514                        let empty_recovery = self.empty_recovery_symbols();
9515                        let follow_outcomes = self.recognize_state_fast(
9516                            atn,
9517                            FastRecognizeRequest {
9518                                state_number: follow_state,
9519                                stop_state,
9520                                index: child_index,
9521                                rule_start_index,
9522                                decision_start_index: next_decision_start_index,
9523                                precedence,
9524                                depth: depth + 1,
9525                                recovery_symbols: empty_recovery,
9526                                recovery_state: None,
9527                            },
9528                            FastRecognizeScratch {
9529                                predicate_context,
9530                                visiting,
9531                                memo,
9532                                expected,
9533                                native_depth: native_depth + 1,
9534                            },
9535                        );
9536                        if follow_outcomes.is_empty() {
9537                            continue;
9538                        }
9539                        let child_stop_index =
9540                            self.rule_stop_token_index(child_index, child_consumed_eof);
9541                        let child_node = self.build_parse_trees.then(|| {
9542                            self.recognition_arena.deferred_rule_node(FastDeferredRule {
9543                                rule_index: u32::try_from(rule_index)
9544                                    .expect("rule index fits in u32"),
9545                                invoking_state: i32::try_from(invoking_state_number(state_number))
9546                                    .expect("invoking state fits in i32"),
9547                                start_index: u32::try_from(index)
9548                                    .expect("rule start index fits in u32"),
9549                                stop_index: child_stop_index.map(|stop_index| {
9550                                    u32::try_from(stop_index).expect("rule stop index fits in u32")
9551                                }),
9552                                deferred_children: child.deferred_nodes,
9553                                children: child.nodes,
9554                            })
9555                        });
9556                        let child_diags_empty = child_diagnostics.is_empty();
9557                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
9558                            outcome.consumed_eof |= child_consumed_eof;
9559                            // Skip the prepend dance when there's nothing to
9560                            // merge from the child — common case in pass 1.
9561                            if !child_diags_empty {
9562                                outcome.diagnostics = self
9563                                    .recognition_arena
9564                                    .concat_diagnostics(child_diagnostics, outcome.diagnostics);
9565                            }
9566                            if let Some(child_node) = child_node {
9567                                outcome.deferred_nodes = self
9568                                    .recognition_arena
9569                                    .concat_deferred_nodes(child_node, outcome.deferred_nodes);
9570                            }
9571                            outcome
9572                        }));
9573                    }
9574                }
9575                ParserTransitionKind::Atom
9576                | ParserTransitionKind::Range
9577                | ParserTransitionKind::Set
9578                | ParserTransitionKind::NotSet
9579                | ParserTransitionKind::Wildcard => {
9580                    #[cfg(feature = "perf-counters")]
9581                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
9582                    let symbol = self.token_type_at(index);
9583                    if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
9584                        let next_index = self.consume_index(index, symbol);
9585                        let empty_recovery = self.empty_recovery_symbols();
9586                        outcomes.extend(
9587                            self.recognize_state_fast(
9588                                atn,
9589                                FastRecognizeRequest {
9590                                    state_number: target,
9591                                    stop_state,
9592                                    index: next_index,
9593                                    rule_start_index,
9594                                    decision_start_index: next_decision_start_index,
9595                                    precedence,
9596                                    depth: depth + 1,
9597                                    recovery_symbols: empty_recovery,
9598                                    recovery_state: None,
9599                                },
9600                                FastRecognizeScratch {
9601                                    predicate_context,
9602                                    visiting,
9603                                    memo,
9604                                    expected,
9605                                    native_depth: native_depth + 1,
9606                                },
9607                            )
9608                            .into_iter()
9609                            .map(|mut outcome| {
9610                                outcome.consumed_eof |= symbol == TOKEN_EOF;
9611                                if self.fast_token_nodes_enabled {
9612                                    let token = self.arena_token_node(index, false);
9613                                    self.defer_fast_outcome_node(&mut outcome, token);
9614                                }
9615                                outcome
9616                            }),
9617                        );
9618                    } else {
9619                        if !self.fast_recovery_enabled {
9620                            // In pass 1 there is no recovery to attempt; the
9621                            // recovery branch below would never run, and the
9622                            // `expected_symbols` computation is just there
9623                            // to gate that branch. Skipping it eliminates
9624                            // ~1× `state_expected_symbols` lookup per failed
9625                            // atom transition (≈82K on mono-statement.cs)
9626                            // for zero observable behavior change.
9627                            continue;
9628                        }
9629                        let expected_symbols = fast_recovery_expected_symbols(
9630                            self,
9631                            atn,
9632                            state.state_number(),
9633                            &recovery_symbols,
9634                        );
9635                        if expected_symbols.contains(&symbol) {
9636                            continue;
9637                        }
9638                        {
9639                            expected.record_transition(index, transition, max_token_type);
9640                            record_no_viable_if_ambiguous(
9641                                expected,
9642                                next_decision_start_index,
9643                                index,
9644                            );
9645                            outcomes.extend(self.fast_single_token_deletion_recovery(
9646                                FastRecoveryRequest {
9647                                    atn,
9648                                    transition,
9649                                    expected_symbols: Rc::clone(&expected_symbols),
9650                                    target,
9651                                    request: FastRecognizeRequest {
9652                                        state_number,
9653                                        stop_state,
9654                                        index,
9655                                        rule_start_index,
9656                                        decision_start_index,
9657                                        precedence,
9658                                        depth,
9659                                        recovery_symbols: Rc::clone(&recovery_symbols),
9660                                        recovery_state,
9661                                    },
9662                                    visiting,
9663                                    memo,
9664                                    expected,
9665                                },
9666                                predicate_context,
9667                            ));
9668                            if !state_is_left_recursive_rule(atn, state) {
9669                                outcomes.extend(self.fast_single_token_insertion_recovery(
9670                                    FastRecoveryRequest {
9671                                        atn,
9672                                        transition,
9673                                        expected_symbols: Rc::clone(&expected_symbols),
9674                                        target,
9675                                        request: FastRecognizeRequest {
9676                                            state_number,
9677                                            stop_state,
9678                                            index,
9679                                            rule_start_index,
9680                                            decision_start_index,
9681                                            precedence,
9682                                            depth,
9683                                            recovery_symbols: Rc::clone(&recovery_symbols),
9684                                            recovery_state,
9685                                        },
9686                                        visiting,
9687                                        memo,
9688                                        expected,
9689                                    },
9690                                    predicate_context,
9691                                ));
9692                            }
9693                            outcomes.extend(self.fast_current_token_deletion_recovery(
9694                                FastCurrentTokenDeletionRequest {
9695                                    atn,
9696                                    expected_symbols,
9697                                    request: FastRecognizeRequest {
9698                                        state_number,
9699                                        stop_state,
9700                                        index,
9701                                        rule_start_index,
9702                                        decision_start_index,
9703                                        precedence,
9704                                        depth,
9705                                        recovery_symbols: Rc::clone(&recovery_symbols),
9706                                        recovery_state,
9707                                    },
9708                                    visiting,
9709                                    memo,
9710                                    expected,
9711                                },
9712                                predicate_context,
9713                            ));
9714                        }
9715                    }
9716                }
9717            }
9718            let alt_number = next_alt_number(
9719                state,
9720                transition_count,
9721                transition_index,
9722                0,
9723                self.fast_track_alt_numbers,
9724            );
9725            if alt_number != 0 || left_recursive_boundary.is_some() {
9726                for outcome in &mut outcomes[outcomes_before_transition..] {
9727                    if alt_number != 0 {
9728                        self.defer_fast_outcome_alternative(outcome, alt_number);
9729                    }
9730                    if let Some(rule_index) = left_recursive_boundary {
9731                        self.defer_fast_outcome_boundary(outcome, rule_index);
9732                    }
9733                }
9734            }
9735        }
9736
9737        if has_inserted_cycle_guard {
9738            visiting.remove(&key);
9739        }
9740        if matches!(
9741            self.prediction_mode,
9742            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
9743        ) && self.fast_recovery_enabled
9744        {
9745            // Without recovery enabled every outcome already has empty
9746            // diagnostics, so the discard pass is a no-op — skipping it
9747            // saves an iter+retain on each of the ~1M visits.
9748            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
9749        }
9750        if self.fast_recovery_enabled {
9751            dedupe_fast_outcomes(&mut outcomes, &self.recognition_arena);
9752        } else {
9753            dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
9754        }
9755        // Skip memoization for single-transition states whose outcome is
9756        // unambiguous: they only get re-entered if the caller revisits the
9757        // exact same call site, which is rare since the loop above already
9758        // collapsed straight-line epsilon walks. Multi-alternative states
9759        // are where backtracking actually revisits the same coordinate, so
9760        // we still memoize there. With recovery on we keep the existing
9761        // memoization unconditionally because the recovery branch may
9762        // record diagnostics that the cache must surface to repeated
9763        // failed visits.
9764        let should_memoize = self.fast_recovery_enabled
9765            || (transition_count > 1 && self.clean_memo_mode != CleanMemoMode::Sparse);
9766        // Apply inline pending state to each outcome before returning.
9767        // Tokens consumed inline by the loop-collapse don't appear in the
9768        // recursive recognizer's output, so we need to prepend them here.
9769        let mut apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
9770            if inline_consumed_eof {
9771                outcome.consumed_eof = true;
9772            }
9773            if !inline_consumed_tokens.is_empty() {
9774                for token_index in inline_consumed_tokens.iter().rev() {
9775                    let token = self.arena_token_node(*token_index, false);
9776                    self.defer_fast_outcome_node(&mut outcome, token);
9777                }
9778            }
9779            outcome
9780        };
9781        if should_memoize {
9782            #[cfg(feature = "perf-counters")]
9783            {
9784                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
9785                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
9786                match outcomes.len() {
9787                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
9788                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
9789                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
9790                }
9791            }
9792            // The memo is keyed by the loop-exit `(state_number, index)` so
9793            // the inline-consumed tokens belong to *this* call's output, not
9794            // the cached result. Memoize the bare outcomes (without the
9795            // inline-pending data), then prepend the inline data on return.
9796            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
9797            memo.insert(key, Rc::clone(&stored));
9798            if inline_pending {
9799                return stored
9800                    .iter()
9801                    .copied()
9802                    .map(&mut apply_inline_pending)
9803                    .collect();
9804            }
9805            return stored.to_vec();
9806        }
9807        #[cfg(feature = "perf-counters")]
9808        match outcomes.len() {
9809            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
9810            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
9811            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
9812        }
9813        if inline_pending {
9814            return outcomes.into_iter().map(apply_inline_pending).collect();
9815        }
9816        outcomes
9817    }
9818
9819    /// Explores single-token deletion recovery while preserving the matched
9820    /// token and skipped error token in the selected parse tree path.
9821    fn single_token_deletion_recovery(
9822        &mut self,
9823        recovery: RecoveryRequest<'_, '_>,
9824    ) -> Vec<RecognizeOutcome> {
9825        let RecoveryRequest {
9826            atn,
9827            transition,
9828            expected_symbols,
9829            target,
9830            request,
9831            visiting,
9832            memo,
9833            expected,
9834        } = recovery;
9835        let RecognizeRequest {
9836            stop_state,
9837            index,
9838            rule_start_index,
9839            decision_start_index,
9840            init_action_rules,
9841            predicates,
9842            semantics,
9843            rule_args,
9844            member_actions,
9845            return_actions,
9846            local_int_arg,
9847            member_values,
9848            return_values,
9849            rule_alt_number,
9850            track_alt_numbers,
9851            consumed_eof,
9852            precedence,
9853            depth,
9854            ..
9855        } = request;
9856        let Some((diagnostic, next_index, next_symbol)) =
9857            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
9858        else {
9859            return Vec::new();
9860        };
9861        let after_next = self.consume_index(next_index, next_symbol);
9862        self.recognize_state(
9863            atn,
9864            RecognizeRequest {
9865                state_number: target,
9866                stop_state,
9867                index: after_next,
9868                rule_start_index,
9869                decision_start_index,
9870                init_action_rules,
9871                predicates,
9872                semantics,
9873                rule_args,
9874                member_actions,
9875                return_actions,
9876                local_int_arg,
9877                member_values,
9878                return_values,
9879                rule_alt_number,
9880                track_alt_numbers,
9881                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
9882                committed_decision: false,
9883                precedence,
9884                depth: depth + 1,
9885                recovery_symbols: BTreeSet::new(),
9886                recovery_state: None,
9887            },
9888            visiting,
9889            memo,
9890            expected,
9891        )
9892        .into_iter()
9893        .map(|mut outcome| {
9894            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
9895            outcome.diagnostics = self
9896                .recognition_arena
9897                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9898            let token = self.arena_token_node(next_index, false);
9899            self.arena_prepend(&mut outcome.nodes, token);
9900            let error = self.arena_token_node(index, true);
9901            self.arena_prepend(&mut outcome.nodes, error);
9902            outcome
9903        })
9904        .collect()
9905    }
9906
9907    /// Retries the current recognition state after deleting one unexpected
9908    /// token, preserving the deleted token as an error node in the parse tree.
9909    fn current_token_deletion_recovery(
9910        &mut self,
9911        recovery: CurrentTokenDeletionRequest<'_, '_>,
9912    ) -> Vec<RecognizeOutcome> {
9913        let CurrentTokenDeletionRequest {
9914            atn,
9915            expected_symbols,
9916            mut request,
9917            visiting,
9918            memo,
9919            expected,
9920        } = recovery;
9921        let error_index = request.index;
9922        if error_index == request.rule_start_index {
9923            return Vec::new();
9924        }
9925        let Some((diagnostic, next_index, skipped)) =
9926            self.current_token_deletion(error_index, &expected_symbols)
9927        else {
9928            return Vec::new();
9929        };
9930        request.state_number = request.recovery_state.unwrap_or(request.state_number);
9931        request.index = next_index;
9932        request.committed_decision = false;
9933        request.depth += 1;
9934        request.recovery_state = None;
9935        self.recognize_state(atn, request, visiting, memo, expected)
9936            .into_iter()
9937            .map(|mut outcome| {
9938                outcome.diagnostics = self
9939                    .recognition_arena
9940                    .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9941                for index in skipped.iter().rev() {
9942                    let error = self.arena_token_node(*index, true);
9943                    self.arena_prepend(&mut outcome.nodes, error);
9944                }
9945                outcome
9946            })
9947            .collect()
9948    }
9949
9950    /// Falls back after deletion/insertion repairs cannot continue from a
9951    /// failed consuming transition.
9952    fn consuming_failure_fallback(
9953        &mut self,
9954        fallback: ConsumingFailureFallback<'_>,
9955        visiting: &mut BTreeSet<RecognizeKey>,
9956        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9957        expected: &mut ExpectedTokens,
9958    ) -> Vec<RecognizeOutcome> {
9959        if fallback.expected_symbols.is_empty() {
9960            return Vec::new();
9961        }
9962        if fallback.symbol == TOKEN_EOF {
9963            return self.eof_consuming_failure_fallback(fallback, expected);
9964        }
9965        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
9966    }
9967
9968    /// Keeps unexpected non-EOF input visible as an error node when no repair
9969    /// path can otherwise reach the transition target.
9970    fn non_eof_consuming_failure_fallback(
9971        &mut self,
9972        fallback: ConsumingFailureFallback<'_>,
9973        visiting: &mut BTreeSet<RecognizeKey>,
9974        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
9975        expected: &mut ExpectedTokens,
9976    ) -> Vec<RecognizeOutcome> {
9977        let ConsumingFailureFallback {
9978            atn,
9979            target,
9980            request,
9981            symbol,
9982            expected_symbols,
9983            decision_start_index,
9984            decision,
9985        } = fallback;
9986        let error_index = request.index;
9987        let diagnostic =
9988            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
9989        let next_index = self.consume_index(error_index, symbol);
9990        self.recognize_state(
9991            atn,
9992            RecognizeRequest {
9993                state_number: target,
9994                stop_state: request.stop_state,
9995                index: next_index,
9996                rule_start_index: request.rule_start_index,
9997                decision_start_index,
9998                init_action_rules: request.init_action_rules,
9999                predicates: request.predicates,
10000                semantics: request.semantics,
10001                rule_args: request.rule_args,
10002                member_actions: request.member_actions,
10003                return_actions: request.return_actions,
10004                local_int_arg: request.local_int_arg,
10005                member_values: request.member_values,
10006                return_values: request.return_values,
10007                rule_alt_number: request.rule_alt_number,
10008                track_alt_numbers: request.track_alt_numbers,
10009                consumed_eof: request.consumed_eof,
10010                committed_decision: false,
10011                precedence: request.precedence,
10012                depth: request.depth + 1,
10013                recovery_symbols: BTreeSet::new(),
10014                recovery_state: None,
10015            },
10016            visiting,
10017            memo,
10018            expected,
10019        )
10020        .into_iter()
10021        .map(|mut outcome| {
10022            prepend_decision(&mut outcome, decision);
10023            outcome.diagnostics = self
10024                .recognition_arena
10025                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
10026            let error = self.arena_token_node(error_index, true);
10027            self.arena_prepend(&mut outcome.nodes, error);
10028            outcome
10029        })
10030        .collect()
10031    }
10032
10033    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
10034    /// behavior of unwinding instead of inserting caller tokens at EOF.
10035    fn eof_consuming_failure_fallback(
10036        &mut self,
10037        fallback: ConsumingFailureFallback<'_>,
10038        expected: &ExpectedTokens,
10039    ) -> Vec<RecognizeOutcome> {
10040        let request = fallback.request;
10041        if request.index == request.rule_start_index {
10042            return Vec::new();
10043        }
10044        let diagnostic =
10045            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
10046        let diagnostics = self
10047            .recognition_arena
10048            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
10049        vec![RecognizeOutcome {
10050            index: request.index,
10051            consumed_eof: request.consumed_eof,
10052            alt_number: request.rule_alt_number,
10053            member_values: request.member_values,
10054            return_values: request.return_values,
10055            diagnostics,
10056            decisions: Vec::new(),
10057            actions: Vec::new(),
10058            nodes: NodeSeqId::EMPTY,
10059        }]
10060    }
10061
10062    /// Explores single-token insertion recovery while adding a conjured
10063    /// missing-token error node to the selected parse tree path.
10064    fn single_token_insertion_recovery(
10065        &mut self,
10066        recovery: RecoveryRequest<'_, '_>,
10067    ) -> Vec<RecognizeOutcome> {
10068        let RecoveryRequest {
10069            atn,
10070            transition,
10071            expected_symbols,
10072            target,
10073            request,
10074            visiting,
10075            memo,
10076            expected,
10077        } = recovery;
10078        let RecognizeRequest {
10079            stop_state,
10080            index,
10081            rule_start_index,
10082            decision_start_index,
10083            init_action_rules,
10084            predicates,
10085            semantics,
10086            rule_args,
10087            member_actions,
10088            return_actions,
10089            local_int_arg,
10090            member_values,
10091            return_values,
10092            rule_alt_number,
10093            track_alt_numbers,
10094            consumed_eof,
10095            precedence,
10096            depth,
10097            ..
10098        } = request;
10099        let follow_symbols = state_expected_symbols(atn, transition.target());
10100        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
10101            transition,
10102            index,
10103            atn.max_token_type(),
10104            &expected_symbols,
10105            &follow_symbols,
10106        ) else {
10107            return Vec::new();
10108        };
10109        self.recognize_state(
10110            atn,
10111            RecognizeRequest {
10112                state_number: target,
10113                stop_state,
10114                index,
10115                rule_start_index,
10116                decision_start_index,
10117                init_action_rules,
10118                predicates,
10119                semantics,
10120                rule_args,
10121                member_actions,
10122                return_actions,
10123                local_int_arg,
10124                member_values,
10125                return_values,
10126                rule_alt_number,
10127                track_alt_numbers,
10128                consumed_eof,
10129                committed_decision: false,
10130                precedence,
10131                depth: depth + 1,
10132                recovery_symbols: BTreeSet::new(),
10133                recovery_state: None,
10134            },
10135            visiting,
10136            memo,
10137            expected,
10138        )
10139        .into_iter()
10140        .map(|mut outcome| {
10141            outcome.diagnostics = self
10142                .recognition_arena
10143                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
10144            let missing = self.arena_missing_token_node(token_type, index, text.clone());
10145            self.arena_prepend(&mut outcome.nodes, missing);
10146            outcome
10147        })
10148        .collect()
10149    }
10150
10151    /// Attempts to reach `stop_state` and carries semantic actions for the
10152    /// selected parser path.
10153    #[allow(clippy::too_many_lines)]
10154    fn recognize_state(
10155        &mut self,
10156        atn: &Atn,
10157        request: RecognizeRequest<'_>,
10158        visiting: &mut BTreeSet<RecognizeKey>,
10159        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
10160        expected: &mut ExpectedTokens,
10161    ) -> Vec<RecognizeOutcome> {
10162        let request_template = request.clone();
10163        let RecognizeRequest {
10164            state_number,
10165            stop_state,
10166            index,
10167            rule_start_index,
10168            decision_start_index,
10169            init_action_rules,
10170            predicates,
10171            semantics,
10172            rule_args,
10173            member_actions,
10174            return_actions,
10175            local_int_arg,
10176            member_values,
10177            return_values,
10178            rule_alt_number,
10179            track_alt_numbers,
10180            consumed_eof,
10181            committed_decision,
10182            precedence,
10183            depth,
10184            recovery_symbols,
10185            recovery_state,
10186        } = request;
10187        if depth > RECOGNITION_DEPTH_LIMIT {
10188            return Vec::new();
10189        }
10190        if state_number == stop_state {
10191            return stop_outcome(
10192                index,
10193                consumed_eof,
10194                rule_alt_number,
10195                member_values,
10196                return_values,
10197            );
10198        }
10199        let key = RecognizeKey {
10200            state_number,
10201            stop_state,
10202            index,
10203            rule_start_index,
10204            decision_start_index,
10205            local_int_arg,
10206            member_values: member_values.clone(),
10207            return_values: return_values.clone(),
10208            rule_alt_number,
10209            track_alt_numbers,
10210            consumed_eof,
10211            committed_decision,
10212            precedence,
10213            recovery_symbols: recovery_symbols.clone(),
10214            recovery_state,
10215        };
10216        if let Some(outcomes) = memo.get(&key) {
10217            return outcomes.clone();
10218        }
10219
10220        let visit_key = key.clone();
10221        if !visiting.insert(visit_key.clone()) {
10222            return Vec::new();
10223        }
10224
10225        let Some(state) = atn.state(state_number) else {
10226            visiting.remove(&visit_key);
10227            return Vec::new();
10228        };
10229        let decision_override_generation = self.decision_override_generation;
10230        let transitions = state.transitions();
10231        let transition_count = transitions.len();
10232        let overridden_transition = if transition_count > 1
10233            && self.semantic_hooks.observes_parser_decisions()
10234        {
10235            atn.decision_to_state()
10236                .iter()
10237                .position(|candidate| candidate == state_number)
10238                .and_then(|decision| {
10239                    self.semantic_hooks
10240                        .parser_decision_override(decision, index, transition_count)
10241                })
10242                .and_then(|alternative| alternative.checked_sub(1))
10243                .filter(|alternative| *alternative < transition_count)
10244        } else {
10245            None
10246        };
10247        if overridden_transition.is_some() {
10248            self.decision_override_generation = self.decision_override_generation.wrapping_add(1);
10249        }
10250        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
10251            Some(index)
10252        } else {
10253            decision_start_index
10254        };
10255        let (epsilon_recovery_symbols, epsilon_recovery_state) =
10256            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
10257        let mut outcomes = Vec::new();
10258        for (transition_index, transition) in transitions.iter().enumerate() {
10259            if overridden_transition.is_some_and(|forced| forced != transition_index) {
10260                continue;
10261            }
10262            let transition_committed =
10263                committed_decision || overridden_transition == Some(transition_index);
10264            let mut transition_request = request_template.clone();
10265            transition_request.committed_decision = transition_committed;
10266            let decision =
10267                transition_decision(atn, state, transition_count, transition_index, predicates);
10268            let next_alt_number = next_alt_number(
10269                state,
10270                transition_count,
10271                transition_index,
10272                rule_alt_number,
10273                track_alt_numbers,
10274            );
10275            let transition_data = transition.data();
10276            match &transition_data {
10277                Transition::Epsilon { target } | Transition::Action { target, .. } => {
10278                    let action_rule_index = match &transition_data {
10279                        Transition::Action { rule_index, .. } => Some(*rule_index),
10280                        _ => None,
10281                    };
10282                    outcomes.extend(self.recognize_epsilon_or_action_step(
10283                        atn,
10284                        &transition_request,
10285                        EpsilonActionStep {
10286                            source_state: state_number,
10287                            target: *target,
10288                            action_rule_index,
10289                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
10290                            decision,
10291                            decision_start_index: next_decision_start_index,
10292                            alt_number: next_alt_number,
10293                            recovery_symbols: epsilon_recovery_symbols.clone(),
10294                            recovery_state: epsilon_recovery_state,
10295                        },
10296                        RecognizeScratch {
10297                            visiting,
10298                            memo,
10299                            expected,
10300                        },
10301                    ));
10302                }
10303                Transition::Predicate {
10304                    target,
10305                    rule_index,
10306                    pred_index,
10307                    ..
10308                } => {
10309                    let predicate = PredicateEval {
10310                        index,
10311                        rule_index: *rule_index,
10312                        pred_index: *pred_index,
10313                        predicates,
10314                        semantics,
10315                        context: None,
10316                        local_int_arg,
10317                        member_values: &member_values,
10318                    };
10319                    if self.parser_predicate_matches(predicate) {
10320                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
10321                        outcomes.extend(
10322                            self.recognize_state(
10323                                atn,
10324                                RecognizeRequest {
10325                                    state_number: *target,
10326                                    stop_state,
10327                                    index,
10328                                    rule_start_index,
10329                                    decision_start_index: next_decision_start_index,
10330                                    init_action_rules,
10331                                    predicates,
10332                                    semantics,
10333                                    rule_args,
10334                                    member_actions,
10335                                    return_actions,
10336                                    local_int_arg,
10337                                    member_values: member_values.clone(),
10338                                    return_values: return_values.clone(),
10339                                    rule_alt_number: next_alt_number,
10340                                    track_alt_numbers,
10341                                    consumed_eof,
10342                                    committed_decision: transition_committed,
10343                                    precedence,
10344                                    depth: depth + 1,
10345                                    recovery_symbols: epsilon_recovery_symbols.clone(),
10346                                    recovery_state: epsilon_recovery_state,
10347                                },
10348                                visiting,
10349                                memo,
10350                                expected,
10351                            )
10352                            .into_iter()
10353                            .map(|mut outcome| {
10354                                prepend_decision(&mut outcome, decision);
10355                                if let Some(rule_index) = left_recursive_boundary {
10356                                    let boundary =
10357                                        self.arena_boundary_node(rule_index, next_alt_number);
10358                                    self.arena_prepend(&mut outcome.nodes, boundary);
10359                                }
10360                                outcome
10361                            }),
10362                        );
10363                    } else if let Some(message) = semantics
10364                        .and_then(|semantics| {
10365                            self.parser_semantic_ir_predicate_failure_message(
10366                                *rule_index,
10367                                *pred_index,
10368                                semantics,
10369                            )
10370                        })
10371                        .or_else(|| {
10372                            self.parser_predicate_failure_message(
10373                                *rule_index,
10374                                *pred_index,
10375                                predicates,
10376                            )
10377                        })
10378                    {
10379                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
10380                            rule_index: *rule_index,
10381                            index,
10382                            message,
10383                            member_values: member_values.clone(),
10384                            return_values: return_values.clone(),
10385                            rule_alt_number,
10386                        }));
10387                    } else {
10388                        record_predicate_no_viable(expected, next_decision_start_index, index);
10389                    }
10390                }
10391                Transition::Precedence {
10392                    target,
10393                    precedence: transition_precedence,
10394                } => {
10395                    if *transition_precedence >= precedence {
10396                        outcomes.extend(
10397                            self.recognize_state(
10398                                atn,
10399                                RecognizeRequest {
10400                                    state_number: *target,
10401                                    stop_state,
10402                                    index,
10403                                    rule_start_index,
10404                                    decision_start_index: next_decision_start_index,
10405                                    init_action_rules,
10406                                    predicates,
10407                                    semantics,
10408                                    rule_args,
10409                                    member_actions,
10410                                    return_actions,
10411                                    local_int_arg,
10412                                    member_values: member_values.clone(),
10413                                    return_values: return_values.clone(),
10414                                    rule_alt_number: next_alt_number,
10415                                    track_alt_numbers,
10416                                    consumed_eof,
10417                                    committed_decision: transition_committed,
10418                                    precedence,
10419                                    depth: depth + 1,
10420                                    recovery_symbols: epsilon_recovery_symbols.clone(),
10421                                    recovery_state: epsilon_recovery_state,
10422                                },
10423                                visiting,
10424                                memo,
10425                                expected,
10426                            )
10427                            .into_iter()
10428                            .map(|mut outcome| {
10429                                prepend_decision(&mut outcome, decision);
10430                                outcome
10431                            }),
10432                        );
10433                    }
10434                }
10435                Transition::Rule {
10436                    target,
10437                    rule_index,
10438                    follow_state,
10439                    precedence: rule_precedence,
10440                    ..
10441                } => {
10442                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
10443                        continue;
10444                    };
10445                    let child_local_int_arg =
10446                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
10447                    let expected_before_child = expected.clone();
10448                    let children = self.recognize_state(
10449                        atn,
10450                        RecognizeRequest {
10451                            state_number: *target,
10452                            stop_state: child_stop,
10453                            index,
10454                            rule_start_index: index,
10455                            decision_start_index: None,
10456                            init_action_rules,
10457                            predicates,
10458                            semantics,
10459                            rule_args,
10460                            member_actions,
10461                            return_actions,
10462                            local_int_arg: child_local_int_arg,
10463                            member_values: member_values.clone(),
10464                            return_values: BTreeMap::new(),
10465                            rule_alt_number: 0,
10466                            track_alt_numbers,
10467                            consumed_eof: false,
10468                            committed_decision: transition_committed,
10469                            precedence: *rule_precedence,
10470                            depth: depth + 1,
10471                            recovery_symbols: epsilon_recovery_symbols.clone(),
10472                            recovery_state: epsilon_recovery_state,
10473                        },
10474                        visiting,
10475                        memo,
10476                        expected,
10477                    );
10478                    let children = if children.is_empty() {
10479                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
10480                            atn,
10481                            rule_index: *rule_index,
10482                            start_index: index,
10483                            follow_state: *follow_state,
10484                            stop_state,
10485                            member_values: member_values.clone(),
10486                            expected,
10487                        })
10488                    } else {
10489                        children
10490                    };
10491                    let preserve_child_expected =
10492                        self.child_expected_reaches_clean_eof(&children, expected);
10493                    restore_expected(
10494                        &children,
10495                        index,
10496                        expected,
10497                        expected_before_child,
10498                        preserve_child_expected,
10499                    );
10500                    for child in children {
10501                        let child_stop_index =
10502                            self.rule_stop_token_index(child.index, child.consumed_eof);
10503                        let child_nodes = self
10504                            .recognition_arena
10505                            .fold_left_recursive_boundaries(child.nodes);
10506                        let child_node = self.arena_rule_node(ArenaRuleSpec {
10507                            rule_index: *rule_index,
10508                            invoking_state: invoking_state_number(state_number),
10509                            alt_number: child.alt_number,
10510                            start_index: index,
10511                            stop_index: child_stop_index,
10512                            return_values: child.return_values.clone(),
10513                            children: child_nodes,
10514                        });
10515                        outcomes.extend(
10516                            self.recognize_state(
10517                                atn,
10518                                RecognizeRequest {
10519                                    state_number: *follow_state,
10520                                    stop_state,
10521                                    index: child.index,
10522                                    rule_start_index,
10523                                    decision_start_index: next_decision_start_index,
10524                                    init_action_rules,
10525                                    predicates,
10526                                    semantics,
10527                                    rule_args,
10528                                    member_actions,
10529                                    return_actions,
10530                                    local_int_arg,
10531                                    member_values: child.member_values.clone(),
10532                                    return_values: return_values.clone(),
10533                                    rule_alt_number,
10534                                    track_alt_numbers,
10535                                    consumed_eof: consumed_eof || child.consumed_eof,
10536                                    committed_decision: transition_committed
10537                                        && child.index == index,
10538                                    precedence,
10539                                    depth: depth + 1,
10540                                    recovery_symbols: BTreeSet::new(),
10541                                    recovery_state: None,
10542                                },
10543                                visiting,
10544                                memo,
10545                                expected,
10546                            )
10547                            .into_iter()
10548                            .map(|mut outcome| {
10549                                outcome.consumed_eof |= child.consumed_eof;
10550                                outcome.diagnostics = self
10551                                    .recognition_arena
10552                                    .concat_diagnostics(child.diagnostics, outcome.diagnostics);
10553                                let mut decisions = child.decisions.clone();
10554                                decisions.append(&mut outcome.decisions);
10555                                outcome.decisions = decisions;
10556                                prepend_decision(&mut outcome, decision);
10557                                let mut actions = child.actions.clone();
10558                                if init_action_rules.contains(rule_index) {
10559                                    actions.insert(
10560                                        0,
10561                                        ParserAction::new_rule_init(
10562                                            *rule_index,
10563                                            index,
10564                                            Some(*follow_state),
10565                                        ),
10566                                    );
10567                                }
10568                                actions.append(&mut outcome.actions);
10569                                outcome.actions = actions;
10570                                self.arena_prepend(&mut outcome.nodes, child_node);
10571                                outcome
10572                            }),
10573                        );
10574                    }
10575                }
10576                Transition::Atom { target, .. }
10577                | Transition::Range { target, .. }
10578                | Transition::Set { target, .. }
10579                | Transition::NotSet { target, .. }
10580                | Transition::Wildcard { target, .. } => {
10581                    let symbol = self.token_type_at(index);
10582                    if transition_data.matches(symbol, 1, atn.max_token_type()) {
10583                        let next_index = self.consume_index(index, symbol);
10584                        outcomes.extend(
10585                            self.recognize_state(
10586                                atn,
10587                                RecognizeRequest {
10588                                    state_number: *target,
10589                                    stop_state,
10590                                    index: next_index,
10591                                    rule_start_index,
10592                                    decision_start_index: next_decision_start_index,
10593                                    init_action_rules,
10594                                    predicates,
10595                                    semantics,
10596                                    rule_args,
10597                                    member_actions,
10598                                    return_actions,
10599                                    local_int_arg,
10600                                    member_values: member_values.clone(),
10601                                    return_values: return_values.clone(),
10602                                    rule_alt_number: next_alt_number,
10603                                    track_alt_numbers,
10604                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
10605                                    committed_decision: false,
10606                                    precedence,
10607                                    depth: depth + 1,
10608                                    recovery_symbols: BTreeSet::new(),
10609                                    recovery_state: None,
10610                                },
10611                                visiting,
10612                                memo,
10613                                expected,
10614                            )
10615                            .into_iter()
10616                            .map(|mut outcome| {
10617                                prepend_decision(&mut outcome, decision);
10618                                outcome.consumed_eof |= symbol == TOKEN_EOF;
10619                                let token = self.arena_token_node(index, false);
10620                                self.arena_prepend(&mut outcome.nodes, token);
10621                                outcome
10622                            }),
10623                        );
10624                    } else {
10625                        let expected_symbols =
10626                            recovery_expected_symbols(atn, state.state_number(), &recovery_symbols);
10627                        if expected_symbols.contains(&symbol) && !transition_committed {
10628                            continue;
10629                        }
10630                        expected.record_transition(index, transition, atn.max_token_type());
10631                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
10632                        let before_recovery = outcomes.len();
10633                        let recovery_request = transition_request.clone();
10634                        if transition_committed {
10635                            outcomes.extend(self.consuming_failure_fallback(
10636                                ConsumingFailureFallback {
10637                                    atn,
10638                                    target: *target,
10639                                    request: recovery_request,
10640                                    symbol,
10641                                    expected_symbols,
10642                                    decision_start_index: next_decision_start_index,
10643                                    decision,
10644                                },
10645                                visiting,
10646                                memo,
10647                                expected,
10648                            ));
10649                            break;
10650                        }
10651                        outcomes.extend(
10652                            self.single_token_deletion_recovery(RecoveryRequest {
10653                                atn,
10654                                transition,
10655                                expected_symbols: expected_symbols.clone(),
10656                                target: *target,
10657                                request: recovery_request.clone(),
10658                                visiting,
10659                                memo,
10660                                expected,
10661                            })
10662                            .into_iter()
10663                            .map(|mut outcome| {
10664                                prepend_decision(&mut outcome, decision);
10665                                outcome
10666                            }),
10667                        );
10668                        if !state_is_left_recursive_rule(atn, state) {
10669                            outcomes.extend(
10670                                self.single_token_insertion_recovery(RecoveryRequest {
10671                                    atn,
10672                                    transition,
10673                                    expected_symbols: expected_symbols.clone(),
10674                                    target: *target,
10675                                    request: recovery_request.clone(),
10676                                    visiting,
10677                                    memo,
10678                                    expected,
10679                                })
10680                                .into_iter()
10681                                .map(|mut outcome| {
10682                                    prepend_decision(&mut outcome, decision);
10683                                    outcome
10684                                }),
10685                            );
10686                        }
10687                        outcomes.extend(self.current_token_deletion_recovery(
10688                            CurrentTokenDeletionRequest {
10689                                atn,
10690                                expected_symbols: expected_symbols.clone(),
10691                                request: recovery_request.clone(),
10692                                visiting,
10693                                memo,
10694                                expected,
10695                            },
10696                        ));
10697                        if outcomes.len() == before_recovery {
10698                            outcomes.extend(self.consuming_failure_fallback(
10699                                ConsumingFailureFallback {
10700                                    atn,
10701                                    target: *target,
10702                                    request: recovery_request,
10703                                    symbol,
10704                                    expected_symbols,
10705                                    decision_start_index: next_decision_start_index,
10706                                    decision,
10707                                },
10708                                visiting,
10709                                memo,
10710                                expected,
10711                            ));
10712                        }
10713                    }
10714                }
10715            }
10716            if self.decision_override_generation != decision_override_generation {
10717                break;
10718            }
10719        }
10720
10721        visiting.remove(&visit_key);
10722        self.record_prediction_diagnostics(atn, state, index, &outcomes);
10723        if matches!(
10724            self.prediction_mode,
10725            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
10726        ) {
10727            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes, &self.recognition_arena);
10728        }
10729        dedupe_outcomes(&mut outcomes, &self.recognition_arena);
10730        memo.insert(key, outcomes.clone());
10731        outcomes
10732    }
10733
10734    /// Follows an epsilon or semantic-action transition while preserving the
10735    /// path-local side effects that may later become generated action output.
10736    fn recognize_epsilon_or_action_step(
10737        &mut self,
10738        atn: &Atn,
10739        request: &RecognizeRequest<'_>,
10740        step: EpsilonActionStep,
10741        scratch: RecognizeScratch<'_>,
10742    ) -> Vec<RecognizeOutcome> {
10743        let RecognizeScratch {
10744            visiting,
10745            memo,
10746            expected,
10747        } = scratch;
10748        let action = step.action_rule_index.map(|rule_index| {
10749            ParserAction::new(
10750                step.source_state,
10751                rule_index,
10752                request.rule_start_index,
10753                self.rule_stop_token_index(request.index, request.consumed_eof),
10754            )
10755        });
10756        let next_member_values = if action.is_some() {
10757            member_values_after_action(
10758                step.source_state,
10759                request.member_actions,
10760                request.semantics,
10761                &request.member_values,
10762            )
10763        } else {
10764            request.member_values.clone()
10765        };
10766        let next_return_values = action.map_or_else(
10767            || request.return_values.clone(),
10768            |action| {
10769                return_values_after_action(
10770                    step.source_state,
10771                    action.rule_index(),
10772                    request.return_actions,
10773                    request.semantics,
10774                    &request.return_values,
10775                )
10776            },
10777        );
10778
10779        self.recognize_state(
10780            atn,
10781            RecognizeRequest {
10782                state_number: step.target,
10783                stop_state: request.stop_state,
10784                index: request.index,
10785                rule_start_index: request.rule_start_index,
10786                decision_start_index: step.decision_start_index,
10787                init_action_rules: request.init_action_rules,
10788                predicates: request.predicates,
10789                semantics: request.semantics,
10790                rule_args: request.rule_args,
10791                member_actions: request.member_actions,
10792                return_actions: request.return_actions,
10793                local_int_arg: request.local_int_arg,
10794                member_values: next_member_values,
10795                return_values: next_return_values,
10796                rule_alt_number: if step.left_recursive_boundary.is_some() {
10797                    0
10798                } else {
10799                    step.alt_number
10800                },
10801                track_alt_numbers: request.track_alt_numbers,
10802                consumed_eof: request.consumed_eof,
10803                committed_decision: request.committed_decision,
10804                precedence: request.precedence,
10805                depth: request.depth + 1,
10806                recovery_symbols: step.recovery_symbols,
10807                recovery_state: step.recovery_state,
10808            },
10809            visiting,
10810            memo,
10811            expected,
10812        )
10813        .into_iter()
10814        .map(|mut outcome| {
10815            prepend_decision(&mut outcome, step.decision);
10816            if let Some(rule_index) = step.left_recursive_boundary {
10817                let boundary = self.arena_boundary_node(rule_index, step.alt_number);
10818                self.arena_prepend(&mut outcome.nodes, boundary);
10819            }
10820            if let Some(action) = action {
10821                outcome.actions.insert(0, action);
10822            }
10823            outcome
10824        })
10825        .collect()
10826    }
10827
10828    /// Reads the token type at an absolute token-stream index without moving
10829    /// the parser's stream cursor. The fast recognizer probes lookahead at
10830    /// every state visit, so avoiding the seek round-trip is a measurable
10831    /// hot-path win on long inputs.
10832    fn token_type_at(&mut self, index: usize) -> i32 {
10833        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
10834            self.input.fill();
10835        }
10836        self.input.token_type_at_index(index)
10837    }
10838
10839    /// Returns the cached `state_expected_symbols` set for an ATN state.
10840    ///
10841    /// The fast recognizer consults this set on every state visit through
10842    /// `next_recovery_context`; the underlying DFS is a pure function of the
10843    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
10844    ///
10845    /// Caching is layered through `intern_recovery_symbols` so two ATN states
10846    /// with the same expected-symbol set share one `Rc`. That invariant is
10847    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
10848    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
10849    /// always interned before it ends up in a key.
10850    fn cached_state_expected_symbols(
10851        &mut self,
10852        atn: &Atn,
10853        state_number: usize,
10854    ) -> Rc<BTreeSet<i32>> {
10855        if let Some(cached) = self.state_expected_cache.get(&state_number) {
10856            return Rc::clone(cached);
10857        }
10858        let symbols = state_expected_symbols(atn, state_number);
10859        let entry = self.intern_recovery_symbols(symbols);
10860        self.state_expected_cache
10861            .insert(state_number, Rc::clone(&entry));
10862        entry
10863    }
10864
10865    fn cached_state_expected_token_set(
10866        &mut self,
10867        atn: &Atn,
10868        state_number: usize,
10869    ) -> Rc<TokenBitSet> {
10870        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
10871            return Rc::clone(cached);
10872        }
10873        // Purely a function of the ATN, so back the per-parser cache with the
10874        // thread-shared one — fresh parser instances (one per parse in
10875        // generated usage) start warm instead of rewalking the ATN.
10876        let symbols = with_shared_atn_caches(atn, |cache| {
10877            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
10878                return Rc::clone(cached);
10879            }
10880            let symbols = Rc::new(state_expected_token_set(atn, state_number));
10881            cache
10882                .state_expected_tokens
10883                .insert(state_number, Rc::clone(&symbols));
10884            symbols
10885        });
10886        self.state_expected_token_cache
10887            .insert(state_number, Rc::clone(&symbols));
10888        symbols
10889    }
10890
10891    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
10892        if self.rule_stop_reach_cache.len() <= state_number {
10893            self.rule_stop_reach_cache
10894                .resize_with(atn.states().len().max(state_number + 1), || None);
10895        }
10896        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
10897            return reaches;
10898        }
10899        let reaches = with_shared_atn_caches(atn, |cache| {
10900            *cache
10901                .rule_stop_reach
10902                .entry(state_number)
10903                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
10904        });
10905        self.rule_stop_reach_cache[state_number] = Some(reaches);
10906        reaches
10907    }
10908
10909    /// Returns the parser's empty `recovery_symbols` singleton so callers can
10910    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
10911    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
10912        Rc::clone(&self.empty_recovery_symbols)
10913    }
10914
10915    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
10916    /// recognizer can hash and compare keys by pointer.
10917    ///
10918    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
10919    /// come from this method or the empty singleton; otherwise two
10920    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
10921    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
10922    /// recognition coordinates.
10923    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
10924        if set.is_empty() {
10925            return Rc::clone(&self.empty_recovery_symbols);
10926        }
10927        let candidate = Rc::new(set);
10928        match self.recovery_symbols_intern.get(&candidate) {
10929            Some(existing) => Rc::clone(existing),
10930            None => {
10931                self.recovery_symbols_intern
10932                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
10933                candidate
10934            }
10935        }
10936    }
10937
10938    /// Returns the cached look-1 entry for a decision state, computing it on
10939    /// first use. Multi-alternative states are visited many times during
10940    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
10941    /// hash lookup per visit.
10942    fn cached_decision_lookahead(
10943        &mut self,
10944        atn: &Atn,
10945        state: AtnState<'_>,
10946        rule_stop_state: usize,
10947    ) -> Rc<DecisionLookahead> {
10948        // Hit the parser-instance cache first. Decision lookahead is purely
10949        // a function of the ATN/state, so on a warm cache we skip the
10950        // thread-local + RefCell + HashMap-entry dance through
10951        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
10952        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
10953        // hashmap-entry overhead in profiles.
10954        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number()) {
10955            return Rc::clone(cached);
10956        }
10957        let entry = with_shared_atn_caches(atn, |cache| {
10958            if let Some(cached) = cache.decision_lookahead.get(&state.state_number()) {
10959                return Rc::clone(cached);
10960            }
10961            let mut entry = DecisionLookahead {
10962                transitions: Vec::with_capacity(state.transitions().len()),
10963            };
10964            for transition in &state.transitions() {
10965                entry.transitions.push(transition_first_set(
10966                    atn,
10967                    transition,
10968                    rule_stop_state,
10969                    &mut cache.first_set,
10970                ));
10971            }
10972            let entry = Rc::new(entry);
10973            cache
10974                .decision_lookahead
10975                .insert(state.state_number(), Rc::clone(&entry));
10976            entry
10977        });
10978        self.decision_lookahead_cache
10979            .insert(state.state_number(), Rc::clone(&entry));
10980        entry
10981    }
10982
10983    fn cached_rule_first_set(
10984        &mut self,
10985        atn: &Atn,
10986        target: usize,
10987        child_stop: usize,
10988    ) -> Rc<FirstSet> {
10989        if self.rule_first_set_cache.len() <= target {
10990            self.rule_first_set_cache
10991                .resize_with(atn.states().len().max(target + 1), || None);
10992        }
10993        if let Some(cached) = self
10994            .rule_first_set_cache
10995            .get(target)
10996            .and_then(Option::as_ref)
10997        {
10998            return Rc::clone(cached);
10999        }
11000        let first = with_shared_first_set_cache(atn, |cache| {
11001            rule_first_set(atn, target, child_stop, cache)
11002        });
11003        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
11004        first
11005    }
11006
11007    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
11008        let atn_key = SharedAtnCacheKey::for_atn(atn);
11009        if self.empty_cycle_cache_atn != Some(atn_key) {
11010            self.empty_cycle_cache.clear();
11011            self.empty_cycle_cache_atn = Some(atn_key);
11012        }
11013        if self.empty_cycle_cache.len() <= state_number {
11014            self.empty_cycle_cache
11015                .resize_with(atn.state_count().max(state_number + 1), || None);
11016        }
11017        if let Some(cached) = self.empty_cycle_cache[state_number] {
11018            return cached;
11019        }
11020        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
11021        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
11022        self.empty_cycle_cache[state_number] = Some(result);
11023        result
11024    }
11025
11026    fn empty_path_reaches_state(
11027        &mut self,
11028        atn: &Atn,
11029        state_number: usize,
11030        target_state: usize,
11031        visited: &mut FxHashSet<usize>,
11032    ) -> bool {
11033        enum Work {
11034            Visit(usize),
11035            RuleFollow {
11036                target: usize,
11037                rule_index: usize,
11038                follow_state: usize,
11039            },
11040        }
11041
11042        let mut work = vec![Work::Visit(state_number)];
11043        while let Some(item) = work.pop() {
11044            match item {
11045                Work::Visit(state_number) => {
11046                    if !visited.insert(state_number) {
11047                        continue;
11048                    }
11049                    let Some(state) = atn.state(state_number) else {
11050                        continue;
11051                    };
11052                    let transitions = state.transitions();
11053                    for transition_index in (0..transitions.len()).rev() {
11054                        let transition = transitions
11055                            .get(transition_index)
11056                            .expect("in-bounds parser transition");
11057                        let kind = transition.kind();
11058                        let target = transition.target();
11059                        match kind {
11060                            ParserTransitionKind::Atom
11061                            | ParserTransitionKind::Range
11062                            | ParserTransitionKind::Set
11063                            | ParserTransitionKind::NotSet
11064                            | ParserTransitionKind::Wildcard => {}
11065                            ParserTransitionKind::Rule => {
11066                                if target == target_state {
11067                                    return true;
11068                                }
11069                                work.push(Work::RuleFollow {
11070                                    target,
11071                                    rule_index: transition.arg0() as usize,
11072                                    follow_state: transition.arg1() as usize,
11073                                });
11074                                work.push(Work::Visit(target));
11075                            }
11076                            ParserTransitionKind::Epsilon
11077                            | ParserTransitionKind::Predicate
11078                            | ParserTransitionKind::Action
11079                            | ParserTransitionKind::Precedence => {
11080                                if target == target_state {
11081                                    return true;
11082                                }
11083                                work.push(Work::Visit(target));
11084                            }
11085                        }
11086                    }
11087                }
11088                Work::RuleFollow {
11089                    target,
11090                    rule_index,
11091                    follow_state,
11092                } => {
11093                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
11094                        continue;
11095                    };
11096                    if self.cached_rule_first_set(atn, target, child_stop).nullable {
11097                        if follow_state == target_state {
11098                            return true;
11099                        }
11100                        work.push(Work::Visit(follow_state));
11101                    }
11102                }
11103            }
11104        }
11105        false
11106    }
11107
11108    /// Decides whether the clean recognizer should use its full outcome memo
11109    /// table for this coordinate.
11110    fn clean_memo_enabled_for_key(&mut self, key: &FastRecognizeKey) -> bool {
11111        match self.clean_memo_mode {
11112            CleanMemoMode::Promote => true,
11113            CleanMemoMode::Probe => self.observe_clean_memo_probe(key),
11114            CleanMemoMode::Sparse => {
11115                self.clean_memo_sparse_samples += 1;
11116                if self.clean_memo_sparse_samples < CLEAN_MEMO_REPROBE_INTERVAL {
11117                    return false;
11118                }
11119                self.clean_memo_sparse_samples = 0;
11120                self.clean_memo_mode = CleanMemoMode::Probe;
11121                self.clean_memo_probe_samples = 0;
11122                self.clean_memo_probe_repeats = 0;
11123                self.clean_memo_probe_seen.clear();
11124                self.observe_clean_memo_probe(key)
11125            }
11126        }
11127    }
11128
11129    fn observe_clean_memo_probe(&mut self, key: &FastRecognizeKey) -> bool {
11130        self.clean_memo_probe_samples += 1;
11131        if !self.clean_memo_probe_seen.insert(key.clone()) {
11132            self.clean_memo_probe_repeats += 1;
11133        }
11134        if self.clean_memo_probe_repeats >= CLEAN_MEMO_REPEAT_LIMIT {
11135            self.clean_memo_mode = CleanMemoMode::Promote;
11136            self.clean_memo_probe_seen.clear();
11137            return true;
11138        }
11139        if self.clean_memo_probe_samples >= CLEAN_MEMO_PROBE_LIMIT {
11140            self.clean_memo_mode = CleanMemoMode::Sparse;
11141            self.clean_memo_sparse_samples = 0;
11142            self.clean_memo_probe_seen.clear();
11143            return false;
11144        }
11145        true
11146    }
11147
11148    /// Borrows the visible token at an absolute token-stream index.
11149    fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
11150        self.input.get(index)
11151    }
11152
11153    /// Returns the compact token ID at an absolute token-stream index.
11154    fn token_id_at(&self, index: usize) -> Option<TokenId> {
11155        self.input.get_id(index)
11156    }
11157
11158    fn arena_token_node(&mut self, index: usize, error: bool) -> RecognizedNodeId {
11159        let token = self
11160            .token_id_at(index)
11161            .expect("recognized token index must exist in the token store");
11162        let node = if error {
11163            ArenaRecognizedNode::ErrorToken { token }
11164        } else {
11165            ArenaRecognizedNode::Token { token }
11166        };
11167        self.recognition_arena.push_node(node)
11168    }
11169
11170    fn arena_missing_token_node(
11171        &mut self,
11172        token_type: i32,
11173        at_index: usize,
11174        text: String,
11175    ) -> RecognizedNodeId {
11176        let extra = self
11177            .recognition_arena
11178            .push_extra(RecognitionExtra::MissingToken {
11179                token_type,
11180                at_index: u32::try_from(at_index).expect("missing-token stream index fits in u32"),
11181                text,
11182            });
11183        self.recognition_arena
11184            .push_node(ArenaRecognizedNode::MissingToken { extra })
11185    }
11186
11187    fn arena_rule_node(&mut self, spec: ArenaRuleSpec) -> RecognizedNodeId {
11188        let ArenaRuleSpec {
11189            rule_index,
11190            invoking_state,
11191            alt_number,
11192            start_index,
11193            stop_index,
11194            return_values,
11195            children,
11196        } = spec;
11197        let return_values = (!return_values.is_empty()).then(|| {
11198            self.recognition_arena
11199                .push_extra(RecognitionExtra::ReturnValues(return_values))
11200        });
11201        self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
11202            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
11203            invoking_state: i32::try_from(invoking_state).expect("invoking state fits in i32"),
11204            alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
11205            start_index: u32::try_from(start_index).expect("rule start index fits in u32"),
11206            stop_index: stop_index
11207                .map(|index| u32::try_from(index).expect("rule stop index fits in u32")),
11208            return_values,
11209            children,
11210        })
11211    }
11212
11213    fn arena_boundary_node(&mut self, rule_index: usize, alt_number: usize) -> RecognizedNodeId {
11214        self.recognition_arena
11215            .push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
11216                rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
11217                alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
11218            })
11219    }
11220
11221    fn arena_prepend(&mut self, sequence: &mut NodeSeqId, node: RecognizedNodeId) {
11222        *sequence = self.recognition_arena.prepend(*sequence, node);
11223    }
11224
11225    fn finish_recognition_arena(&mut self, root: NodeSeqId, diagnostics: DiagnosticSeqId) {
11226        self.last_recognition_arena_root = root;
11227        self.last_recognition_arena_diagnostics = diagnostics;
11228        #[cfg(feature = "perf-counters")]
11229        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
11230            let stats = self.recognition_arena_stats();
11231            #[allow(clippy::print_stderr)]
11232            {
11233                eprintln!("perf recognition_nodes_total={}", stats.total_nodes);
11234                eprintln!("perf recognition_nodes_live={}", stats.live_nodes);
11235                eprintln!("perf recognition_nodes_dead={}", stats.dead_nodes);
11236                eprintln!("perf recognition_nodes_capacity={}", stats.node_capacity);
11237                eprintln!("perf recognition_links_total={}", stats.total_links);
11238                eprintln!("perf recognition_links_live={}", stats.live_links);
11239                eprintln!("perf recognition_links_dead={}", stats.dead_links);
11240                eprintln!("perf recognition_links_capacity={}", stats.link_capacity);
11241                eprintln!("perf recognition_extras_total={}", stats.total_extras);
11242                eprintln!("perf recognition_extras_live={}", stats.live_extras);
11243                eprintln!("perf recognition_extras_dead={}", stats.dead_extras);
11244                eprintln!("perf recognition_extras_capacity={}", stats.extra_capacity);
11245            }
11246        }
11247    }
11248
11249    fn reset_recognition_arena(&mut self) {
11250        self.recognition_arena.reset();
11251        self.last_recognition_arena_root = NodeSeqId::EMPTY;
11252        self.last_recognition_arena_diagnostics = DiagnosticSeqId::EMPTY;
11253    }
11254
11255    /// Normalizes the current token-stream cursor to the next parser-visible
11256    /// token before capturing a rule start boundary.
11257    fn current_visible_index(&mut self) -> usize {
11258        let index = self.input.index();
11259        self.input.seek(index);
11260        self.input.index()
11261    }
11262
11263    /// Reports whether a child rule reached EOF cleanly while also recording
11264    /// an EOF expectation from a longer path inside that child.
11265    fn child_expected_reaches_clean_eof(
11266        &mut self,
11267        children: &[RecognizeOutcome],
11268        expected: &ExpectedTokens,
11269    ) -> bool {
11270        let Some(index) = expected.index else {
11271            return false;
11272        };
11273        self.token_type_at(index) == TOKEN_EOF
11274            && children
11275                .iter()
11276                .any(|child| child.diagnostics.is_empty() && child.index == index)
11277    }
11278
11279    /// Finds the previous token visible to the parser before `index`.
11280    ///
11281    /// The token stream cursor skips hidden-channel tokens, so subtracting one
11282    /// from a visible-token index can point at whitespace. Parser intervals use
11283    /// this helper to stop at the previous visible token while preserving hidden
11284    /// text inside the rendered interval.
11285    fn previous_token_index(&self, index: usize) -> Option<usize> {
11286        self.input.previous_visible_token_index(index)
11287    }
11288
11289    /// Returns the token-stream index used as a rule stop boundary.
11290    ///
11291    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
11292    /// stop at `index` rather than at the previous visible token.
11293    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
11294        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
11295            Some(index)
11296        } else {
11297            self.previous_token_index(index)
11298        }
11299    }
11300
11301    /// Stop-token index for a rule's `@after` action, matching the boundary that
11302    /// `finish_rule` records on the rule context.
11303    ///
11304    /// A rule that matched EOF leaves the cursor parked on the EOF token
11305    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
11306    /// the current index rather than the previous visible token. Without this,
11307    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
11308    /// report the token before EOF (or `None` for empty input), diverging from
11309    /// the rule context that `finish_rule` builds.
11310    ///
11311    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
11312    /// rule ends right before EOF without matching it (the cursor is parked on
11313    /// EOF, but the rule did not consume it). Prefer
11314    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
11315    /// rule context already recorded with the real flag. Kept for callers without
11316    /// the rule tree in hand.
11317    #[must_use]
11318    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
11319        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
11320        self.rule_stop_token_index(current_index, consumed_eof)
11321    }
11322
11323    /// Stop-token index for a rule's `@after` action, taken from the stop token
11324    /// the rule context already recorded.
11325    ///
11326    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
11327    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
11328    /// the rule context — even when the rule ends immediately before EOF without
11329    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
11330    /// to the cursor-based inference only when the tree carries no rule stop.
11331    #[must_use]
11332    pub fn after_action_stop_index_for_tree(
11333        &mut self,
11334        tree: ParseTree,
11335        current_index: usize,
11336    ) -> Option<usize> {
11337        if let Some(stop) = self
11338            .node(tree)
11339            .as_rule()
11340            .and_then(crate::tree::RuleNodeView::stop_id)
11341        {
11342            return Some(stop.index());
11343        }
11344        self.after_action_stop_index(current_index)
11345    }
11346
11347    /// Start-token index for a rule's `@after` action, taken from the start token
11348    /// the rule context already recorded.
11349    ///
11350    /// `enter_rule` sets the rule context start to the first visible token (it
11351    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
11352    /// `$text` in an `@after` action aligned with the rule context — even when the
11353    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
11354    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
11355    /// the tree carries no rule start.
11356    #[must_use]
11357    pub fn after_action_start_index_for_tree(
11358        &self,
11359        tree: ParseTree,
11360        fallback_index: usize,
11361    ) -> usize {
11362        if let Some(start) = self
11363            .node(tree)
11364            .as_rule()
11365            .and_then(crate::tree::RuleNodeView::start_id)
11366        {
11367            return start.index();
11368        }
11369        fallback_index
11370    }
11371
11372    /// Returns the rule stop token for a selected parse path.
11373    ///
11374    /// EOF transitions do not advance the token-stream cursor, so an EOF match
11375    /// must use the current token rather than the previous visible token.
11376    fn rule_stop_token_id(&mut self, index: usize, consumed_eof: bool) -> Option<TokenId> {
11377        self.rule_stop_token_index(index, consumed_eof)
11378            .and_then(|token_index| self.token_id_at(token_index))
11379    }
11380
11381    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
11382    ///
11383    /// Generated Java reports the failed-predicate message at the current
11384    /// lookahead, then consumes until rule recovery can resume. The metadata
11385    /// runtime models the same visible tree shape by keeping skipped tokens as
11386    /// error nodes and returning from the active rule at EOF.
11387    fn predicate_failure_recovery(
11388        &mut self,
11389        request: PredicateFailureRecovery<'_>,
11390    ) -> RecognizeOutcome {
11391        let PredicateFailureRecovery {
11392            rule_index,
11393            index,
11394            message,
11395            member_values,
11396            return_values,
11397            rule_alt_number,
11398        } = request;
11399        let rule_name = self
11400            .rule_names()
11401            .get(rule_index)
11402            .map_or_else(|| rule_index.to_string(), Clone::clone);
11403        let diagnostic = diagnostic_for_token(
11404            self.token_at(index).as_ref(),
11405            format!("rule {rule_name} {message}"),
11406        );
11407        let mut reversed_nodes = NodeSeqId::EMPTY;
11408        let mut next_index = index;
11409        loop {
11410            let symbol = self.token_type_at(next_index);
11411            if symbol == TOKEN_EOF {
11412                break;
11413            }
11414            let error = self.arena_token_node(next_index, true);
11415            self.arena_prepend(&mut reversed_nodes, error);
11416            let after = self.consume_index(next_index, symbol);
11417            if after == next_index {
11418                break;
11419            }
11420            next_index = after;
11421        }
11422        let nodes = self.recognition_arena.reverse_sequence(reversed_nodes);
11423        let diagnostics = self
11424            .recognition_arena
11425            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
11426        RecognizeOutcome {
11427            index: next_index,
11428            consumed_eof: false,
11429            alt_number: rule_alt_number,
11430            member_values,
11431            return_values,
11432            diagnostics,
11433            decisions: Vec::new(),
11434            actions: Vec::new(),
11435            nodes,
11436        }
11437    }
11438
11439    /// Evaluates a user hook for a predicate coordinate that has no generated
11440    /// runtime table entry.
11441    fn parser_semantic_hook_result(
11442        &mut self,
11443        request: ParserSemanticHookRequest<'_>,
11444    ) -> Option<bool> {
11445        let ParserSemanticHookRequest {
11446            index,
11447            rule_index,
11448            pred_index,
11449            context,
11450            local_int_arg,
11451            member_values,
11452        } = request;
11453        let rule_name = self.rule_names().get(rule_index).cloned();
11454        self.input.seek(index);
11455        let input = &mut self.input;
11456        let semantic_hooks = &mut self.semantic_hooks;
11457        let mut ctx = ParserSemCtx {
11458            input,
11459            tree_storage: &self.tree,
11460            rule_index,
11461            coordinate_index: pred_index,
11462            rule_name,
11463            context,
11464            tree: None,
11465            local_int_arg,
11466            member_values,
11467            action: None,
11468        };
11469        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
11470    }
11471
11472    /// Re-inserts unknown-predicate coordinates recorded before a nested
11473    /// interpreted recognition, preserving order and skipping any the nested
11474    /// call already recorded, so a generated parent's fail-loud coordinates
11475    /// survive descending into an interpreted child.
11476    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
11477        if prior.is_empty() {
11478            return;
11479        }
11480        let mut merged = prior;
11481        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
11482            if !merged.contains(&coordinate) {
11483                merged.push(coordinate);
11484            }
11485        }
11486        self.unknown_predicate_hits = merged;
11487    }
11488
11489    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
11490    /// that has no entry in the generated predicate table.
11491    ///
11492    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
11493    /// the guarded path is abandoned; the parse entry surfaces the recorded
11494    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
11495    /// because a parse that consulted an unknown predicate is unreliable no
11496    /// matter which paths were ultimately selected.
11497    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
11498        apply_unknown_predicate_policy(
11499            self.unknown_predicate_policy,
11500            rule_index,
11501            pred_index,
11502            &mut self.unknown_predicate_hits,
11503        )
11504    }
11505
11506    /// Builds the fail-loud error for unknown predicate coordinates recorded
11507    /// by the current parse, if any.
11508    fn unknown_semantic_error(&self) -> Option<AntlrError> {
11509        use std::fmt::Write as _;
11510        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
11511            return None;
11512        }
11513        let mut message = String::new();
11514        for (rule_index, pred_index) in &self.unknown_predicate_hits {
11515            if !message.is_empty() {
11516                message.push_str("; ");
11517            }
11518            let _ = match self.rule_names().get(*rule_index) {
11519                Some(rule_name) => write!(
11520                    message,
11521                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
11522                ),
11523                None => write!(
11524                    message,
11525                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
11526                ),
11527            };
11528        }
11529        for (rule_index, source_state) in &self.unhandled_action_hits {
11530            if !message.is_empty() {
11531                message.push_str("; ");
11532            }
11533            let _ = match self.rule_names().get(*rule_index) {
11534                Some(rule_name) => write!(
11535                    message,
11536                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
11537                ),
11538                None => write!(
11539                    message,
11540                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
11541                ),
11542            };
11543        }
11544        Some(AntlrError::Unsupported(message))
11545    }
11546
11547    /// Evaluates one lowered predicate expression at the requested input
11548    /// position.
11549    ///
11550    /// This sits in the prediction hot loop, so the context borrows the
11551    /// speculative member state read-only and the rule name by reference —
11552    /// no per-evaluation allocation. Only the hook escape path materializes
11553    /// owned copies, and only when a hook is actually consulted.
11554    fn parser_semir_predicate_matches(
11555        &mut self,
11556        semantics: &ParserSemantics,
11557        predicate: &ParserSemanticPredicate,
11558        request: ParserSemanticHookRequest<'_>,
11559    ) -> bool {
11560        self.input.seek(request.index);
11561        let rule_name = self
11562            .data
11563            .rule_names()
11564            .get(request.rule_index)
11565            .map(String::as_str);
11566        let unknown_predicate_policy = self.unknown_predicate_policy;
11567        let mut ctx = ParserSemIrCtx {
11568            input: &mut self.input,
11569            tree_storage: &self.tree,
11570            semantic_hooks: &mut self.semantic_hooks,
11571            rule_index: request.rule_index,
11572            coordinate_index: request.pred_index,
11573            rule_name,
11574            context: request.context,
11575            local_int_arg: request.local_int_arg,
11576            member_values: request.member_values,
11577            invoked_predicates: &mut self.invoked_predicates,
11578            unknown_predicate_policy,
11579            unknown_predicate_hits: &mut self.unknown_predicate_hits,
11580        };
11581        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
11582    }
11583
11584    fn fast_parser_predicate_matches(
11585        &mut self,
11586        context: Option<FastPredicateContext<'_>>,
11587        transition: ParserTransition<'_>,
11588        index: usize,
11589    ) -> bool {
11590        let Some(context) = context else {
11591            return true;
11592        };
11593        let rule_index = transition.arg0() as usize;
11594        let pred_index = transition.arg1() as usize;
11595        let key = (index, rule_index, pred_index);
11596        if let Some(result) = self.fast_predicate_cache.get(&key) {
11597            return *result;
11598        }
11599        let result = self.parser_predicate_matches(PredicateEval {
11600            index,
11601            rule_index,
11602            pred_index,
11603            predicates: context.predicates,
11604            semantics: context.semantics,
11605            context: None,
11606            local_int_arg: None,
11607            member_values: context.member_values,
11608        });
11609        self.fast_predicate_cache.insert(key, result);
11610        result
11611    }
11612
11613    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
11614        let PredicateEval {
11615            index,
11616            rule_index,
11617            pred_index,
11618            predicates,
11619            semantics,
11620            context,
11621            local_int_arg,
11622            member_values,
11623        } = eval;
11624        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
11625            semantics
11626                .predicates
11627                .iter()
11628                .find(|predicate| {
11629                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
11630                })
11631                .map(|predicate| (semantics, predicate))
11632        }) {
11633            return self.parser_semir_predicate_matches(
11634                semantics,
11635                predicate,
11636                ParserSemanticHookRequest {
11637                    index,
11638                    rule_index,
11639                    pred_index,
11640                    context,
11641                    local_int_arg,
11642                    member_values,
11643                },
11644            );
11645        }
11646        let Some((_, _, predicate)) = predicates
11647            .iter()
11648            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
11649        else {
11650            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
11651                index,
11652                rule_index,
11653                pred_index,
11654                context,
11655                local_int_arg,
11656                member_values,
11657            }) {
11658                return result;
11659            }
11660            return self.unknown_predicate_result(rule_index, pred_index);
11661        };
11662        self.input.seek(index);
11663        match predicate {
11664            ParserPredicate::True => true,
11665            ParserPredicate::False => false,
11666            ParserPredicate::FalseWithMessage { .. } => false,
11667            ParserPredicate::Invoke { value } => {
11668                let key = (rule_index, pred_index);
11669                if !self.invoked_predicates.contains(&key) {
11670                    self.invoked_predicates.push(key);
11671                    use std::io::Write as _;
11672                    let mut stdout = std::io::stdout().lock();
11673                    let _ = writeln!(stdout, "eval={value}");
11674                }
11675                *value
11676            }
11677            ParserPredicate::LookaheadTextEquals { offset, text } => self
11678                .input
11679                .lt(*offset)
11680                .is_some_and(|token| Token::text(&token) == Some(*text)),
11681            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
11682                self.la(*offset) != *token_type
11683            }
11684            ParserPredicate::TokenPairAdjacent => {
11685                let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
11686                    return false;
11687                };
11688                let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
11689                    return false;
11690                };
11691                first + 1 == second
11692            }
11693            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
11694                .and_then(|context| {
11695                    context
11696                        .child_rules(&self.tree, self.input.token_store(), *rule_index)
11697                        .next()
11698                        .map(crate::tree::RuleNodeView::text)
11699                })
11700                .is_none_or(|actual| actual != *text),
11701            ParserPredicate::LocalIntEquals { value } => {
11702                local_int_arg.is_none_or(|(_, actual)| actual == *value)
11703            }
11704            ParserPredicate::LocalIntLessOrEqual { value } => {
11705                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
11706            }
11707            ParserPredicate::MemberModuloEquals {
11708                member,
11709                modulus,
11710                value,
11711                equals,
11712            } => {
11713                if *modulus == 0 {
11714                    return false;
11715                }
11716                let actual = member_values.scalar(*member).unwrap_or_default() % *modulus;
11717                (actual == *value) == *equals
11718            }
11719            ParserPredicate::MemberEquals {
11720                member,
11721                value,
11722                equals,
11723            } => {
11724                let actual = member_values.scalar(*member).unwrap_or_default();
11725                (actual == *value) == *equals
11726            }
11727        }
11728    }
11729
11730    /// Returns a generated fail-option message for a predicate coordinate.
11731    fn parser_predicate_failure_message(
11732        &self,
11733        rule_index: usize,
11734        pred_index: usize,
11735        predicates: &[(usize, usize, ParserPredicate)],
11736    ) -> Option<&'static str> {
11737        predicates
11738            .iter()
11739            .find_map(|(rule, pred, predicate)| match predicate {
11740                ParserPredicate::FalseWithMessage { message }
11741                    if *rule == rule_index && *pred == pred_index =>
11742                {
11743                    Some(*message)
11744                }
11745                _ => None,
11746            })
11747    }
11748
11749    /// Returns a generated fail-option message for a `SemIR` predicate
11750    /// coordinate.
11751    pub fn parser_semantic_ir_predicate_failure_message(
11752        &self,
11753        rule_index: usize,
11754        pred_index: usize,
11755        semantics: &ParserSemantics,
11756    ) -> Option<&'static str> {
11757        semantics
11758            .predicates
11759            .iter()
11760            .find(|predicate| {
11761                predicate.rule_index == rule_index && predicate.pred_index == pred_index
11762            })
11763            .and_then(|predicate| predicate.failure_message)
11764    }
11765
11766    /// Returns the token-stream index after consuming `symbol` at `index`.
11767    ///
11768    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
11769    /// index stable and rely on `consumed_eof` to record that EOF was matched.
11770    /// The parser's stream cursor is left untouched: speculative recognition
11771    /// reads ahead by absolute index, so paying for `seek` on every visited
11772    /// state would dominate the hot path. Real consumption is committed by
11773    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
11774    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
11775        if symbol == TOKEN_EOF {
11776            return index;
11777        }
11778        self.input.next_visible_after(index)
11779    }
11780
11781    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
11782    /// decision that failed after consuming a shared prefix.
11783    fn no_viable_alternative(&self, start_index: usize, error_index: usize) -> ParserDiagnostic {
11784        let text = display_input_text(&self.input.text(start_index, error_index));
11785        diagnostic_for_token(
11786            self.token_at(error_index).as_ref(),
11787            format!("no viable alternative at input '{text}'"),
11788        )
11789    }
11790
11791    /// Selects the diagnostic for a failed consuming transition after all
11792    /// recovery repairs have been ruled out.
11793    fn recovery_failure_diagnostic(
11794        &self,
11795        index: usize,
11796        decision_start_index: Option<usize>,
11797        expected_symbols: &BTreeSet<i32>,
11798    ) -> ParserDiagnostic {
11799        if expected_symbols.len() > 1 {
11800            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
11801                return self.no_viable_alternative(decision_start, index);
11802            }
11803        }
11804        diagnostic_for_token(
11805            self.token_at(index).as_ref(),
11806            format!(
11807                "mismatched input {} expecting {}",
11808                self.token_at(index)
11809                    .as_ref()
11810                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
11811                self.expected_symbols_display(expected_symbols)
11812            ),
11813        )
11814    }
11815
11816    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
11817    /// instead of inserting missing tokens in the caller.
11818    fn eof_rule_recovery_diagnostic(
11819        &self,
11820        index: usize,
11821        expected_symbols: &BTreeSet<i32>,
11822        expected: &ExpectedTokens,
11823    ) -> ParserDiagnostic {
11824        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
11825            &expected.symbols
11826        } else {
11827            expected_symbols
11828        };
11829        diagnostic_for_token(
11830            self.token_at(index).as_ref(),
11831            format!(
11832                "mismatched input {} expecting {}",
11833                self.token_at(index)
11834                    .as_ref()
11835                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
11836                self.expected_symbols_display(symbols)
11837            ),
11838        )
11839    }
11840
11841    /// Returns token text for a buffered token interval used by generated
11842    /// `$text` actions.
11843    ///
11844    /// ANTLR treats EOF as a range boundary rather than printable input text,
11845    /// even when an action interval explicitly stops at the EOF token.
11846    pub fn text_interval(&self, start: usize, stop: Option<usize>) -> String {
11847        let Some(stop) = stop else {
11848            return String::new();
11849        };
11850        let stop = if self
11851            .token_at(stop)
11852            .is_some_and(|token| token.token_type() == TOKEN_EOF)
11853        {
11854            let Some(previous) = self.previous_token_index(stop) else {
11855                return String::new();
11856            };
11857            previous
11858        } else {
11859            stop
11860        };
11861        self.input.text(start, stop)
11862    }
11863
11864    /// Resets per-parse prediction diagnostics while keeping the parser-level
11865    /// reporting flag configured by generated harness code.
11866    fn clear_prediction_diagnostics(&mut self) {
11867        self.prediction_diagnostics.clear();
11868        self.reported_prediction_diagnostics.clear();
11869    }
11870
11871    /// Drops every per-parse cache that depends on ATN identity or pins
11872    /// recovery-symbol allocations.
11873    ///
11874    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
11875    /// same parser instance can legally be driven against different grammars
11876    /// in sequence. The four caches reset here are keyed by raw ATN
11877    /// coordinates (state numbers, rule indexes) and would silently hand back
11878    /// entries from a previous ATN if reused — pruning lookahead against the
11879    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
11880    /// for the rest of the process. Clearing them on every parse entry keeps
11881    /// the perf wins (caches still amortize within one parse) without making
11882    /// long-lived parsers leak memory or surface stale ATN data:
11883    ///
11884    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
11885    ///   functions of the ATN's state graph.
11886    /// * `state_expected_cache`, `state_expected_token_cache`,
11887    ///   `rule_stop_reach_cache`, and
11888    ///   `recovery_symbols_intern` together form
11889    ///   the identity invariant that lets `FastRecognizeKey` hash
11890    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
11891    ///   so a stale interned `Rc` cannot outlive its map entry.
11892    /// * `empty_cycle_cache` is grammar-static and carries its own ATN key, so
11893    ///   it is retained here and invalidated lazily when the ATN changes.
11894    fn reset_per_parse_caches(&mut self) {
11895        self.rule_first_set_cache.clear();
11896        self.decision_lookahead_cache.clear();
11897        self.ll1_decision_cache.clear();
11898        self.fast_predicate_cache.clear();
11899        self.rule_stop_reach_cache.clear();
11900        self.clean_memo_mode = CleanMemoMode::Probe;
11901        self.clean_memo_probe_seen.clear();
11902        self.clean_memo_probe_samples = 0;
11903        self.clean_memo_probe_repeats = 0;
11904        self.clean_memo_sparse_samples = 0;
11905        self.recovery_symbols_intern.clear();
11906        self.state_expected_cache.clear();
11907        self.state_expected_token_cache.clear();
11908    }
11909
11910    /// Buffers ANTLR-style diagnostic-listener messages for decision states
11911    /// where multiple clean alternatives survive full-context recognition.
11912    fn record_prediction_diagnostics(
11913        &mut self,
11914        atn: &Atn,
11915        state: AtnState<'_>,
11916        start_index: usize,
11917        outcomes: &[RecognizeOutcome],
11918    ) {
11919        if !self.report_diagnostic_errors || state.transitions().len() < 2 {
11920            return;
11921        }
11922        let Some(decision) = atn
11923            .decision_to_state()
11924            .iter()
11925            .position(|state_number| state_number == state.state_number())
11926        else {
11927            return;
11928        };
11929        let Some(rule_index) = state.rule_index() else {
11930            return;
11931        };
11932        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
11933        for outcome in outcomes
11934            .iter()
11935            .filter(|outcome| outcome.diagnostics.is_empty())
11936        {
11937            let Some(alt) = outcome.decisions.first() else {
11938                continue;
11939            };
11940            alts_by_end
11941                .entry(outcome.index)
11942                .or_default()
11943                .insert(alt + 1);
11944        }
11945        let Some((&end_index, ambig_alts)) = alts_by_end
11946            .iter()
11947            .filter(|(_, alts)| alts.len() > 1)
11948            .max_by_key(|(end, _)| *end)
11949        else {
11950            return;
11951        };
11952        let rule_name = self
11953            .rule_names()
11954            .get(rule_index)
11955            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
11956        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
11957        let input = display_input_text(&self.input.text(start_index, stop_index));
11958        let alts = ambig_alts
11959            .iter()
11960            .map(usize::to_string)
11961            .collect::<Vec<_>>()
11962            .join(", ");
11963        let key = (decision, start_index, format!("{alts}:{input}"));
11964        if !self.reported_prediction_diagnostics.insert(key) {
11965            return;
11966        }
11967        let start_diagnostic = diagnostic_for_token(
11968            self.token_at(start_index),
11969            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
11970        );
11971        let stop_diagnostic = diagnostic_for_token(
11972            self.token_at(stop_index),
11973            format!(
11974                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
11975            ),
11976        );
11977        self.prediction_diagnostics.push(start_diagnostic);
11978        self.prediction_diagnostics.push(stop_diagnostic);
11979    }
11980
11981    /// Formats the tokens expected from an ATN state using ANTLR display names.
11982    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
11983        expected_symbols_display(
11984            &state_expected_symbols(atn, state_number),
11985            self.vocabulary(),
11986        )
11987    }
11988
11989    /// Expected-token set at the parser's current ATN state — ANTLR's
11990    /// `getExpectedTokens()`. Generated recognizers expose this as
11991    /// `self.expected_tokens()` for embedded test actions
11992    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
11993    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
11994        let state = usize::try_from(self.data().state()).unwrap_or(0);
11995        ExpectedTokenSet {
11996            symbols: state_expected_symbols(atn, state),
11997        }
11998    }
11999
12000    /// Enables the bail error strategy: the first syntax error aborts the
12001    /// parse instead of recovering.
12002    pub const fn set_bail_on_error(&mut self, bail: bool) {
12003        self.bail_on_error = bail;
12004    }
12005
12006    /// Whether the bail error strategy is active.
12007    #[must_use]
12008    pub const fn bail_on_error(&self) -> bool {
12009        self.bail_on_error
12010    }
12011
12012    /// Names of the rules on the live invocation stack, current rule first —
12013    /// ANTLR's `getRuleInvocationStack()`.
12014    pub fn rule_invocation_stack(&self) -> Vec<String> {
12015        self.rule_context_stack
12016            .iter()
12017            .rev()
12018            .map(|frame| {
12019                self.data()
12020                    .rule_names()
12021                    .get(frame.rule_index)
12022                    .cloned()
12023                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
12024            })
12025            .collect()
12026    }
12027
12028    /// Invoking-state chain for the active rule context, current rule first.
12029    ///
12030    /// The root frame is excluded, matching Java's `RuleContext.toString()`.
12031    pub fn active_invocation_states(&self) -> Vec<isize> {
12032        self.rule_context_stack
12033            .iter()
12034            .skip(1)
12035            .rev()
12036            .map(|frame| frame.invoking_state)
12037            .collect()
12038    }
12039
12040    /// Formats a buffered token in ANTLR's diagnostic token display form.
12041    pub fn token_display_at(&self, index: usize) -> Option<String> {
12042        self.token_at(index).map(|token| format!("{token}"))
12043    }
12044}
12045
12046impl<'atn, S, H> DirectAdaptiveParser<'atn, '_, S, H>
12047where
12048    S: TokenSource,
12049    H: SemanticHooks,
12050{
12051    fn parse_rule(
12052        &mut self,
12053        rule_index: usize,
12054        invoking_state: isize,
12055        precedence: i32,
12056    ) -> DirectAdaptiveParseResult<ParseTree> {
12057        let start_state = self.atn.rule_to_start_state().get(rule_index).ok_or(
12058            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
12059        )?;
12060        let stop_state = self
12061            .atn
12062            .rule_to_stop_state()
12063            .get(rule_index)
12064            .filter(|state| *state != usize::MAX)
12065            .ok_or(DirectAdaptiveParseControl::Fallback(
12066                DirectAdaptiveFallback::MissingAtn,
12067            ))?;
12068        let start_index = self.parser.current_visible_index();
12069        let mut context = ParserRuleContext::new(rule_index, invoking_state);
12070        if let Some(token) = self.parser.token_id_at(start_index) {
12071            self.parser.set_context_start(&mut context, token);
12072        }
12073        let mut state_number = start_state;
12074        let mut consumed_eof = false;
12075        while state_number != stop_state {
12076            self.step()?;
12077            let (transition, boundary) = self.next_transition(state_number, precedence)?;
12078            if boundary.is_some() {
12079                return Err(DirectAdaptiveParseControl::Fallback(
12080                    DirectAdaptiveFallback::LeftRecursiveBoundary,
12081                ));
12082            }
12083            match transition.data() {
12084                Transition::Epsilon { target } => {
12085                    state_number = target;
12086                }
12087                Transition::Precedence {
12088                    target,
12089                    precedence: transition_precedence,
12090                } => {
12091                    if transition_precedence < precedence {
12092                        return Err(DirectAdaptiveParseControl::Fallback(
12093                            DirectAdaptiveFallback::Precedence,
12094                        ));
12095                    }
12096                    state_number = target;
12097                }
12098                Transition::Rule {
12099                    rule_index,
12100                    follow_state,
12101                    precedence: rule_precedence,
12102                    ..
12103                } => {
12104                    let child = self.parse_rule(
12105                        rule_index,
12106                        invoking_state_number(state_number),
12107                        rule_precedence,
12108                    )?;
12109                    if self.parser.build_parse_trees {
12110                        self.parser.tree.add_child(&mut context, child);
12111                    }
12112                    state_number = follow_state;
12113                }
12114                Transition::Atom { .. }
12115                | Transition::Range { .. }
12116                | Transition::Set { .. }
12117                | Transition::NotSet { .. }
12118                | Transition::Wildcard { .. } => {
12119                    let (matched_eof, child) = self.consume_transition(transition)?;
12120                    consumed_eof |= matched_eof;
12121                    if let Some(child) = child {
12122                        self.parser.tree.add_child(&mut context, child);
12123                    }
12124                    state_number = transition.target();
12125                }
12126                Transition::Predicate { .. } => {
12127                    return Err(DirectAdaptiveParseControl::Fallback(
12128                        DirectAdaptiveFallback::Predicate,
12129                    ));
12130                }
12131                Transition::Action { .. } => {
12132                    return Err(DirectAdaptiveParseControl::Fallback(
12133                        DirectAdaptiveFallback::Action,
12134                    ));
12135                }
12136            }
12137        }
12138
12139        let stop_index = self
12140            .parser
12141            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
12142        if let Some(token) = stop_index.and_then(|index| self.parser.token_id_at(index)) {
12143            self.parser.set_context_stop(&mut context, token);
12144        }
12145        Ok(self.parser.rule_node(context))
12146    }
12147
12148    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
12149        self.steps += 1;
12150        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
12151            return Err(DirectAdaptiveParseControl::Fallback(
12152                DirectAdaptiveFallback::StepLimit,
12153            ));
12154        }
12155        Ok(())
12156    }
12157
12158    fn next_transition(
12159        &mut self,
12160        state_number: usize,
12161        precedence: i32,
12162    ) -> DirectAdaptiveParseResult<(ParserTransition<'atn>, Option<usize>)> {
12163        let state = self
12164            .atn
12165            .state(state_number)
12166            .ok_or(DirectAdaptiveParseControl::Fallback(
12167                DirectAdaptiveFallback::MissingAtn,
12168            ))?;
12169        if state.is_rule_stop() {
12170            return Err(DirectAdaptiveParseControl::Fallback(
12171                DirectAdaptiveFallback::RuleStop,
12172            ));
12173        }
12174        let transition_index =
12175            self.transition_index(state_number, state.transitions().len(), precedence)?;
12176        let transition = state.transitions().get(transition_index).ok_or(
12177            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
12178        )?;
12179        let boundary = match &transition.data() {
12180            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
12181                left_recursive_boundary(self.atn, state, *target)
12182            }
12183            _ => None,
12184        };
12185        Ok((transition, boundary))
12186    }
12187
12188    fn transition_index(
12189        &mut self,
12190        state_number: usize,
12191        transition_count: usize,
12192        precedence: i32,
12193    ) -> DirectAdaptiveParseResult<usize> {
12194        match transition_count {
12195            0 => Err(DirectAdaptiveParseControl::Fallback(
12196                DirectAdaptiveFallback::NoTransition,
12197            )),
12198            1 => Ok(0),
12199            _ => {
12200                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
12201                    return Ok(alt);
12202                }
12203                let decision = self
12204                    .decision_by_state
12205                    .get(state_number)
12206                    .and_then(|decision| *decision)
12207                    .ok_or(DirectAdaptiveParseControl::Fallback(
12208                        DirectAdaptiveFallback::UnknownDecision,
12209                    ))?;
12210                let prediction = self
12211                    .simulator
12212                    .adaptive_predict_stream_info_with_precedence(
12213                        decision,
12214                        direct_precedence(precedence),
12215                        &mut self.parser.input,
12216                    )
12217                    .map_err(|_| {
12218                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
12219                    })?;
12220                if prediction.has_semantic_context {
12221                    return Err(DirectAdaptiveParseControl::Fallback(
12222                        DirectAdaptiveFallback::SemanticContext,
12223                    ));
12224                }
12225                prediction
12226                    .alt
12227                    .checked_sub(1)
12228                    .filter(|index| *index < transition_count)
12229                    .ok_or(DirectAdaptiveParseControl::Fallback(
12230                        DirectAdaptiveFallback::InvalidAlt,
12231                    ))
12232            }
12233        }
12234    }
12235
12236    fn ll1_transition_index(
12237        &mut self,
12238        state_number: usize,
12239        transition_count: usize,
12240    ) -> DirectAdaptiveParseResult<Option<usize>> {
12241        let state = self
12242            .atn
12243            .state(state_number)
12244            .ok_or(DirectAdaptiveParseControl::Fallback(
12245                DirectAdaptiveFallback::MissingAtn,
12246            ))?;
12247        if state.precedence_rule_decision() {
12248            return Ok(None);
12249        }
12250        let Some(rule_stop) = state
12251            .rule_index()
12252            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index))
12253        else {
12254            return Ok(None);
12255        };
12256        let symbol = self.parser.input.la_token(1);
12257        let entry = self
12258            .parser
12259            .cached_decision_lookahead(self.atn, state, rule_stop);
12260        Ok(
12261            ll1_greedy_alt(&entry, symbol, state.non_greedy())
12262                .filter(|alt| *alt < transition_count),
12263        )
12264    }
12265
12266    fn consume_transition(
12267        &mut self,
12268        transition: ParserTransition<'_>,
12269    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
12270        let symbol = self.parser.input.la_token(1);
12271        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
12272            return Err(DirectAdaptiveParseControl::Fallback(
12273                DirectAdaptiveFallback::TokenMismatch,
12274            ));
12275        }
12276        let token = self
12277            .parser
12278            .input
12279            .lt_id(1)
12280            .ok_or(DirectAdaptiveParseControl::Fallback(
12281                DirectAdaptiveFallback::TokenMismatch,
12282            ))?;
12283        let matched_eof = symbol == TOKEN_EOF;
12284        if !matched_eof {
12285            self.parser.consume();
12286        }
12287        let child = self
12288            .parser
12289            .build_parse_trees
12290            .then(|| self.parser.terminal_tree(token));
12291        Ok((matched_eof, child))
12292    }
12293}
12294
12295/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
12296/// transformed left-recursive rule.
12297fn left_recursive_boundary(atn: &Atn, state: AtnState<'_>, target: usize) -> Option<usize> {
12298    if !state.precedence_rule_decision() {
12299        return None;
12300    }
12301    let target_state = atn.state(target)?;
12302    if target_state.kind() == AtnStateKind::LoopEnd {
12303        return None;
12304    }
12305    state.rule_index()
12306}
12307
12308/// Selects the first outer alternative observed for a rule path.
12309///
12310/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
12311/// outer decision. The metadata recognizer only needs this when a generated
12312/// grammar opts into that target template; otherwise the value remains `0` and
12313/// parse-tree rendering is unchanged.
12314fn next_alt_number(
12315    state: AtnState<'_>,
12316    transition_count: usize,
12317    transition_index: usize,
12318    current_alt_number: usize,
12319    track_alt_numbers: bool,
12320) -> usize {
12321    if !track_alt_numbers || current_alt_number != 0 || transition_count <= 1 {
12322        return current_alt_number;
12323    }
12324    if matches!(
12325        state.kind(),
12326        AtnStateKind::Basic
12327            | AtnStateKind::BlockStart
12328            | AtnStateKind::PlusBlockStart
12329            | AtnStateKind::StarBlockStart
12330            | AtnStateKind::StarLoopEntry
12331    ) && !state.precedence_rule_decision()
12332    {
12333        return transition_index + 1;
12334    }
12335    current_alt_number
12336}
12337
12338/// Converts an ATN state number into the signed invoking-state slot used by
12339/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
12340fn invoking_state_number(state_number: usize) -> isize {
12341    isize::try_from(state_number).unwrap_or(isize::MAX)
12342}
12343
12344const fn packed_i32(value: u32) -> i32 {
12345    i32::from_le_bytes(value.to_le_bytes())
12346}
12347
12348fn direct_precedence(precedence: i32) -> usize {
12349    usize::try_from(precedence.max(0)).unwrap_or_default()
12350}
12351
12352fn token_input_display(token: &impl Token) -> String {
12353    format!("'{}'", token.text().unwrap_or("<EOF>"))
12354}
12355
12356fn display_input_text(text: &str) -> String {
12357    let mut out = String::new();
12358    for ch in text.chars() {
12359        match ch {
12360            '\n' => out.push_str("\\n"),
12361            '\r' => out.push_str("\\r"),
12362            '\t' => out.push_str("\\t"),
12363            other => out.push(other),
12364        }
12365    }
12366    out
12367}
12368
12369fn diagnostic_for_token<T: Token>(token: Option<T>, message: String) -> ParserDiagnostic {
12370    let (line, column, offending) = token.map_or((0, 0, None), |token| {
12371        (token.line(), token.column(), Some(token.token_id()))
12372    });
12373    ParserDiagnostic {
12374        line,
12375        column,
12376        message,
12377        offending,
12378    }
12379}
12380
12381fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
12382    expected_symbols_display_iter(symbols.iter().copied(), vocabulary)
12383}
12384
12385fn expected_symbols_display_iter(
12386    symbols: impl IntoIterator<Item = i32>,
12387    vocabulary: &Vocabulary,
12388) -> String {
12389    let items = symbols
12390        .into_iter()
12391        .map(|symbol| expected_symbol_display(symbol, vocabulary))
12392        .collect::<Vec<_>>();
12393    if let [single] = items.as_slice() {
12394        return single.clone();
12395    }
12396    format!("{{{}}}", items.join(", "))
12397}
12398
12399fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
12400    if symbol == TOKEN_EOF {
12401        return "<EOF>".to_owned();
12402    }
12403    vocabulary.display_name(symbol)
12404}
12405
12406fn caller_follow_token_info_for_stream<S: TokenSource>(
12407    input: &mut CommonTokenStream<S>,
12408    index: usize,
12409) -> (i32, bool, bool) {
12410    // Generated callers own statement separators; leave them available when
12411    // an interpreted child rule can either stop before or consume one.
12412    if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !input.is_filled() {
12413        input.fill();
12414    }
12415    let token_type = input.token_type_at_index(index);
12416    let visible_channel = input.channel();
12417    let token = input.get(index);
12418    let is_boundary = token
12419        .as_ref()
12420        .and_then(Token::text)
12421        .is_some_and(is_caller_follow_boundary_text);
12422    let is_boundary_gap = token.as_ref().is_some_and(|token| {
12423        token.channel() != visible_channel
12424            || is_caller_follow_boundary_gap_text(token.text_or_empty())
12425    });
12426    (token_type, is_boundary, is_boundary_gap)
12427}
12428
12429fn is_caller_follow_boundary_text(text: &str) -> bool {
12430    text.chars().any(|ch| ch == ';' || ch == '\n')
12431        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
12432}
12433
12434fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
12435    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
12436}
12437
12438/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
12439/// Inline insertion in those precedence loops can synthesize a missing operand
12440/// before an operator and then block the legitimate loop-exit path.
12441fn state_is_left_recursive_rule(atn: &Atn, state: AtnState<'_>) -> bool {
12442    let Some(rule_index) = state.rule_index() else {
12443        return false;
12444    };
12445    atn.rule_to_start_state()
12446        .get(rule_index)
12447        .and_then(|state_number| atn.state(state_number))
12448        .is_some_and(AtnState::left_recursive_rule)
12449}
12450
12451/// Picks the better of two `parse_atn_rule` passes (with and without the
12452/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
12453/// recovered one; among recovered outcomes the second pass is preferred
12454/// because the no-prefilter walk reaches ANTLR-style recovery inside child
12455/// rules. If both passes failed, the second pass's expected-token snapshot
12456/// is returned so the caller renders the same diagnostic ANTLR would.
12457fn select_better_top_outcome(
12458    first: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
12459    second: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
12460    arena: &RecognitionArena,
12461) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
12462    match (first, second) {
12463        (Ok(first), Ok(second)) => {
12464            if arena.diagnostics(first.0.diagnostics).next().is_none() {
12465                Ok(first)
12466            } else {
12467                Ok(second)
12468            }
12469        }
12470        (Ok(first), Err(_)) => Ok(first),
12471        (Err(_), Ok(second)) => Ok(second),
12472        (Err(_), Err(second_expected)) => Err(second_expected),
12473    }
12474}
12475
12476/// Chooses the outermost parse result that consumed the most input.
12477///
12478/// The recognizer intentionally keeps shorter endpoints available while walking
12479/// nested rule transitions so callers can satisfy following tokens such as
12480/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
12481fn select_best_fast_outcome(
12482    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
12483    prediction_mode: PredictionMode,
12484    caller_follow: Option<&TokenBitSet>,
12485    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
12486    arena: &RecognitionArena,
12487) -> Option<FastRecognizeOutcome> {
12488    let mut best = None;
12489    let mut best_caller_follow = None;
12490    for outcome in outcomes {
12491        if matches!(
12492            prediction_mode,
12493            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
12494        ) && outcome.diagnostics.is_empty()
12495            && let Some(follow) = caller_follow
12496        {
12497            let (token_type, is_boundary, _) = token_info_at(outcome.index);
12498            if is_boundary && follow.contains(token_type) {
12499                let replace =
12500                    best_caller_follow
12501                        .as_ref()
12502                        .is_none_or(|existing: &FastRecognizeOutcome| {
12503                            (outcome.index, outcome.consumed_eof)
12504                                < (existing.index, existing.consumed_eof)
12505                        });
12506                if replace {
12507                    best_caller_follow = Some(outcome);
12508                }
12509            }
12510        }
12511        let Some(existing) = best else {
12512            best = Some(outcome);
12513            continue;
12514        };
12515        let outcome_position = (outcome.index, outcome.consumed_eof);
12516        let best_position = (existing.index, existing.consumed_eof);
12517        let better = match prediction_mode {
12518            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
12519                outcome_position,
12520                outcome.diagnostics,
12521                best_position,
12522                existing.diagnostics,
12523                arena,
12524            ),
12525            PredictionMode::Sll => outcome.index > existing.index,
12526        };
12527        best = Some(if better { outcome } else { existing });
12528    }
12529    let should_use_caller_follow =
12530        best_caller_follow
12531            .as_ref()
12532            .zip(best.as_ref())
12533            .is_some_and(|(candidate, selected)| {
12534                if !selected.diagnostics.is_empty() {
12535                    return true;
12536                }
12537                candidate.index < selected.index
12538                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
12539            });
12540    if should_use_caller_follow {
12541        best_caller_follow
12542    } else {
12543        best
12544    }
12545}
12546
12547fn select_best_outcome(
12548    outcomes: impl Iterator<Item = RecognizeOutcome>,
12549    prediction_mode: PredictionMode,
12550    arena: &RecognitionArena,
12551) -> Option<RecognizeOutcome> {
12552    let outcomes = outcomes.collect::<Vec<_>>();
12553    let prefer_first_tie = outcomes
12554        .iter()
12555        .any(|outcome| arena.sequence_needs_stable_tie(outcome.nodes));
12556    outcomes.into_iter().reduce(|best, outcome| {
12557        let outcome_position = (outcome.index, outcome.consumed_eof);
12558        let best_position = (best.index, best.consumed_eof);
12559        let better = match prediction_mode {
12560            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
12561                outcome_is_better(
12562                    outcome_position,
12563                    outcome.diagnostics,
12564                    best_position,
12565                    best.diagnostics,
12566                    arena,
12567                ) || (outcome_position == best_position
12568                    && arena.diagnostics_len(outcome.diagnostics)
12569                        == arena.diagnostics_len(best.diagnostics)
12570                    && arena.diagnostics_recovery_rank(outcome.diagnostics)
12571                        == arena.diagnostics_recovery_rank(best.diagnostics)
12572                    && (outcome.decisions < best.decisions
12573                        || (!prefer_first_tie
12574                            && outcome.decisions == best.decisions
12575                            && outcome.actions > best.actions)))
12576            }
12577            PredictionMode::Sll => {
12578                outcome_position > best_position
12579                    || (outcome_position == best_position
12580                        && !prefer_first_tie
12581                        && (outcome.decisions < best.decisions
12582                            || (outcome.decisions == best.decisions
12583                                && outcome_is_better(
12584                                    outcome_position,
12585                                    outcome.diagnostics,
12586                                    best_position,
12587                                    best.diagnostics,
12588                                    arena,
12589                                ))))
12590            }
12591        };
12592        if better {
12593            return outcome;
12594        }
12595        best
12596    })
12597}
12598
12599/// Records the serialized transition order at parser decision states.
12600///
12601/// When two clean paths consume the same input, ANTLR's adaptive prediction
12602/// chooses by alternative order. Keeping this compact trace lets the metadata
12603/// recognizer distinguish greedy and non-greedy optional blocks without a full
12604/// prediction simulator.
12605fn transition_decision(
12606    atn: &Atn,
12607    state: AtnState<'_>,
12608    transition_count: usize,
12609    transition_index: usize,
12610    predicates: &[(usize, usize, ParserPredicate)],
12611) -> Option<usize> {
12612    if transition_count <= 1 || decision_reaches_unsupported_predicate(atn, state, predicates) {
12613        return None;
12614    }
12615    Some(transition_index)
12616}
12617
12618/// Reports whether a state should reset the active no-viable decision start.
12619///
12620/// Loop entry/back states are continuations of the surrounding adaptive
12621/// prediction; resetting at those states would turn LL-star failures back into
12622/// ordinary mismatches.
12623fn starts_prediction_decision(state: AtnState<'_>, transition_count: usize) -> bool {
12624    transition_count > 1
12625        && !matches!(
12626            state.kind(),
12627            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
12628        )
12629}
12630
12631/// Marks a farthest expected-token set as no-viable when multiple alternatives
12632/// failed after the active decision had already consumed input.
12633fn record_no_viable_if_ambiguous(
12634    expected: &mut ExpectedTokens,
12635    decision_start_index: Option<usize>,
12636    index: usize,
12637) {
12638    if expected.index == Some(index) && expected.symbols.len() > 1 {
12639        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
12640            expected.record_no_viable(decision_start, index);
12641        }
12642    }
12643}
12644
12645/// Records a no-viable decision caused by a failed semantic predicate before
12646/// any consuming transition can contribute an expected-token set.
12647const fn record_predicate_no_viable(
12648    expected: &mut ExpectedTokens,
12649    decision_start_index: Option<usize>,
12650    index: usize,
12651) {
12652    if let Some(decision_start) = decision_start_index {
12653        expected.record_no_viable(decision_start, index);
12654    }
12655}
12656
12657/// Returns the active decision start only when the error is past that start.
12658const fn no_viable_decision_start(
12659    decision_start_index: Option<usize>,
12660    index: usize,
12661) -> Option<usize> {
12662    match decision_start_index {
12663        Some(start) if index > start => Some(start),
12664        _ => None,
12665    }
12666}
12667
12668/// Restores expected-token bookkeeping when a child rule found a clean
12669/// consuming path; failures in longer child alternatives should not pollute the
12670/// caller's final expectation set.
12671fn restore_expected(
12672    children: &[RecognizeOutcome],
12673    child_start_index: usize,
12674    expected: &mut ExpectedTokens,
12675    snapshot: ExpectedTokens,
12676    preserve_child_expected: bool,
12677) {
12678    if preserve_child_expected {
12679        return;
12680    }
12681    if children
12682        .iter()
12683        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
12684    {
12685        *expected = snapshot;
12686    }
12687}
12688
12689/// Reports whether a decision can reach a predicate the generator did not
12690/// translate. Static alternative order is unsafe for those context predicates.
12691fn decision_reaches_unsupported_predicate(
12692    atn: &Atn,
12693    state: AtnState<'_>,
12694    predicates: &[(usize, usize, ParserPredicate)],
12695) -> bool {
12696    state.transitions().iter().any(|transition| {
12697        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
12698    })
12699}
12700
12701/// Walks epsilon-like edges from one transition to find unsupported predicates.
12702fn transition_reaches_unsupported_predicate(
12703    atn: &Atn,
12704    transition: ParserTransition<'_>,
12705    predicates: &[(usize, usize, ParserPredicate)],
12706    visited: &mut BTreeSet<usize>,
12707) -> bool {
12708    match &transition.data() {
12709        Transition::Predicate {
12710            rule_index,
12711            pred_index,
12712            ..
12713        } => !predicates
12714            .iter()
12715            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
12716        Transition::Epsilon { target }
12717        | Transition::Action { target, .. }
12718        | Transition::Rule { target, .. } => {
12719            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
12720        }
12721        Transition::Precedence { .. }
12722        | Transition::Atom { .. }
12723        | Transition::Range { .. }
12724        | Transition::Set { .. }
12725        | Transition::NotSet { .. }
12726        | Transition::Wildcard { .. } => false,
12727    }
12728}
12729
12730/// Finds an unsupported predicate reachable before a consuming transition.
12731fn state_reaches_unsupported_predicate(
12732    atn: &Atn,
12733    state_number: usize,
12734    predicates: &[(usize, usize, ParserPredicate)],
12735    visited: &mut BTreeSet<usize>,
12736) -> bool {
12737    if !visited.insert(state_number) {
12738        return false;
12739    }
12740    let Some(state) = atn.state(state_number) else {
12741        return false;
12742    };
12743    state.transitions().iter().any(|transition| {
12744        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
12745    })
12746}
12747
12748/// Adds a decision step to the front of an already-recognized suffix path.
12749fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
12750    if let Some(decision) = decision {
12751        outcome.decisions.insert(0, decision);
12752    }
12753}
12754
12755fn outcome_is_better(
12756    outcome_position: (usize, bool),
12757    outcome_diagnostics: DiagnosticSeqId,
12758    best_position: (usize, bool),
12759    best_diagnostics: DiagnosticSeqId,
12760    arena: &RecognitionArena,
12761) -> bool {
12762    let outcome_len = arena.diagnostics_len(outcome_diagnostics);
12763    let best_len = arena.diagnostics_len(best_diagnostics);
12764    outcome_position > best_position
12765        || (outcome_position == best_position
12766            && (outcome_len < best_len
12767                || (outcome_len == best_len
12768                    && arena.diagnostics_recovery_rank(outcome_diagnostics)
12769                        < arena.diagnostics_recovery_rank(best_diagnostics))))
12770}
12771
12772fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
12773    if outcomes
12774        .iter()
12775        .any(|outcome| outcome.diagnostics.is_empty())
12776    {
12777        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
12778    }
12779}
12780
12781fn discard_recovered_outcomes_if_clean_path_exists(
12782    outcomes: &mut Vec<RecognizeOutcome>,
12783    arena: &RecognitionArena,
12784) {
12785    if outcomes
12786        .iter()
12787        .any(|outcome| outcome_has_rule_failure_diagnostic(outcome, arena))
12788    {
12789        return;
12790    }
12791    if outcomes
12792        .iter()
12793        .any(|outcome| outcome.diagnostics.is_empty())
12794    {
12795        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
12796    }
12797}
12798
12799/// Reports whether a recovered outcome came from an explicit predicate
12800/// fail-option and therefore should compete with shorter clean loop exits.
12801fn outcome_has_rule_failure_diagnostic(
12802    outcome: &RecognizeOutcome,
12803    arena: &RecognitionArena,
12804) -> bool {
12805    arena
12806        .diagnostics(outcome.diagnostics)
12807        .any(|diagnostic| diagnostic.message.starts_with("rule "))
12808}
12809
12810/// Removes equivalent endpoints before memoizing a state result while
12811/// preserving ATN transition-discovery order.
12812///
12813/// Outcomes are compared on observable recognition state — the input index,
12814/// EOF consumption, and diagnostics — without descending into the parse-tree
12815/// fragment carried by `nodes`. Two paths reaching the same point with
12816/// different node trees would otherwise prevent memoization from collapsing
12817/// equivalent suffixes and explode the speculative-path cache.
12818///
12819/// The first occurrence per recognition key wins, which matches ANTLR's
12820/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
12821/// transitions before loop-exit, so the first-discovered outcome carries the
12822/// greedy parse-tree fragment.
12823fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>, arena: &RecognitionArena) {
12824    if outcomes.len() < 2 {
12825        return;
12826    }
12827    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
12828    outcomes.retain(|outcome| {
12829        seen.insert((
12830            outcome.index,
12831            outcome.consumed_eof,
12832            arena.diagnostics_len(outcome.diagnostics),
12833            arena.diagnostics_recovery_rank(outcome.diagnostics),
12834        ))
12835    });
12836}
12837
12838const FAST_OUTCOME_INLINE_KEYS: usize = 8;
12839const FAST_OUTCOME_BITS_PER_WORD: usize = 64;
12840const MAX_FAST_OUTCOME_DENSE_BYTES: usize = 64 * 1024;
12841const MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS: usize = 65_536;
12842
12843#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12844enum FastOutcomeDedupStrategy {
12845    Inline,
12846    Dense,
12847    Sparse,
12848}
12849
12850impl FastOutcomeDedupScratch {
12851    fn prepare_dense(&mut self, word_count: usize) {
12852        while let Some(word_index) = self.touched_dense_words.pop() {
12853            self.dense_words[usize::try_from(word_index).expect("u32 fits in usize")] = 0;
12854        }
12855        if self.dense_words.len() < word_count {
12856            self.dense_words.resize(word_count, 0);
12857        }
12858    }
12859}
12860
12861fn clean_fast_outcome_dense_layout(outcomes: &[FastRecognizeOutcome]) -> Option<(usize, usize)> {
12862    let first_index = outcomes.first()?.index;
12863    let (min_index, max_index) = outcomes[1..].iter().fold(
12864        (first_index, first_index),
12865        |(min_index, max_index), outcome| {
12866            (min_index.min(outcome.index), max_index.max(outcome.index))
12867        },
12868    );
12869    let index_span = max_index.checked_sub(min_index)?.checked_add(1)?;
12870    let bit_count = index_span.checked_mul(2)?;
12871    let word_count =
12872        bit_count.checked_add(FAST_OUTCOME_BITS_PER_WORD - 1)? / FAST_OUTCOME_BITS_PER_WORD;
12873    let dense_bytes = word_count.checked_mul(size_of::<u64>())?;
12874    let sparse_key_bytes = outcomes.len().checked_mul(size_of::<(usize, bool)>())?;
12875    (dense_bytes <= MAX_FAST_OUTCOME_DENSE_BYTES && dense_bytes <= sparse_key_bytes)
12876        .then_some((min_index, word_count))
12877}
12878
12879#[cfg(feature = "perf-counters")]
12880fn record_clean_fast_outcome_dedup(
12881    strategy: FastOutcomeDedupStrategy,
12882    input_len: usize,
12883    output_len: usize,
12884    dense_words: usize,
12885) {
12886    let counter = match strategy {
12887        FastOutcomeDedupStrategy::Inline => &perf_counters::OUTCOME_DEDUPE_INLINE,
12888        FastOutcomeDedupStrategy::Dense => &perf_counters::OUTCOME_DEDUPE_DENSE,
12889        FastOutcomeDedupStrategy::Sparse => &perf_counters::OUTCOME_DEDUPE_SPARSE,
12890    };
12891    perf_counters::inc(
12892        &perf_counters::OUTCOME_DEDUPE_INPUTS,
12893        u64::try_from(input_len).unwrap_or(u64::MAX),
12894    );
12895    perf_counters::inc(
12896        &perf_counters::OUTCOME_DEDUPE_REMOVED,
12897        u64::try_from(input_len - output_len).unwrap_or(u64::MAX),
12898    );
12899    perf_counters::inc(counter, 1);
12900    perf_counters::inc(
12901        &perf_counters::OUTCOME_DEDUPE_DENSE_WORDS,
12902        u64::try_from(dense_words).unwrap_or(u64::MAX),
12903    );
12904}
12905
12906/// Removes duplicate clean endpoints while preserving transition-discovery
12907/// order. Tiny lists stay on the stack; larger compact ranges use a direct
12908/// bitmap, and only wide sparse ranges pay for hashing.
12909fn dedupe_clean_fast_outcomes(
12910    outcomes: &mut Vec<FastRecognizeOutcome>,
12911    scratch: &mut FastOutcomeDedupScratch,
12912) -> FastOutcomeDedupStrategy {
12913    #[cfg(feature = "perf-counters")]
12914    let input_len = outcomes.len();
12915    if outcomes.len() <= FAST_OUTCOME_INLINE_KEYS {
12916        let mut inline_keys = [(0, false); FAST_OUTCOME_INLINE_KEYS];
12917        let mut inline_len = 0_usize;
12918        outcomes.retain(|outcome| {
12919            let key = (outcome.index, outcome.consumed_eof);
12920            if inline_keys[..inline_len].contains(&key) {
12921                return false;
12922            }
12923            inline_keys[inline_len] = key;
12924            inline_len += 1;
12925            true
12926        });
12927        #[cfg(feature = "perf-counters")]
12928        record_clean_fast_outcome_dedup(
12929            FastOutcomeDedupStrategy::Inline,
12930            input_len,
12931            outcomes.len(),
12932            0,
12933        );
12934        return FastOutcomeDedupStrategy::Inline;
12935    }
12936
12937    if let Some((base_index, word_count)) = clean_fast_outcome_dense_layout(outcomes) {
12938        scratch.prepare_dense(word_count);
12939        outcomes.retain(|outcome| {
12940            let bit_index = (outcome.index - base_index) * 2 + usize::from(outcome.consumed_eof);
12941            let word_index = bit_index / FAST_OUTCOME_BITS_PER_WORD;
12942            let bit = 1_u64 << (bit_index % FAST_OUTCOME_BITS_PER_WORD);
12943            let word = &mut scratch.dense_words[word_index];
12944            if *word & bit != 0 {
12945                return false;
12946            }
12947            if *word == 0 {
12948                scratch
12949                    .touched_dense_words
12950                    .push(u32::try_from(word_index).expect("dense outcome bitmap is capped"));
12951            }
12952            *word |= bit;
12953            true
12954        });
12955        #[cfg(feature = "perf-counters")]
12956        record_clean_fast_outcome_dedup(
12957            FastOutcomeDedupStrategy::Dense,
12958            input_len,
12959            outcomes.len(),
12960            word_count,
12961        );
12962        return FastOutcomeDedupStrategy::Dense;
12963    }
12964
12965    scratch.sparse_keys.clear();
12966    scratch.sparse_keys.reserve(outcomes.len());
12967    outcomes.retain(|outcome| {
12968        scratch
12969            .sparse_keys
12970            .insert((outcome.index, outcome.consumed_eof))
12971    });
12972    #[cfg(feature = "perf-counters")]
12973    record_clean_fast_outcome_dedup(
12974        FastOutcomeDedupStrategy::Sparse,
12975        input_len,
12976        outcomes.len(),
12977        0,
12978    );
12979    if scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS {
12980        scratch.sparse_keys = FxHashSet::default();
12981    }
12982    FastOutcomeDedupStrategy::Sparse
12983}
12984
12985/// Sorts and removes equivalent endpoints, including action traces and the
12986/// arena-backed node sequence's structural contents.
12987fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>, arena: &RecognitionArena) {
12988    outcomes.sort_unstable_by(|left, right| compare_recognize_outcomes(left, right, arena));
12989    outcomes
12990        .dedup_by(|left, right| compare_recognize_outcomes(left, right, arena) == Ordering::Equal);
12991}
12992
12993fn compare_recognize_outcomes(
12994    left: &RecognizeOutcome,
12995    right: &RecognizeOutcome,
12996    arena: &RecognitionArena,
12997) -> Ordering {
12998    left.index
12999        .cmp(&right.index)
13000        .then_with(|| left.consumed_eof.cmp(&right.consumed_eof))
13001        .then_with(|| left.alt_number.cmp(&right.alt_number))
13002        .then_with(|| left.member_values.cmp(&right.member_values))
13003        .then_with(|| left.return_values.cmp(&right.return_values))
13004        .then_with(|| arena.compare_diagnostics(left.diagnostics, right.diagnostics))
13005        .then_with(|| left.decisions.cmp(&right.decisions))
13006        .then_with(|| left.actions.cmp(&right.actions))
13007        .then_with(|| arena.compare_sequences(left.nodes, right.nodes))
13008}
13009
13010impl<S, H> Recognizer for BaseParser<S, H>
13011where
13012    S: TokenSource,
13013    H: SemanticHooks,
13014{
13015    fn data(&self) -> &RecognizerData {
13016        &self.data
13017    }
13018
13019    fn data_mut(&mut self) -> &mut RecognizerData {
13020        &mut self.data
13021    }
13022}
13023
13024impl<S, H> Parser for BaseParser<S, H>
13025where
13026    S: TokenSource,
13027    H: SemanticHooks,
13028{
13029    fn build_parse_trees(&self) -> bool {
13030        self.build_parse_trees
13031    }
13032
13033    fn set_build_parse_trees(&mut self, build: bool) {
13034        self.build_parse_trees = build;
13035    }
13036
13037    fn number_of_syntax_errors(&self) -> usize {
13038        Self::number_of_syntax_errors(self)
13039    }
13040
13041    fn report_diagnostic_errors(&self) -> bool {
13042        self.report_diagnostic_errors
13043    }
13044
13045    fn set_report_diagnostic_errors(&mut self, report: bool) {
13046        self.report_diagnostic_errors = report;
13047    }
13048
13049    fn prediction_mode(&self) -> PredictionMode {
13050        self.prediction_mode
13051    }
13052
13053    fn set_prediction_mode(&mut self, mode: PredictionMode) {
13054        self.prediction_mode = mode;
13055    }
13056
13057    fn max_rule_depth(&self) -> Option<usize> {
13058        self.max_rule_depth
13059    }
13060
13061    fn set_max_rule_depth(&mut self, depth: Option<usize>) {
13062        self.max_rule_depth = depth;
13063    }
13064
13065    fn add_parse_listener(&mut self, listener: Box<dyn ParseListener>) {
13066        self.parse_listeners.push(ParseListenerSlot(listener));
13067    }
13068
13069    fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
13070        Self::remove_parse_listeners(self)
13071    }
13072}
13073
13074#[cfg(test)]
13075#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
13076mod tests {
13077    use super::*;
13078    use crate::atn::parser::{
13079        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
13080    };
13081    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
13082    use crate::token::{
13083        HIDDEN_CHANNEL, Token, TokenId, TokenSink, TokenSpec, TokenStoreError, TokenView,
13084    };
13085    use crate::token_stream::CommonTokenStream;
13086    use crate::tree::{NodeKind, ParseTreeStats};
13087    use crate::vocabulary::Vocabulary;
13088    use std::cell::RefCell;
13089    use std::mem::size_of;
13090    use std::rc::Rc;
13091    use std::sync::{Arc, Mutex};
13092
13093    #[test]
13094    fn fx_hasher_write_matches_typed_methods_for_full_words() {
13095        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
13096        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
13097        // fields) must hash the same as the typed methods, otherwise an
13098        // `FxHashMap` keyed on such a type silently disagrees with itself
13099        // depending on which entry point the caller used. Verify the
13100        // little-endian word equivalence this PR established.
13101        let value: u64 = 0x0102_0304_0506_0708;
13102        let mut typed = FxHasher::default();
13103        typed.write_u64(value);
13104        let mut bytewise = FxHasher::default();
13105        bytewise.write(&value.to_le_bytes());
13106        assert_eq!(typed.finish(), bytewise.finish());
13107    }
13108
13109    #[derive(Clone, Debug)]
13110    struct TestToken {
13111        spec: TokenSpec,
13112        id: TokenId,
13113        source_name: String,
13114    }
13115
13116    impl TestToken {
13117        fn new(token_type: i32) -> Self {
13118            Self {
13119                spec: TokenSpec::explicit(token_type, ""),
13120                id: TokenId::try_from(0).expect("zero token ID"),
13121                source_name: String::new(),
13122            }
13123        }
13124
13125        fn eof(source_name: &str, index: usize, line: usize, column: usize) -> Self {
13126            Self {
13127                spec: TokenSpec::eof(index, index, line, column),
13128                id: TokenId::try_from(0).expect("zero token ID"),
13129                source_name: source_name.to_owned(),
13130            }
13131        }
13132
13133        fn with_text(mut self, text: impl Into<String>) -> Self {
13134            self.spec.text = Some(text.into());
13135            self
13136        }
13137
13138        const fn with_channel(mut self, channel: i32) -> Self {
13139            self.spec.channel = channel;
13140            self
13141        }
13142
13143        const fn with_span(mut self, start: usize, stop: usize) -> Self {
13144            self.spec.start = start;
13145            self.spec.stop = stop;
13146            self.spec.start_byte = start;
13147            self.spec.stop_byte = match stop.checked_add(1) {
13148                Some(end) if end >= start => end,
13149                Some(_) | None => start,
13150            };
13151            self
13152        }
13153
13154        const fn with_position(mut self, line: usize, column: usize) -> Self {
13155            self.spec.line = line;
13156            self.spec.column = column;
13157            self
13158        }
13159
13160        fn set_token_index(&mut self, index: isize) {
13161            self.id = TokenId::try_from(index.max(0).cast_unsigned()).expect("test token index");
13162        }
13163    }
13164
13165    impl Token for TestToken {
13166        fn token_id(&self) -> TokenId {
13167            self.id
13168        }
13169
13170        fn token_type(&self) -> i32 {
13171            self.spec.token_type
13172        }
13173
13174        fn channel(&self) -> i32 {
13175            self.spec.channel
13176        }
13177
13178        fn start(&self) -> usize {
13179            self.spec.start
13180        }
13181
13182        fn stop(&self) -> usize {
13183            self.spec.stop
13184        }
13185
13186        fn line(&self) -> usize {
13187            self.spec.line
13188        }
13189
13190        fn column(&self) -> usize {
13191            self.spec.column
13192        }
13193
13194        fn text(&self) -> Option<&str> {
13195            self.spec.text.as_deref()
13196        }
13197
13198        fn source_name(&self) -> &str {
13199            &self.source_name
13200        }
13201
13202        fn start_byte(&self) -> usize {
13203            self.spec.start_byte
13204        }
13205
13206        fn stop_byte(&self) -> usize {
13207            self.spec.stop_byte
13208        }
13209    }
13210
13211    #[derive(Debug)]
13212    struct Source {
13213        tokens: Vec<TestToken>,
13214        index: usize,
13215    }
13216
13217    impl TokenSource for Source {
13218        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
13219            let token = self
13220                .tokens
13221                .get(self.index)
13222                .cloned()
13223                .unwrap_or_else(|| TestToken::eof("parser-test", self.index, 1, self.index));
13224            self.index += 1;
13225            sink.push(token.spec)
13226        }
13227
13228        fn line(&self) -> usize {
13229            1
13230        }
13231
13232        fn column(&self) -> usize {
13233            self.index
13234        }
13235
13236        fn source_name(&self) -> &'static str {
13237            "parser-test"
13238        }
13239    }
13240
13241    #[derive(Clone, Debug, Eq, PartialEq)]
13242    struct RecordedDiagnostic {
13243        grammar_file_name: String,
13244        offending_text: Option<String>,
13245        line: usize,
13246        column: usize,
13247        message: String,
13248        error: Option<AntlrError>,
13249    }
13250
13251    #[derive(Clone, Debug)]
13252    struct RecordingErrorListener {
13253        diagnostics: Arc<Mutex<Vec<RecordedDiagnostic>>>,
13254    }
13255
13256    impl<R> crate::ErrorListener<R> for RecordingErrorListener
13257    where
13258        R: Recognizer + ?Sized,
13259    {
13260        fn syntax_error(
13261            &mut self,
13262            recognizer: &R,
13263            offending: Option<TokenView<'_>>,
13264            line: usize,
13265            column: usize,
13266            message: &str,
13267            error: Option<&AntlrError>,
13268        ) {
13269            self.diagnostics
13270                .lock()
13271                .expect("recorded diagnostics lock")
13272                .push(RecordedDiagnostic {
13273                    grammar_file_name: recognizer.grammar_file_name().to_owned(),
13274                    offending_text: offending.and_then(|token| token.text().map(str::to_owned)),
13275                    line,
13276                    column,
13277                    message: message.to_owned(),
13278                    error: error.cloned(),
13279                });
13280        }
13281    }
13282
13283    #[derive(Debug)]
13284    struct ReportingSource {
13285        source: Source,
13286        diagnostics: Rc<RefCell<Vec<TokenSourceError>>>,
13287    }
13288
13289    impl TokenSource for ReportingSource {
13290        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
13291            self.source.next_token(sink)
13292        }
13293
13294        fn line(&self) -> usize {
13295            self.source.line()
13296        }
13297
13298        fn column(&self) -> usize {
13299            self.source.column()
13300        }
13301
13302        fn source_name(&self) -> &str {
13303            self.source.source_name()
13304        }
13305
13306        fn report_error(&self, error: &TokenSourceError) -> bool {
13307            self.diagnostics.borrow_mut().push(error.clone());
13308            true
13309        }
13310    }
13311
13312    fn mini_parser_data() -> RecognizerData {
13313        RecognizerData::new(
13314            "Mini.g4",
13315            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
13316        )
13317        .with_rule_names(["s"])
13318    }
13319
13320    fn mini_parser(tokens: Vec<TestToken>) -> BaseParser<Source> {
13321        let data = mini_parser_data();
13322        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
13323    }
13324
13325    fn mini_parser_with_hooks<H>(tokens: Vec<TestToken>, hooks: H) -> BaseParser<Source, H>
13326    where
13327        H: SemanticHooks,
13328    {
13329        BaseParser::with_semantic_hooks(
13330            CommonTokenStream::new(Source { tokens, index: 0 }),
13331            mini_parser_data(),
13332            hooks,
13333        )
13334    }
13335
13336    #[test]
13337    fn parser_dispatches_recovery_diagnostics_through_registered_listeners() {
13338        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
13339        parser.remove_error_listeners();
13340        let diagnostics = Arc::new(Mutex::new(Vec::new()));
13341        parser.add_error_listener(RecordingErrorListener {
13342            diagnostics: Arc::clone(&diagnostics),
13343        });
13344        let parser_diagnostics = [ParserDiagnostic {
13345            line: 1,
13346            column: 2,
13347            message: "missing 'x' at 'y'".to_owned(),
13348            offending: None,
13349        }];
13350        let token_errors = [
13351            TokenSourceError::new(1, 1, "token recognition error at: '@'"),
13352            TokenSourceError::new(1, 3, "token recognition error at: '#'"),
13353        ];
13354
13355        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
13356
13357        // The interleaved token/parser diagnostic stream (ordering, columns, messages) is one
13358        // reviewable snapshot instead of three hand-written RecordedDiagnostic literals.
13359        insta::assert_debug_snapshot!(
13360            "parser_dispatches_recovery_diagnostics_through_registered_listeners",
13361            *diagnostics.lock().expect("recorded diagnostics lock")
13362        );
13363
13364        parser.remove_error_listeners();
13365        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
13366        assert_eq!(
13367            diagnostics.lock().expect("recorded diagnostics lock").len(),
13368            3
13369        );
13370    }
13371
13372    #[test]
13373    fn recovery_diagnostics_expose_the_offending_token_to_listeners() {
13374        let mut parser = mini_parser(vec![
13375            TestToken::new(7)
13376                .with_text("oops")
13377                .with_span(0, 3)
13378                .with_position(1, 2),
13379            TestToken::eof("parser-test", 4, 1, 6),
13380        ]);
13381        parser.remove_error_listeners();
13382        let diagnostics = Arc::new(Mutex::new(Vec::new()));
13383        parser.add_error_listener(RecordingErrorListener {
13384            diagnostics: Arc::clone(&diagnostics),
13385        });
13386        let offending = parser.input.lt_id(1);
13387        assert!(offending.is_some(), "current token should be buffered");
13388        let parser_diagnostics = [ParserDiagnostic {
13389            line: 1,
13390            column: 2,
13391            message: "extraneous input 'oops'".to_owned(),
13392            offending,
13393        }];
13394
13395        parser.dispatch_generated_diagnostics(&parser_diagnostics, &[]);
13396
13397        // Listeners receive a resolvable view of the offending token — the
13398        // ANTLR offendingSymbol contract downstream span-building error
13399        // reporters (miette-style byte-offset underlines) rely on.
13400        let recorded = diagnostics
13401            .lock()
13402            .expect("recorded diagnostics lock")
13403            .clone();
13404        insta::assert_debug_snapshot!(
13405            "recovery_diagnostics_expose_the_offending_token_to_listeners",
13406            recorded
13407        );
13408    }
13409
13410    #[test]
13411    fn parser_leaves_token_errors_to_source_owned_listeners() {
13412        let source_diagnostics = Rc::new(RefCell::new(Vec::new()));
13413        let source = ReportingSource {
13414            source: Source {
13415                tokens: vec![TestToken::eof("parser-test", 0, 1, 0)],
13416                index: 0,
13417            },
13418            diagnostics: Rc::clone(&source_diagnostics),
13419        };
13420        let mut parser = BaseParser::new(CommonTokenStream::new(source), mini_parser_data());
13421        parser.remove_error_listeners();
13422        let parser_diagnostics = Arc::new(Mutex::new(Vec::new()));
13423        parser.add_error_listener(RecordingErrorListener {
13424            diagnostics: Arc::clone(&parser_diagnostics),
13425        });
13426        let source_error = TokenSourceError::new(2, 4, "token recognition error at: '$'");
13427
13428        parser.dispatch_token_source_errors(std::slice::from_ref(&source_error));
13429
13430        assert_eq!(*source_diagnostics.borrow(), [source_error]);
13431        assert!(
13432            parser_diagnostics
13433                .lock()
13434                .expect("recorded diagnostics lock")
13435                .is_empty()
13436        );
13437    }
13438
13439    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
13440        builder.finish().expect("valid packed parser ATN")
13441    }
13442
13443    fn nested_rule_chain_atn(depth: usize) -> Atn {
13444        nested_rule_graph_atn(depth, false, false)
13445    }
13446
13447    fn nested_rule_graph_atn(depth: usize, branching: bool, consuming_follows: bool) -> Atn {
13448        assert!(depth > 0);
13449        let mut atn = ParserAtnBuilder::new(2);
13450        let mut starts = Vec::with_capacity(depth);
13451        let mut stops = Vec::with_capacity(depth);
13452        let mut follows = Vec::with_capacity(depth.saturating_sub(1));
13453        for rule_index in 0..depth {
13454            starts.push(
13455                atn.add_state(AtnStateKind::RuleStart, Some(rule_index))
13456                    .expect("rule start")
13457                    .index(),
13458            );
13459        }
13460        for rule_index in 0..depth {
13461            stops.push(
13462                atn.add_state(AtnStateKind::RuleStop, Some(rule_index))
13463                    .expect("rule stop")
13464                    .index(),
13465            );
13466        }
13467        if consuming_follows {
13468            for rule_index in 0..depth - 1 {
13469                follows.push(
13470                    atn.add_state(AtnStateKind::Basic, Some(rule_index))
13471                        .expect("rule follow")
13472                        .index(),
13473                );
13474            }
13475        }
13476        atn.set_rule_to_start_state(starts.clone())
13477            .expect("rule start states");
13478        atn.set_rule_to_stop_state(stops.clone())
13479            .expect("rule stop states");
13480        for rule_index in 0..depth - 1 {
13481            let follow_state = if consuming_follows {
13482                follows[rule_index]
13483            } else {
13484                stops[rule_index]
13485            };
13486            atn.add_transition(
13487                starts[rule_index],
13488                ParserTransitionSpec::Rule {
13489                    target: starts[rule_index + 1],
13490                    rule_index: rule_index + 1,
13491                    follow_state,
13492                    precedence: 0,
13493                },
13494            )
13495            .expect("nested rule transition");
13496            if branching {
13497                atn.add_transition(
13498                    starts[rule_index],
13499                    ParserTransitionSpec::Atom {
13500                        target: stops[rule_index],
13501                        label: 2,
13502                    },
13503                )
13504                .expect("dead branch transition");
13505            }
13506            if consuming_follows {
13507                atn.add_transition(
13508                    follow_state,
13509                    ParserTransitionSpec::Atom {
13510                        target: stops[rule_index],
13511                        label: 1,
13512                    },
13513                )
13514                .expect("consuming follow transition");
13515            }
13516        }
13517        let token_set = atn.add_interval_set([(1, 1)]).expect("token set");
13518        atn.add_transition(
13519            starts[depth - 1],
13520            ParserTransitionSpec::Set {
13521                target: stops[depth - 1],
13522                set: token_set,
13523            },
13524        )
13525        .expect("terminal set transition");
13526        if branching {
13527            atn.add_transition(
13528                starts[depth - 1],
13529                ParserTransitionSpec::Atom {
13530                    target: stops[depth - 1],
13531                    label: 2,
13532                },
13533            )
13534            .expect("dead leaf branch transition");
13535        }
13536        finish_atn(atn)
13537    }
13538
13539    fn ordinary_star_loop_atn() -> Atn {
13540        let mut atn = ParserAtnBuilder::new(2);
13541        for (state_number, kind, rule_index) in [
13542            (0, AtnStateKind::RuleStart, 0),
13543            (1, AtnStateKind::StarLoopEntry, 0),
13544            (2, AtnStateKind::Basic, 0),
13545            (3, AtnStateKind::StarLoopBack, 0),
13546            (4, AtnStateKind::LoopEnd, 0),
13547            (5, AtnStateKind::Basic, 0),
13548            (6, AtnStateKind::RuleStop, 0),
13549            (7, AtnStateKind::RuleStart, 1),
13550            (8, AtnStateKind::Basic, 1),
13551            (9, AtnStateKind::RuleStop, 1),
13552        ] {
13553            assert_eq!(
13554                atn.add_state(kind, Some(rule_index))
13555                    .expect("state")
13556                    .index(),
13557                state_number
13558            );
13559        }
13560        atn.set_rule_to_start_state(vec![0, 7])
13561            .expect("rule start states");
13562        atn.set_rule_to_stop_state(vec![6, 9])
13563            .expect("rule stop states");
13564        atn.add_decision_state(1).expect("decision state");
13565        atn.set_loop_back_state(4, 3).expect("loop back state");
13566        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13567            .expect("transition");
13568        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13569            .expect("transition");
13570        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
13571            .expect("transition");
13572        atn.add_transition(
13573            2,
13574            ParserTransitionSpec::Rule {
13575                target: 7,
13576                rule_index: 1,
13577                follow_state: 3,
13578                precedence: 0,
13579            },
13580        )
13581        .expect("transition");
13582        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
13583            .expect("transition");
13584        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13585            .expect("transition");
13586        atn.add_transition(
13587            5,
13588            ParserTransitionSpec::Atom {
13589                target: 6,
13590                label: TOKEN_EOF,
13591            },
13592        )
13593        .expect("transition");
13594        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
13595            .expect("transition");
13596        atn.add_transition(
13597            8,
13598            ParserTransitionSpec::Atom {
13599                target: 9,
13600                label: 1,
13601            },
13602        )
13603        .expect("transition");
13604        finish_atn(atn)
13605    }
13606
13607    /// ATN for `s : (X | X X)* EOF`.
13608    fn ambiguous_ordinary_star_loop_atn() -> Atn {
13609        let mut atn = ParserAtnBuilder::new(1);
13610        for (state_number, kind) in [
13611            (0, AtnStateKind::RuleStart),
13612            (1, AtnStateKind::StarLoopEntry),
13613            (2, AtnStateKind::StarBlockStart),
13614            (3, AtnStateKind::Basic),
13615            (4, AtnStateKind::BlockEnd),
13616            (5, AtnStateKind::StarLoopBack),
13617            (6, AtnStateKind::LoopEnd),
13618            (7, AtnStateKind::Basic),
13619            (8, AtnStateKind::RuleStop),
13620        ] {
13621            assert_eq!(
13622                atn.add_state(kind, Some(0)).expect("state").index(),
13623                state_number
13624            );
13625        }
13626        atn.set_rule_to_start_state(vec![0])
13627            .expect("rule start states");
13628        atn.set_rule_to_stop_state(vec![8])
13629            .expect("rule stop states");
13630        atn.set_end_state(2, 4).expect("block end state");
13631        atn.set_loop_back_state(6, 5).expect("loop back state");
13632        atn.add_decision_state(1).expect("decision state");
13633        atn.add_decision_state(2).expect("decision state");
13634        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13635            .expect("transition");
13636        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13637            .expect("transition");
13638        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
13639            .expect("transition");
13640        atn.add_transition(
13641            2,
13642            ParserTransitionSpec::Atom {
13643                target: 4,
13644                label: 1,
13645            },
13646        )
13647        .expect("transition");
13648        atn.add_transition(
13649            2,
13650            ParserTransitionSpec::Atom {
13651                target: 3,
13652                label: 1,
13653            },
13654        )
13655        .expect("transition");
13656        atn.add_transition(
13657            3,
13658            ParserTransitionSpec::Atom {
13659                target: 4,
13660                label: 1,
13661            },
13662        )
13663        .expect("transition");
13664        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13665            .expect("transition");
13666        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
13667            .expect("transition");
13668        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13669            .expect("transition");
13670        atn.add_transition(
13671            7,
13672            ParserTransitionSpec::Atom {
13673                target: 8,
13674                label: TOKEN_EOF,
13675            },
13676        )
13677        .expect("transition");
13678        finish_atn(atn)
13679    }
13680
13681    fn ordinary_plus_loop_atn() -> Atn {
13682        let mut atn = ParserAtnBuilder::new(2);
13683        for (state_number, kind, rule_index) in [
13684            (0, AtnStateKind::RuleStart, 0),
13685            (1, AtnStateKind::Basic, 0),
13686            (2, AtnStateKind::PlusLoopBack, 0),
13687            (3, AtnStateKind::LoopEnd, 0),
13688            (4, AtnStateKind::Basic, 0),
13689            (5, AtnStateKind::RuleStop, 0),
13690            (6, AtnStateKind::RuleStart, 1),
13691            (7, AtnStateKind::Basic, 1),
13692            (8, AtnStateKind::RuleStop, 1),
13693        ] {
13694            assert_eq!(
13695                atn.add_state(kind, Some(rule_index))
13696                    .expect("state")
13697                    .index(),
13698                state_number
13699            );
13700        }
13701        atn.set_rule_to_start_state(vec![0, 6])
13702            .expect("rule start states");
13703        atn.set_rule_to_stop_state(vec![5, 8])
13704            .expect("rule stop states");
13705        atn.add_decision_state(2).expect("decision state");
13706        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
13707            .expect("transition");
13708        atn.add_transition(
13709            1,
13710            ParserTransitionSpec::Rule {
13711                target: 6,
13712                rule_index: 1,
13713                follow_state: 2,
13714                precedence: 0,
13715            },
13716        )
13717        .expect("transition");
13718        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
13719            .expect("transition");
13720        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13721            .expect("transition");
13722        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
13723            .expect("transition");
13724        atn.add_transition(
13725            4,
13726            ParserTransitionSpec::Atom {
13727                target: 5,
13728                label: TOKEN_EOF,
13729            },
13730        )
13731        .expect("transition");
13732        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
13733            .expect("transition");
13734        atn.add_transition(
13735            7,
13736            ParserTransitionSpec::Atom {
13737                target: 8,
13738                label: 1,
13739            },
13740        )
13741        .expect("transition");
13742        finish_atn(atn)
13743    }
13744
13745    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
13746        let mut tokens = (0..count)
13747            .map(|_| TestToken::new(1).with_text("x"))
13748            .collect::<Vec<_>>();
13749        tokens.push(TestToken::eof("parser-test", count, 1, count));
13750        tokens
13751    }
13752
13753    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
13754        let mut atn = ParserAtnBuilder::new(2);
13755        assert_eq!(
13756            atn.add_state(AtnStateKind::RuleStart, Some(0))
13757                .expect("state")
13758                .index(),
13759            0
13760        );
13761        assert_eq!(
13762            atn.add_state(AtnStateKind::Basic, Some(0))
13763                .expect("state")
13764                .index(),
13765            1
13766        );
13767        assert_eq!(
13768            atn.add_state(AtnStateKind::Basic, Some(0))
13769                .expect("state")
13770                .index(),
13771            2
13772        );
13773        assert_eq!(
13774            atn.add_state(AtnStateKind::RuleStart, Some(1))
13775                .expect("state")
13776                .index(),
13777            3
13778        );
13779        atn.set_left_recursive_rule(3)
13780            .expect("left-recursive rule start");
13781        assert_eq!(
13782            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
13783                .expect("state")
13784                .index(),
13785            4
13786        );
13787        atn.set_precedence_rule_decision(4)
13788            .expect("precedence decision");
13789        assert_eq!(
13790            atn.add_state(AtnStateKind::Basic, Some(1))
13791                .expect("state")
13792                .index(),
13793            5
13794        );
13795        assert_eq!(
13796            atn.add_state(AtnStateKind::Basic, Some(1))
13797                .expect("state")
13798                .index(),
13799            6
13800        );
13801        assert_eq!(
13802            atn.add_state(AtnStateKind::LoopEnd, Some(1))
13803                .expect("state")
13804                .index(),
13805            7
13806        );
13807        assert_eq!(
13808            atn.add_state(AtnStateKind::RuleStop, Some(1))
13809                .expect("state")
13810                .index(),
13811            8
13812        );
13813        assert_eq!(
13814            atn.add_state(AtnStateKind::RuleStop, Some(0))
13815                .expect("state")
13816                .index(),
13817            9
13818        );
13819        atn.set_rule_to_start_state(vec![0, 3])
13820            .expect("rule start states");
13821        atn.set_rule_to_stop_state(vec![9, 8])
13822            .expect("rule stop states");
13823        atn.add_transition(
13824            1,
13825            ParserTransitionSpec::Rule {
13826                target: 3,
13827                rule_index: 1,
13828                follow_state: 2,
13829                precedence: 0,
13830            },
13831        )
13832        .expect("transition");
13833        atn.add_transition(
13834            2,
13835            ParserTransitionSpec::Atom {
13836                target: 9,
13837                label: caller_symbol,
13838            },
13839        )
13840        .expect("transition");
13841        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
13842            .expect("transition");
13843        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
13844            .expect("transition");
13845        atn.add_transition(
13846            5,
13847            ParserTransitionSpec::Precedence {
13848                target: 6,
13849                precedence: 1,
13850            },
13851        )
13852        .expect("transition");
13853        atn.add_transition(
13854            6,
13855            ParserTransitionSpec::Atom {
13856                target: 4,
13857                label: 1,
13858            },
13859        )
13860        .expect("transition");
13861        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
13862            .expect("transition");
13863        finish_atn(atn)
13864    }
13865
13866    fn labeled_left_recursive_operator_atn() -> Atn {
13867        let mut atn = ParserAtnBuilder::new(4);
13868        for (state, kind) in [
13869            (0, AtnStateKind::RuleStart),
13870            (1, AtnStateKind::BlockStart),
13871            (2, AtnStateKind::StarLoopEntry),
13872            (3, AtnStateKind::StarBlockStart),
13873            (4, AtnStateKind::Basic),
13874            (5, AtnStateKind::Basic),
13875            (6, AtnStateKind::Basic),
13876            (7, AtnStateKind::StarLoopBack),
13877            (8, AtnStateKind::LoopEnd),
13878            (9, AtnStateKind::RuleStop),
13879        ] {
13880            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
13881        }
13882        atn.set_left_recursive_rule(0)
13883            .expect("left-recursive rule start");
13884        atn.set_precedence_rule_decision(2)
13885            .expect("precedence decision");
13886        atn.set_loop_back_state(8, 7).expect("loop-back state");
13887        atn.set_rule_to_start_state(vec![0])
13888            .expect("rule start states");
13889        atn.set_rule_to_stop_state(vec![9])
13890            .expect("rule stop states");
13891        for state in [1, 2, 3] {
13892            atn.add_decision_state(state).expect("decision state");
13893        }
13894        for (source, target) in [(0, 1), (2, 3), (2, 8), (7, 2), (8, 9)] {
13895            atn.add_transition(source, ParserTransitionSpec::Epsilon { target })
13896                .expect("epsilon transition");
13897        }
13898        for (source, target, label) in [(1, 2, 1), (1, 2, 2), (4, 6, 4), (5, 6, 3), (6, 7, 1)] {
13899            atn.add_transition(source, ParserTransitionSpec::Atom { target, label })
13900                .expect("token transition");
13901        }
13902        for (target, precedence) in [(4, 2), (5, 1)] {
13903            atn.add_transition(3, ParserTransitionSpec::Precedence { target, precedence })
13904                .expect("operator precedence");
13905        }
13906        finish_atn(atn)
13907    }
13908
13909    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
13910        let mut parser = mini_parser(vec![
13911            TestToken::new(symbol).with_text("lookahead"),
13912            TestToken::eof("parser-test", 1, 1, 1),
13913        ]);
13914        parser.rule_context_stack = vec![
13915            RuleContextFrame {
13916                rule_index: 0,
13917                invoking_state: -1,
13918            },
13919            RuleContextFrame {
13920                rule_index: 1,
13921                invoking_state: 1,
13922            },
13923        ];
13924        parser
13925    }
13926
13927    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
13928        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
13929        //   prec 2: token 1, token 1  (shift `>>`)
13930        //   prec 1: token 1           (relational `>`)
13931        let mut atn = ParserAtnBuilder::new(1);
13932        for (state, kind, rule) in [
13933            (0, AtnStateKind::RuleStart, 0),
13934            (1, AtnStateKind::StarLoopEntry, 0),
13935            (2, AtnStateKind::Basic, 0), // ops hub
13936            (3, AtnStateKind::Basic, 0), // shift prec
13937            (4, AtnStateKind::Basic, 0), // shift first >
13938            (5, AtnStateKind::Basic, 0), // shift second >
13939            (6, AtnStateKind::Basic, 0), // rel prec
13940            (7, AtnStateKind::Basic, 0), // rel >
13941            (8, AtnStateKind::LoopEnd, 0),
13942            (9, AtnStateKind::RuleStop, 0),
13943        ] {
13944            assert_eq!(
13945                atn.add_state(kind, Some(rule)).expect("state").index(),
13946                state
13947            );
13948            if state == 0 {
13949                atn.set_left_recursive_rule(state)
13950                    .expect("left-recursive rule start");
13951            } else if state == 1 {
13952                atn.set_precedence_rule_decision(state)
13953                    .expect("precedence decision");
13954            }
13955        }
13956        atn.set_rule_to_start_state(vec![0])
13957            .expect("rule start states");
13958        atn.set_rule_to_stop_state(vec![9])
13959            .expect("rule stop states");
13960        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
13961            .expect("ops");
13962        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
13963            .expect("exit");
13964        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
13965            .expect("to shift");
13966        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
13967            .expect("to rel");
13968        atn.add_transition(
13969            3,
13970            ParserTransitionSpec::Precedence {
13971                target: 4,
13972                precedence: 2,
13973            },
13974        )
13975        .expect("shift prec");
13976        atn.add_transition(
13977            4,
13978            ParserTransitionSpec::Atom {
13979                target: 5,
13980                label: 1,
13981            },
13982        )
13983        .expect("shift first >");
13984        atn.add_transition(
13985            5,
13986            ParserTransitionSpec::Atom {
13987                target: 1,
13988                label: 1,
13989            },
13990        )
13991        .expect("shift second >");
13992        atn.add_transition(
13993            6,
13994            ParserTransitionSpec::Precedence {
13995                target: 7,
13996                precedence: 1,
13997            },
13998        )
13999        .expect("rel prec");
14000        atn.add_transition(
14001            7,
14002            ParserTransitionSpec::Atom {
14003                target: 1,
14004                label: 1,
14005            },
14006        )
14007        .expect("rel >");
14008        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14009            .expect("loop end");
14010        finish_atn(atn)
14011    }
14012
14013    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
14014        let mut atn = ParserAtnBuilder::new(2);
14015        for (state, kind, rule) in [
14016            (0, AtnStateKind::RuleStart, 0),
14017            (1, AtnStateKind::StarLoopEntry, 0),
14018            (2, AtnStateKind::Basic, 0),
14019            (3, AtnStateKind::Basic, 0),
14020            (4, AtnStateKind::Basic, 0),
14021            (5, AtnStateKind::Basic, 0),
14022            (6, AtnStateKind::Basic, 0),
14023            (7, AtnStateKind::Basic, 0),
14024            (8, AtnStateKind::LoopEnd, 0),
14025            (9, AtnStateKind::RuleStop, 0),
14026            (10, AtnStateKind::RuleStart, 1),
14027            (11, AtnStateKind::Basic, 1),
14028            (12, AtnStateKind::RuleStop, 1),
14029        ] {
14030            assert_eq!(
14031                atn.add_state(kind, Some(rule)).expect("state").index(),
14032                state
14033            );
14034            if state == 0 {
14035                atn.set_left_recursive_rule(state)
14036                    .expect("left-recursive rule start");
14037            } else if state == 1 {
14038                atn.set_precedence_rule_decision(state)
14039                    .expect("precedence decision");
14040            }
14041        }
14042        atn.set_rule_to_start_state(vec![0, 10])
14043            .expect("rule start states");
14044        atn.set_rule_to_stop_state(vec![9, 12])
14045            .expect("rule stop states");
14046        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14047            .expect("ops");
14048        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
14049            .expect("exit");
14050        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
14051            .expect("to shift");
14052        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
14053            .expect("to relational");
14054        atn.add_transition(
14055            3,
14056            ParserTransitionSpec::Precedence {
14057                target: 4,
14058                precedence: 2,
14059            },
14060        )
14061        .expect("shift precedence");
14062        atn.add_transition(
14063            4,
14064            ParserTransitionSpec::Rule {
14065                target: 10,
14066                rule_index: 1,
14067                follow_state: 5,
14068                precedence: 0,
14069            },
14070        )
14071        .expect("first shift token helper");
14072        atn.add_transition(
14073            5,
14074            ParserTransitionSpec::Atom {
14075                target: 1,
14076                label: 1,
14077            },
14078        )
14079        .expect("second shift token");
14080        atn.add_transition(
14081            6,
14082            ParserTransitionSpec::Precedence {
14083                target: 7,
14084                precedence: 1,
14085            },
14086        )
14087        .expect("relational precedence");
14088        atn.add_transition(
14089            7,
14090            ParserTransitionSpec::Atom {
14091                target: 1,
14092                label: 1,
14093            },
14094        )
14095        .expect("relational token");
14096        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
14097            .expect("loop end");
14098        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
14099            .expect("helper entry");
14100        atn.add_transition(
14101            11,
14102            ParserTransitionSpec::Atom {
14103                target: 12,
14104                label: 1,
14105            },
14106        )
14107        .expect("first shift token");
14108        finish_atn(atn)
14109    }
14110
14111    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
14112        let mut atn = ParserAtnBuilder::new(1);
14113        for (state, kind) in [
14114            (0, AtnStateKind::RuleStart),
14115            (1, AtnStateKind::StarLoopEntry),
14116            (2, AtnStateKind::Basic),
14117            (3, AtnStateKind::Basic),
14118            (4, AtnStateKind::Basic),
14119            (5, AtnStateKind::Basic),
14120            (6, AtnStateKind::Basic),
14121            (7, AtnStateKind::Basic),
14122            (8, AtnStateKind::Basic),
14123            (9, AtnStateKind::LoopEnd),
14124            (10, AtnStateKind::RuleStop),
14125        ] {
14126            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
14127            if state == 0 {
14128                atn.set_left_recursive_rule(state)
14129                    .expect("left-recursive rule start");
14130            } else if state == 1 {
14131                atn.set_precedence_rule_decision(state)
14132                    .expect("precedence decision");
14133            }
14134        }
14135        atn.set_rule_to_start_state(vec![0])
14136            .expect("rule start states");
14137        atn.set_rule_to_stop_state(vec![10])
14138            .expect("rule stop states");
14139        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14140            .expect("ops");
14141        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
14142            .expect("exit");
14143        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
14144            .expect("to multi-token operator");
14145        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
14146            .expect("to predicate operator");
14147        atn.add_transition(
14148            3,
14149            ParserTransitionSpec::Precedence {
14150                target: 4,
14151                precedence: 2,
14152            },
14153        )
14154        .expect("multi-token precedence");
14155        atn.add_transition(
14156            4,
14157            ParserTransitionSpec::Atom {
14158                target: 5,
14159                label: 1,
14160            },
14161        )
14162        .expect("multi-token first");
14163        atn.add_transition(
14164            5,
14165            ParserTransitionSpec::Atom {
14166                target: 1,
14167                label: 1,
14168            },
14169        )
14170        .expect("multi-token second");
14171        atn.add_transition(
14172            6,
14173            ParserTransitionSpec::Precedence {
14174                target: 7,
14175                precedence: 2,
14176            },
14177        )
14178        .expect("predicate precedence");
14179        atn.add_transition(
14180            7,
14181            ParserTransitionSpec::Predicate {
14182                target: 8,
14183                rule_index: 0,
14184                pred_index: 0,
14185                context_dependent: false,
14186            },
14187        )
14188        .expect("operator predicate");
14189        atn.add_transition(
14190            8,
14191            ParserTransitionSpec::Atom {
14192                target: 1,
14193                label: 1,
14194            },
14195        )
14196        .expect("predicate single token");
14197        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
14198            .expect("loop end");
14199        finish_atn(atn)
14200    }
14201
14202    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
14203        let mut atn = ParserAtnBuilder::new(2);
14204        for (state, kind, rule) in [
14205            (0, AtnStateKind::RuleStart, 0),
14206            (1, AtnStateKind::StarLoopEntry, 0),
14207            (2, AtnStateKind::Basic, 0),
14208            (3, AtnStateKind::Basic, 0),
14209            (4, AtnStateKind::Basic, 0),
14210            (5, AtnStateKind::LoopEnd, 0),
14211            (6, AtnStateKind::RuleStop, 0),
14212            (7, AtnStateKind::RuleStart, 1),
14213            (8, AtnStateKind::RuleStop, 1),
14214            (9, AtnStateKind::Basic, 1),
14215        ] {
14216            assert_eq!(
14217                atn.add_state(kind, Some(rule)).expect("state").index(),
14218                state
14219            );
14220            if state == 0 {
14221                atn.set_left_recursive_rule(state)
14222                    .expect("left-recursive rule start");
14223            } else if state == 1 {
14224                atn.set_precedence_rule_decision(state)
14225                    .expect("precedence decision");
14226            }
14227        }
14228        atn.set_rule_to_start_state(vec![0, 7])
14229            .expect("rule start states");
14230        atn.set_rule_to_stop_state(vec![6, 8])
14231            .expect("rule stop states");
14232        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14233            .expect("transition");
14234        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
14235            .expect("transition");
14236        atn.add_transition(
14237            2,
14238            ParserTransitionSpec::Precedence {
14239                target: 3,
14240                precedence: 3,
14241            },
14242        )
14243        .expect("transition");
14244        atn.add_transition(
14245            3,
14246            ParserTransitionSpec::Rule {
14247                target: 7,
14248                rule_index: 1,
14249                follow_state: 4,
14250                precedence: 0,
14251            },
14252        )
14253        .expect("transition");
14254        atn.add_transition(
14255            4,
14256            ParserTransitionSpec::Atom {
14257                target: 1,
14258                label: 1,
14259            },
14260        )
14261        .expect("transition");
14262        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
14263            .expect("transition");
14264        atn.add_transition(
14265            7,
14266            ParserTransitionSpec::Precedence {
14267                target: 9,
14268                precedence: 1,
14269            },
14270        )
14271        .expect("transition");
14272        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
14273            .expect("transition");
14274        finish_atn(atn)
14275    }
14276
14277    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
14278        let mut atn = ParserAtnBuilder::new(2);
14279        for (state, kind) in [
14280            (0, AtnStateKind::RuleStart),
14281            (1, AtnStateKind::StarLoopEntry),
14282            (2, AtnStateKind::Basic),
14283            (3, AtnStateKind::Basic),
14284            (4, AtnStateKind::Basic),
14285            (5, AtnStateKind::LoopEnd),
14286            (6, AtnStateKind::RuleStop),
14287        ] {
14288            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
14289            if state == 0 {
14290                atn.set_left_recursive_rule(state)
14291                    .expect("left-recursive rule start");
14292            } else if state == 1 {
14293                atn.set_precedence_rule_decision(state)
14294                    .expect("precedence decision");
14295            }
14296        }
14297        atn.set_rule_to_start_state(vec![0])
14298            .expect("rule start states");
14299        atn.set_rule_to_stop_state(vec![6])
14300            .expect("rule stop states");
14301        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14302            .expect("transition");
14303        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
14304            .expect("transition");
14305        atn.add_transition(
14306            2,
14307            ParserTransitionSpec::Precedence {
14308                target: 3,
14309                precedence: 1,
14310            },
14311        )
14312        .expect("transition");
14313        atn.add_transition(
14314            3,
14315            ParserTransitionSpec::Predicate {
14316                target: 4,
14317                rule_index: 0,
14318                pred_index: 0,
14319                context_dependent: false,
14320            },
14321        )
14322        .expect("transition");
14323        atn.add_transition(
14324            4,
14325            ParserTransitionSpec::Atom {
14326                target: 1,
14327                label: 1,
14328            },
14329        )
14330        .expect("transition");
14331        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
14332            .expect("transition");
14333        finish_atn(atn)
14334    }
14335
14336    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
14337        let mut atn = ParserAtnBuilder::new(2);
14338        for (state, kind, rule) in [
14339            (0, AtnStateKind::RuleStart, 0),
14340            (1, AtnStateKind::Basic, 0),
14341            (2, AtnStateKind::Basic, 0),
14342            (3, AtnStateKind::Basic, 0),
14343            (4, AtnStateKind::RuleStop, 0),
14344            (5, AtnStateKind::RuleStart, 1),
14345            (6, AtnStateKind::StarLoopEntry, 1),
14346            (7, AtnStateKind::Basic, 1),
14347            (8, AtnStateKind::Basic, 1),
14348            (9, AtnStateKind::LoopEnd, 1),
14349            (10, AtnStateKind::RuleStop, 1),
14350            (11, AtnStateKind::RuleStart, 2),
14351            (12, AtnStateKind::RuleStop, 2),
14352        ] {
14353            assert_eq!(
14354                atn.add_state(kind, Some(rule)).expect("state").index(),
14355                state
14356            );
14357            if state == 5 {
14358                atn.set_left_recursive_rule(state)
14359                    .expect("left-recursive rule start");
14360            } else if state == 6 {
14361                atn.set_precedence_rule_decision(state)
14362                    .expect("precedence decision");
14363            }
14364        }
14365        atn.set_rule_to_start_state(vec![0, 5, 11])
14366            .expect("rule start states");
14367        atn.set_rule_to_stop_state(vec![4, 10, 12])
14368            .expect("rule stop states");
14369        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14370            .expect("transition");
14371        atn.add_transition(
14372            1,
14373            ParserTransitionSpec::Rule {
14374                target: 5,
14375                rule_index: 1,
14376                follow_state: 2,
14377                precedence: 0,
14378            },
14379        )
14380        .expect("transition");
14381        atn.add_transition(
14382            2,
14383            ParserTransitionSpec::Rule {
14384                target: 11,
14385                rule_index: 2,
14386                follow_state: 3,
14387                precedence: 0,
14388            },
14389        )
14390        .expect("transition");
14391        atn.add_transition(
14392            3,
14393            ParserTransitionSpec::Atom {
14394                target: 4,
14395                label: caller_symbol,
14396            },
14397        )
14398        .expect("transition");
14399        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
14400            .expect("transition");
14401        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
14402            .expect("transition");
14403        atn.add_transition(
14404            7,
14405            ParserTransitionSpec::Precedence {
14406                target: 8,
14407                precedence: 1,
14408            },
14409        )
14410        .expect("transition");
14411        atn.add_transition(
14412            8,
14413            ParserTransitionSpec::Atom {
14414                target: 6,
14415                label: 1,
14416            },
14417        )
14418        .expect("transition");
14419        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
14420            .expect("transition");
14421        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
14422            .expect("transition");
14423        finish_atn(atn)
14424    }
14425
14426    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
14427        let mut atn = ParserAtnBuilder::new(2);
14428        for (state, kind, rule) in [
14429            (0, AtnStateKind::RuleStart, 0),
14430            (1, AtnStateKind::Basic, 0),
14431            (2, AtnStateKind::Basic, 0),
14432            (3, AtnStateKind::RuleStop, 0),
14433            (4, AtnStateKind::RuleStart, 1),
14434            (5, AtnStateKind::Basic, 1),
14435            (6, AtnStateKind::Basic, 1),
14436            (7, AtnStateKind::RuleStop, 1),
14437            (8, AtnStateKind::RuleStart, 2),
14438            (9, AtnStateKind::StarLoopEntry, 2),
14439            (10, AtnStateKind::Basic, 2),
14440            (11, AtnStateKind::Basic, 2),
14441            (12, AtnStateKind::LoopEnd, 2),
14442            (13, AtnStateKind::RuleStop, 2),
14443        ] {
14444            assert_eq!(
14445                atn.add_state(kind, Some(rule)).expect("state").index(),
14446                state
14447            );
14448            if state == 8 {
14449                atn.set_left_recursive_rule(state)
14450                    .expect("left-recursive rule start");
14451            } else if state == 9 {
14452                atn.set_precedence_rule_decision(state)
14453                    .expect("precedence decision");
14454            }
14455        }
14456        atn.set_rule_to_start_state(vec![0, 4, 8])
14457            .expect("rule start states");
14458        atn.set_rule_to_stop_state(vec![3, 7, 13])
14459            .expect("rule stop states");
14460        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14461            .expect("transition");
14462        atn.add_transition(
14463            1,
14464            ParserTransitionSpec::Rule {
14465                target: 4,
14466                rule_index: 1,
14467                follow_state: 2,
14468                precedence: 0,
14469            },
14470        )
14471        .expect("transition");
14472        atn.add_transition(
14473            2,
14474            ParserTransitionSpec::Atom {
14475                target: 3,
14476                label: caller_symbol,
14477            },
14478        )
14479        .expect("transition");
14480        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
14481            .expect("transition");
14482        atn.add_transition(
14483            5,
14484            ParserTransitionSpec::Rule {
14485                target: 8,
14486                rule_index: 2,
14487                follow_state: 6,
14488                precedence: 0,
14489            },
14490        )
14491        .expect("transition");
14492        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
14493            .expect("transition");
14494        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
14495            .expect("transition");
14496        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
14497            .expect("transition");
14498        atn.add_transition(
14499            10,
14500            ParserTransitionSpec::Precedence {
14501                target: 11,
14502                precedence: 1,
14503            },
14504        )
14505        .expect("transition");
14506        atn.add_transition(
14507            11,
14508            ParserTransitionSpec::Atom {
14509                target: 9,
14510                label: 1,
14511            },
14512        )
14513        .expect("transition");
14514        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
14515            .expect("transition");
14516        finish_atn(atn)
14517    }
14518
14519    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
14520        let mut atn = ParserAtnBuilder::new(2);
14521        for (state, kind, rule) in [
14522            (0, AtnStateKind::RuleStart, 0),
14523            (1, AtnStateKind::Basic, 0),
14524            (2, AtnStateKind::Basic, 0),
14525            (3, AtnStateKind::RuleStop, 0),
14526            (4, AtnStateKind::RuleStart, 1),
14527            (5, AtnStateKind::StarLoopEntry, 1),
14528            (6, AtnStateKind::Basic, 1),
14529            (7, AtnStateKind::Basic, 1),
14530            (8, AtnStateKind::Basic, 1),
14531            (9, AtnStateKind::Basic, 1),
14532            (10, AtnStateKind::LoopEnd, 1),
14533            (11, AtnStateKind::RuleStop, 1),
14534        ] {
14535            assert_eq!(
14536                atn.add_state(kind, Some(rule)).expect("state").index(),
14537                state
14538            );
14539            if state == 4 {
14540                atn.set_left_recursive_rule(state)
14541                    .expect("left-recursive rule start");
14542            } else if state == 5 {
14543                atn.set_precedence_rule_decision(state)
14544                    .expect("precedence decision");
14545            }
14546        }
14547        atn.set_rule_to_start_state(vec![0, 4])
14548            .expect("rule start states");
14549        atn.set_rule_to_stop_state(vec![3, 11])
14550            .expect("rule stop states");
14551        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14552            .expect("transition");
14553        atn.add_transition(
14554            1,
14555            ParserTransitionSpec::Rule {
14556                target: 4,
14557                rule_index: 1,
14558                follow_state: 2,
14559                precedence: 0,
14560            },
14561        )
14562        .expect("transition");
14563        atn.add_transition(
14564            2,
14565            ParserTransitionSpec::Atom {
14566                target: 3,
14567                label: caller_symbol,
14568            },
14569        )
14570        .expect("transition");
14571        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
14572            .expect("transition");
14573        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
14574            .expect("transition");
14575        atn.add_transition(
14576            6,
14577            ParserTransitionSpec::Precedence {
14578                target: 7,
14579                precedence: 1,
14580            },
14581        )
14582        .expect("transition");
14583        atn.add_transition(
14584            7,
14585            ParserTransitionSpec::Atom {
14586                target: 8,
14587                label: 1,
14588            },
14589        )
14590        .expect("transition");
14591        atn.add_transition(
14592            8,
14593            ParserTransitionSpec::Rule {
14594                target: 4,
14595                rule_index: 1,
14596                follow_state: 9,
14597                precedence: 2,
14598            },
14599        )
14600        .expect("transition");
14601        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
14602            .expect("transition");
14603        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
14604            .expect("transition");
14605        finish_atn(atn)
14606    }
14607
14608    #[test]
14609    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
14610        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
14611        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
14612
14613        let mut overlapping = parser_inside_left_recursive_callee(1);
14614        assert_eq!(
14615            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
14616            None
14617        );
14618
14619        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
14620        assert_eq!(
14621            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
14622            Some(true)
14623        );
14624
14625        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
14626        assert_eq!(
14627            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
14628            Some(false)
14629        );
14630
14631        assert_eq!(
14632            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
14633            Some(true),
14634            "overlap results must not leak across ATNs"
14635        );
14636    }
14637
14638    #[test]
14639    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
14640        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
14641        let mut parser = mini_parser(vec![
14642            TestToken::new(1).with_text("operator"),
14643            TestToken::eof("parser-test", 1, 1, 1),
14644        ]);
14645        parser.rule_context_stack = vec![RuleContextFrame {
14646            rule_index: 0,
14647            invoking_state: -1,
14648        }];
14649
14650        assert_eq!(
14651            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14652            Some(true)
14653        );
14654        assert_eq!(
14655            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14656            Some(true),
14657            "cached operator lookahead must preserve the nullable prefix return path"
14658        );
14659        assert_eq!(
14660            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14661            Some(true),
14662            "the nullable child must use its rule-call precedence, not the caller precedence"
14663        );
14664    }
14665
14666    #[test]
14667    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
14668        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
14669        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
14670        // must defer so StarLoopEntry adaptive predict can exit when the second
14671        // `>` is absent (as in `a < b > c`).
14672        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
14673        let mut parser = mini_parser(vec![
14674            TestToken::new(1).with_text(">"),
14675            TestToken::new(2).with_text("id"),
14676            TestToken::eof("parser-test", 1, 1, 1),
14677        ]);
14678        parser.rule_context_stack = vec![RuleContextFrame {
14679            rule_index: 0,
14680            invoking_state: -1,
14681        }];
14682
14683        assert_eq!(
14684            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14685            Some(true),
14686            "at low precedence relational `>` is a single-token operator"
14687        );
14688        assert_eq!(
14689            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
14690            Some(true),
14691            "relational remains single-token at its own precedence"
14692        );
14693        assert_eq!(
14694            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14695            None,
14696            "at shift precedence, bare `>` must not force enter"
14697        );
14698    }
14699
14700    #[test]
14701    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
14702        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
14703        let mut parser = mini_parser(vec![
14704            TestToken::new(1).with_text(">"),
14705            TestToken::new(2).with_text("id"),
14706            TestToken::eof("parser-test", 1, 1, 1),
14707        ]);
14708        parser.rule_context_stack = vec![RuleContextFrame {
14709            rule_index: 0,
14710            invoking_state: -1,
14711        }];
14712
14713        assert_eq!(
14714            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14715            Some(true),
14716            "the direct relational alternative remains a one-token operator"
14717        );
14718        assert_eq!(
14719            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14720            None,
14721            "a token matched in the helper rule must return to the second shift token"
14722        );
14723    }
14724
14725    #[test]
14726    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
14727        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
14728        let mut parser = mini_parser(vec![
14729            TestToken::new(1).with_text(">"),
14730            TestToken::new(2).with_text("id"),
14731            TestToken::eof("parser-test", 1, 1, 1),
14732        ]);
14733        parser.rule_context_stack = vec![RuleContextFrame {
14734            rule_index: 0,
14735            invoking_state: -1,
14736        }];
14737
14738        assert_eq!(
14739            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
14740            None,
14741            "a predicate-gated single-token path must not be hidden by a multi-token path"
14742        );
14743    }
14744
14745    #[test]
14746    fn left_recursive_loop_defers_predicate_guarded_operator() {
14747        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
14748        let mut parser = mini_parser_with_hooks(
14749            vec![
14750                TestToken::new(1).with_text("operator"),
14751                TestToken::eof("parser-test", 1, 1, 1),
14752            ],
14753            RejectingPredicateHooks::default(),
14754        );
14755        parser.rule_context_stack = vec![RuleContextFrame {
14756            rule_index: 0,
14757            invoking_state: -1,
14758        }];
14759
14760        assert_eq!(
14761            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14762            None,
14763            "a false predicate must be evaluated before entering the operator alternative"
14764        );
14765        assert_eq!(
14766            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
14767            None,
14768            "cached predicate-dependent lookahead must keep deferring"
14769        );
14770    }
14771
14772    #[test]
14773    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
14774        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
14775        let mut parser = parser_inside_left_recursive_callee(1);
14776
14777        assert_eq!(
14778            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
14779            None
14780        );
14781        assert_eq!(
14782            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
14783            None,
14784            "the cached overlap must preserve the nullable child return path"
14785        );
14786    }
14787
14788    #[test]
14789    fn left_recursive_loop_defers_through_nullable_parent_return() {
14790        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
14791        let mut parser = mini_parser(vec![
14792            TestToken::new(1).with_text("lookahead"),
14793            TestToken::eof("parser-test", 1, 1, 1),
14794        ]);
14795        parser.rule_context_stack = vec![
14796            RuleContextFrame {
14797                rule_index: 0,
14798                invoking_state: -1,
14799            },
14800            RuleContextFrame {
14801                rule_index: 1,
14802                invoking_state: 1,
14803            },
14804            RuleContextFrame {
14805                rule_index: 2,
14806                invoking_state: 5,
14807            },
14808        ];
14809
14810        assert_eq!(
14811            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
14812            None,
14813            "a nullable caller must unwind to its parent's consuming follow path"
14814        );
14815        assert_eq!(
14816            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
14817            None,
14818            "the caller-overlap cache must not retain a false negative"
14819        );
14820    }
14821
14822    #[test]
14823    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
14824        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
14825        let mut parser = mini_parser(vec![
14826            TestToken::new(1).with_text("lookahead"),
14827            TestToken::eof("parser-test", 1, 1, 1),
14828        ]);
14829        parser.rule_context_stack = vec![
14830            RuleContextFrame {
14831                rule_index: 0,
14832                invoking_state: -1,
14833            },
14834            RuleContextFrame {
14835                rule_index: 1,
14836                invoking_state: 1,
14837            },
14838            RuleContextFrame {
14839                rule_index: 1,
14840                invoking_state: 8,
14841            },
14842        ];
14843
14844        assert_eq!(
14845            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
14846            None,
14847            "a recursive operand return must preserve its parent caller context"
14848        );
14849        assert_eq!(
14850            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
14851            None,
14852            "the caller-overlap cache must preserve the loop-boundary return"
14853        );
14854    }
14855
14856    fn token_then_eof_atn() -> Atn {
14857        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14858            4, 1, 2, // version, parser, max token type
14859            3, // states
14860            2, 0, // rule start
14861            1, 0, // basic
14862            7, 0, // rule stop
14863            0, // non-greedy states
14864            0, // precedence states
14865            1, // rules
14866            0, // rule 0 start
14867            0, // modes
14868            0, // sets
14869            2, // transitions
14870            0, 1, 5, 1, 0, 0, // match token 1
14871            1, 2, 5, -1, 0, 0, // match EOF
14872            0, // decisions
14873        ]))
14874        .deserialize_parser()
14875        .expect("artificial parser ATN should deserialize")
14876    }
14877
14878    fn epsilon_cycle_atn() -> Atn {
14879        let mut atn = ParserAtnBuilder::new(1);
14880        for (state_number, kind) in [
14881            (0, AtnStateKind::RuleStart),
14882            (1, AtnStateKind::Basic),
14883            (2, AtnStateKind::RuleStop),
14884        ] {
14885            assert_eq!(
14886                atn.add_state(kind, Some(0)).expect("state").index(),
14887                state_number
14888            );
14889        }
14890        atn.set_rule_to_start_state(vec![0])
14891            .expect("rule start states");
14892        atn.set_rule_to_stop_state(vec![2])
14893            .expect("rule stop states");
14894        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14895            .expect("transition");
14896        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
14897            .expect("self-cycle transition");
14898        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14899            .expect("exit transition");
14900        finish_atn(atn)
14901    }
14902
14903    fn eof_then_action_atn() -> Atn {
14904        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14905            4, 1, 1, // version, parser, max token type
14906            3, // states
14907            2, 0, // rule start
14908            1, 0, // basic
14909            7, 0, // rule stop
14910            0, // non-greedy states
14911            0, // precedence states
14912            1, // rules
14913            0, // rule 0 start
14914            0, // modes
14915            0, // sets
14916            2, // transitions
14917            0, 1, 5, -1, 0, 0, // match EOF
14918            1, 2, 6, 0, 0, 0, // parser action
14919            0, // decisions
14920        ]))
14921        .deserialize_parser()
14922        .expect("artificial parser ATN should deserialize")
14923    }
14924
14925    fn noop_action_then_token_then_eof_atn() -> Atn {
14926        AtnDeserializer::new(&SerializedAtn::from_i32(&[
14927            4, 1, 2, // version, parser, max token type
14928            4, // states
14929            2, 0, // rule start
14930            1, 0, // basic
14931            1, 0, // basic
14932            7, 0, // rule stop
14933            0, // non-greedy states
14934            0, // precedence states
14935            1, // rules
14936            0, // rule 0 start
14937            0, // modes
14938            0, // sets
14939            3, // transitions
14940            0, 1, 6, 0, -1, 0, // no-op parser action
14941            1, 2, 5, 1, 0, 0, // match token 1
14942            2, 3, 5, -1, 0, 0, // match EOF
14943            0, // decisions
14944        ]))
14945        .deserialize_parser()
14946        .expect("artificial no-op action ATN should deserialize")
14947    }
14948
14949    fn two_alt_decision_atn() -> Atn {
14950        let mut atn = ParserAtnBuilder::new(2);
14951        assert_eq!(
14952            atn.add_state(AtnStateKind::RuleStart, Some(0))
14953                .expect("state")
14954                .index(),
14955            0
14956        );
14957        assert_eq!(
14958            atn.add_state(AtnStateKind::BlockStart, Some(0))
14959                .expect("state")
14960                .index(),
14961            1
14962        );
14963        assert_eq!(
14964            atn.add_state(AtnStateKind::Basic, Some(0))
14965                .expect("state")
14966                .index(),
14967            2
14968        );
14969        assert_eq!(
14970            atn.add_state(AtnStateKind::Basic, Some(0))
14971                .expect("state")
14972                .index(),
14973            3
14974        );
14975        assert_eq!(
14976            atn.add_state(AtnStateKind::BlockEnd, Some(0))
14977                .expect("state")
14978                .index(),
14979            4
14980        );
14981        assert_eq!(
14982            atn.add_state(AtnStateKind::RuleStop, Some(0))
14983                .expect("state")
14984                .index(),
14985            5
14986        );
14987        atn.set_rule_to_start_state(vec![0])
14988            .expect("rule start states");
14989        atn.set_rule_to_stop_state(vec![5])
14990            .expect("rule stop states");
14991        atn.add_decision_state(1).expect("decision state");
14992        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14993            .expect("transition");
14994        atn.add_transition(
14995            1,
14996            ParserTransitionSpec::Atom {
14997                target: 2,
14998                label: 1,
14999            },
15000        )
15001        .expect("transition");
15002        atn.add_transition(
15003            1,
15004            ParserTransitionSpec::Atom {
15005                target: 3,
15006                label: 2,
15007            },
15008        )
15009        .expect("transition");
15010        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
15011            .expect("transition");
15012        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
15013            .expect("transition");
15014        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
15015            .expect("transition");
15016        finish_atn(atn)
15017    }
15018
15019    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
15020    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
15021    fn optional_then_b_eof_atn() -> Atn {
15022        let mut atn = ParserAtnBuilder::new(3);
15023        assert_eq!(
15024            atn.add_state(AtnStateKind::RuleStart, Some(0))
15025                .expect("state")
15026                .index(),
15027            0
15028        );
15029        assert_eq!(
15030            atn.add_state(AtnStateKind::BlockStart, Some(0))
15031                .expect("state")
15032                .index(),
15033            1
15034        );
15035        assert_eq!(
15036            atn.add_state(AtnStateKind::Basic, Some(0))
15037                .expect("state")
15038                .index(),
15039            2
15040        );
15041        assert_eq!(
15042            atn.add_state(AtnStateKind::Basic, Some(0))
15043                .expect("state")
15044                .index(),
15045            3
15046        );
15047        assert_eq!(
15048            atn.add_state(AtnStateKind::Basic, Some(0))
15049                .expect("state")
15050                .index(),
15051            4
15052        );
15053        assert_eq!(
15054            atn.add_state(AtnStateKind::RuleStop, Some(0))
15055                .expect("state")
15056                .index(),
15057            5
15058        );
15059        atn.set_rule_to_start_state(vec![0])
15060            .expect("rule start states");
15061        atn.set_rule_to_stop_state(vec![5])
15062            .expect("rule stop states");
15063        atn.add_decision_state(1).expect("decision state");
15064        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15065            .expect("transition");
15066        // Optional block: match A then fall through, or skip straight to state 3.
15067        atn.add_transition(
15068            1,
15069            ParserTransitionSpec::Atom {
15070                target: 3,
15071                label: 1,
15072            },
15073        )
15074        .expect("transition");
15075        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
15076            .expect("transition");
15077        // Match B, then EOF.
15078        atn.add_transition(
15079            3,
15080            ParserTransitionSpec::Atom {
15081                target: 4,
15082                label: 2,
15083            },
15084        )
15085        .expect("transition");
15086        atn.add_transition(
15087            4,
15088            ParserTransitionSpec::Atom {
15089                target: 5,
15090                label: TOKEN_EOF,
15091            },
15092        )
15093        .expect("transition");
15094        finish_atn(atn)
15095    }
15096
15097    #[test]
15098    fn sync_decision_deletes_only_a_single_token() {
15099        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
15100        // expected. `(A)? B EOF` at the optional-block decision:
15101        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
15102        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
15103        //               without consuming and records the expected set for the
15104        //               subsequent mismatch (the parser must not over-consume both
15105        //               `C`s and accept the input).
15106        let atn = optional_then_b_eof_atn();
15107
15108        let mut single = mini_parser(vec![
15109            TestToken::new(3).with_text("c"),
15110            TestToken::new(2).with_text("b"),
15111            TestToken::eof("parser-test", 1, 2, 2),
15112        ]);
15113        single.rule_context_stack = vec![RuleContextFrame {
15114            rule_index: 0,
15115            invoking_state: 0,
15116        }];
15117        let children = single
15118            .sync_decision(&atn, 1, true, false)
15119            .expect("single extraneous token recovers");
15120        assert_eq!(children.len(), 1);
15121        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
15122        assert_eq!(single.number_of_syntax_errors(), 1);
15123        // Exactly one token consumed (the cursor now sits on `b`).
15124        assert_eq!(single.la(1), 2);
15125
15126        let mut double = mini_parser(vec![
15127            TestToken::new(3).with_text("c"),
15128            TestToken::new(3).with_text("c"),
15129            TestToken::new(2).with_text("b"),
15130            TestToken::eof("parser-test", 1, 3, 3),
15131        ]);
15132        double.rule_context_stack = vec![RuleContextFrame {
15133            rule_index: 0,
15134            invoking_state: 0,
15135        }];
15136        let result = double.sync_decision(&atn, 1, true, false);
15137        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
15138        // consume either `c`. It reports the mismatch at the first `c` (so the parser
15139        // does not over-consume both and accept the input). Nothing is consumed, so
15140        // the cursor still sits on the first `c` for rule-level recovery.
15141        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
15142        match error {
15143            AntlrError::ParserError { message, .. } => {
15144                assert!(message.starts_with("mismatched input"), "got: {message}");
15145            }
15146            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
15147        }
15148        assert_eq!(double.la(1), 3);
15149    }
15150
15151    /// The real serialized ATN that `antlr4-rust-gen` emits for
15152    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
15153    /// the loop is `EOF`. The loop decision is state 5.
15154    fn star_loop_then_eof_atn() -> Atn {
15155        AtnDeserializer::new(&SerializedAtn::from_i32(&[
15156            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,
15157            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,
15158            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,
15159            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
15160        ]))
15161        .deserialize_parser()
15162        .expect("star-loop-then-EOF ATN should deserialize")
15163    }
15164
15165    /// ATN for `s : a+ Y ; a : X ;`.
15166    ///
15167    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
15168    /// `+` loop must not treat that zero-width child as a successful iteration
15169    /// and then re-enter the loop at the same token index.
15170    fn plus_loop_with_recovering_body_atn() -> Atn {
15171        let mut atn = ParserAtnBuilder::new(2);
15172        assert_eq!(
15173            atn.add_state(AtnStateKind::RuleStart, Some(0))
15174                .expect("state")
15175                .index(),
15176            0
15177        );
15178        assert_eq!(
15179            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
15180                .expect("state")
15181                .index(),
15182            1
15183        );
15184        assert_eq!(
15185            atn.add_state(AtnStateKind::Basic, Some(0))
15186                .expect("state")
15187                .index(),
15188            2
15189        );
15190        assert_eq!(
15191            atn.add_state(AtnStateKind::BlockEnd, Some(0))
15192                .expect("state")
15193                .index(),
15194            3
15195        );
15196        assert_eq!(
15197            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
15198                .expect("state")
15199                .index(),
15200            4
15201        );
15202        assert_eq!(
15203            atn.add_state(AtnStateKind::LoopEnd, Some(0))
15204                .expect("state")
15205                .index(),
15206            5
15207        );
15208        assert_eq!(
15209            atn.add_state(AtnStateKind::RuleStop, Some(0))
15210                .expect("state")
15211                .index(),
15212            6
15213        );
15214        assert_eq!(
15215            atn.add_state(AtnStateKind::RuleStart, Some(1))
15216                .expect("state")
15217                .index(),
15218            7
15219        );
15220        assert_eq!(
15221            atn.add_state(AtnStateKind::Basic, Some(1))
15222                .expect("state")
15223                .index(),
15224            8
15225        );
15226        assert_eq!(
15227            atn.add_state(AtnStateKind::RuleStop, Some(1))
15228                .expect("state")
15229                .index(),
15230            9
15231        );
15232        atn.set_rule_to_start_state(vec![0, 7])
15233            .expect("rule start states");
15234        atn.set_rule_to_stop_state(vec![6, 9])
15235            .expect("rule stop states");
15236        atn.set_end_state(1, 3).expect("block end state");
15237        atn.set_loop_back_state(5, 4).expect("loop back state");
15238        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15239            .expect("transition");
15240        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15241            .expect("transition");
15242        atn.add_transition(
15243            2,
15244            ParserTransitionSpec::Rule {
15245                target: 7,
15246                rule_index: 1,
15247                follow_state: 3,
15248                precedence: 0,
15249            },
15250        )
15251        .expect("transition");
15252        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
15253            .expect("transition");
15254        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
15255            .expect("transition");
15256        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
15257            .expect("transition");
15258        atn.add_transition(
15259            5,
15260            ParserTransitionSpec::Atom {
15261                target: 6,
15262                label: 2,
15263            },
15264        )
15265        .expect("transition");
15266        atn.add_transition(
15267            7,
15268            ParserTransitionSpec::Atom {
15269                target: 8,
15270                label: 1,
15271            },
15272        )
15273        .expect("transition");
15274        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
15275            .expect("transition");
15276        finish_atn(atn)
15277    }
15278
15279    #[test]
15280    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
15281        let atn = plus_loop_with_recovering_body_atn();
15282        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15283
15284        let error = parser
15285            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
15286            .expect_err("EOF recovery should report a bounded mismatch");
15287
15288        let AntlrError::ParserError { message, .. } = error else {
15289            panic!("expected ParserError, got {error:?}");
15290        };
15291        insta::assert_snapshot!(message, @"mismatched input '<EOF>' expecting {'x', 2}");
15292        assert_eq!(parser.number_of_syntax_errors(), 1);
15293        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
15294    }
15295
15296    #[test]
15297    fn sync_decision_deletes_token_before_eof_at_loop_back() {
15298        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
15299        // At the loop ENTRY (loop_back = false) a single unexpected token before
15300        // EOF is deleted as an error node (then the generated EOF match consumes
15301        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
15302        // EOF must be a valid scan-stop for this to fire.
15303        let atn = star_loop_then_eof_atn();
15304        let mut parser = mini_parser(vec![
15305            TestToken::new(2).with_text("c"),
15306            TestToken::eof("parser-test", 1, 1, 1),
15307        ]);
15308        parser.rule_context_stack = vec![RuleContextFrame {
15309            rule_index: 0,
15310            invoking_state: 0,
15311        }];
15312        let children = parser
15313            .sync_decision(&atn, 5, true, false)
15314            .expect("single token before EOF recovers");
15315        assert_eq!(children.len(), 1);
15316        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
15317        assert_eq!(parser.number_of_syntax_errors(), 1);
15318        assert_eq!(
15319            parser.la(1),
15320            TOKEN_EOF,
15321            "EOF is left for the rule's EOF match"
15322        );
15323    }
15324
15325    #[test]
15326    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
15327        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
15328        // single-token deletion, which fails because LA(2) = `c` is not expected —
15329        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
15330        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
15331        let atn = star_loop_then_eof_atn();
15332        let mut parser = mini_parser(vec![
15333            TestToken::new(2).with_text("c"),
15334            TestToken::new(2).with_text("c"),
15335            TestToken::eof("parser-test", 1, 2, 2),
15336        ]);
15337        parser.rule_context_stack = vec![RuleContextFrame {
15338            rule_index: 0,
15339            invoking_state: 0,
15340        }];
15341        let error = parser
15342            .sync_decision(&atn, 5, true, false)
15343            .expect_err("two tokens at the loop entry must not be deleted");
15344        match error {
15345            AntlrError::ParserError { message, .. } => {
15346                assert!(message.starts_with("mismatched input"), "got: {message}");
15347            }
15348            other => panic!("expected mismatched-input ParserError, got {other:?}"),
15349        }
15350        assert_eq!(
15351            parser.la(1),
15352            2,
15353            "nothing consumed; cursor still on first `c`"
15354        );
15355    }
15356
15357    #[test]
15358    fn sync_decision_consumes_until_eof_at_loop_back() {
15359        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
15360        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
15361        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
15362        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
15363        // post-`a` state directly: `c c <EOF>` with loop_back = true.
15364        let atn = star_loop_then_eof_atn();
15365        let mut parser = mini_parser(vec![
15366            TestToken::new(2).with_text("c"),
15367            TestToken::new(2).with_text("c"),
15368            TestToken::eof("parser-test", 1, 2, 2),
15369        ]);
15370        parser.rule_context_stack = vec![RuleContextFrame {
15371            rule_index: 0,
15372            invoking_state: 0,
15373        }];
15374        let children = parser
15375            .sync_decision(&atn, 5, false, true)
15376            .expect("loop-back multi-token deletion recovers onto EOF");
15377        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
15378        assert!(
15379            children
15380                .iter()
15381                .all(|child| parser.node(*child).kind() == NodeKind::Error)
15382        );
15383        assert_eq!(parser.number_of_syntax_errors(), 1);
15384        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
15385    }
15386
15387    fn predicate_after_token_atn() -> Atn {
15388        let mut atn = ParserAtnBuilder::new(2);
15389        assert_eq!(
15390            atn.add_state(AtnStateKind::RuleStart, Some(0))
15391                .expect("state")
15392                .index(),
15393            0
15394        );
15395        assert_eq!(
15396            atn.add_state(AtnStateKind::Basic, Some(0))
15397                .expect("state")
15398                .index(),
15399            1
15400        );
15401        assert_eq!(
15402            atn.add_state(AtnStateKind::Basic, Some(0))
15403                .expect("state")
15404                .index(),
15405            2
15406        );
15407        assert_eq!(
15408            atn.add_state(AtnStateKind::Basic, Some(0))
15409                .expect("state")
15410                .index(),
15411            3
15412        );
15413        assert_eq!(
15414            atn.add_state(AtnStateKind::RuleStop, Some(0))
15415                .expect("state")
15416                .index(),
15417            4
15418        );
15419        atn.set_rule_to_start_state(vec![0])
15420            .expect("rule start states");
15421        atn.set_rule_to_stop_state(vec![4])
15422            .expect("rule stop states");
15423        atn.add_transition(
15424            0,
15425            ParserTransitionSpec::Atom {
15426                target: 1,
15427                label: 1,
15428            },
15429        )
15430        .expect("transition");
15431        atn.add_transition(
15432            1,
15433            ParserTransitionSpec::Predicate {
15434                target: 2,
15435                rule_index: 0,
15436                pred_index: 0,
15437                context_dependent: false,
15438            },
15439        )
15440        .expect("transition");
15441        atn.add_transition(
15442            2,
15443            ParserTransitionSpec::Atom {
15444                target: 3,
15445                label: 2,
15446            },
15447        )
15448        .expect("transition");
15449        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
15450            .expect("transition");
15451        finish_atn(atn)
15452    }
15453
15454    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
15455        let mut atn = ParserAtnBuilder::new(1);
15456        for (state_number, kind) in [
15457            (0, AtnStateKind::RuleStart),
15458            (1, AtnStateKind::BlockStart),
15459            (2, AtnStateKind::Basic),
15460            (3, AtnStateKind::Basic),
15461            (4, AtnStateKind::Basic),
15462            (5, AtnStateKind::Basic),
15463            (6, AtnStateKind::BlockEnd),
15464            (7, AtnStateKind::RuleStop),
15465        ] {
15466            assert_eq!(
15467                atn.add_state(kind, Some(0)).expect("state").index(),
15468                state_number
15469            );
15470        }
15471        atn.set_rule_to_start_state(vec![0])
15472            .expect("rule start states");
15473        atn.set_rule_to_stop_state(vec![7])
15474            .expect("rule stop states");
15475        atn.add_decision_state(1).expect("decision state");
15476        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15477            .expect("transition");
15478        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15479            .expect("transition");
15480        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
15481            .expect("transition");
15482        atn.add_transition(
15483            2,
15484            ParserTransitionSpec::Predicate {
15485                target: 4,
15486                rule_index: 0,
15487                pred_index: pred_indexes[0],
15488                context_dependent: false,
15489            },
15490        )
15491        .expect("transition");
15492        atn.add_transition(
15493            3,
15494            ParserTransitionSpec::Predicate {
15495                target: 5,
15496                rule_index: 0,
15497                pred_index: pred_indexes[1],
15498                context_dependent: false,
15499            },
15500        )
15501        .expect("transition");
15502        atn.add_transition(
15503            4,
15504            ParserTransitionSpec::Atom {
15505                target: 6,
15506                label: 1,
15507            },
15508        )
15509        .expect("transition");
15510        atn.add_transition(
15511            5,
15512            ParserTransitionSpec::Atom {
15513                target: 6,
15514                label: 1,
15515            },
15516        )
15517        .expect("transition");
15518        atn.add_transition(
15519            6,
15520            ParserTransitionSpec::Atom {
15521                target: 7,
15522                label: TOKEN_EOF,
15523            },
15524        )
15525        .expect("transition");
15526        finish_atn(atn)
15527    }
15528
15529    fn nested_nullable_context_atn() -> Atn {
15530        let mut atn = ParserAtnBuilder::new(1);
15531        for state_number in 0..=20 {
15532            let kind = match state_number {
15533                0 | 10 | 16 => AtnStateKind::RuleStart,
15534                9 | 15 | 20 => AtnStateKind::RuleStop,
15535                _ => AtnStateKind::Basic,
15536            };
15537            let rule_index = match state_number {
15538                0..=9 => 0,
15539                10..=15 => 1,
15540                _ => 2,
15541            };
15542            assert_eq!(
15543                atn.add_state(kind, Some(rule_index))
15544                    .expect("state")
15545                    .index(),
15546                state_number
15547            );
15548        }
15549        atn.set_rule_to_start_state(vec![0, 10, 16])
15550            .expect("rule start states");
15551        atn.set_rule_to_stop_state(vec![9, 15, 20])
15552            .expect("rule stop states");
15553        atn.add_transition(
15554            1,
15555            ParserTransitionSpec::Rule {
15556                target: 10,
15557                rule_index: 1,
15558                follow_state: 8,
15559                precedence: 0,
15560            },
15561        )
15562        .expect("transition");
15563        atn.add_transition(
15564            8,
15565            ParserTransitionSpec::Atom {
15566                target: 9,
15567                label: 1,
15568            },
15569        )
15570        .expect("transition");
15571        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
15572            .expect("transition");
15573        atn.add_transition(
15574            2,
15575            ParserTransitionSpec::Rule {
15576                target: 16,
15577                rule_index: 2,
15578                follow_state: 14,
15579                precedence: 0,
15580            },
15581        )
15582        .expect("transition");
15583        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
15584            .expect("transition");
15585        finish_atn(atn)
15586    }
15587
15588    fn generated_match_recovery_atn() -> Atn {
15589        let mut atn = ParserAtnBuilder::new(2);
15590        assert_eq!(
15591            atn.add_state(AtnStateKind::RuleStart, Some(0))
15592                .expect("state")
15593                .index(),
15594            0
15595        );
15596        assert_eq!(
15597            atn.add_state(AtnStateKind::Basic, Some(0))
15598                .expect("state")
15599                .index(),
15600            1
15601        );
15602        assert_eq!(
15603            atn.add_state(AtnStateKind::Basic, Some(0))
15604                .expect("state")
15605                .index(),
15606            2
15607        );
15608        assert_eq!(
15609            atn.add_state(AtnStateKind::RuleStop, Some(0))
15610                .expect("state")
15611                .index(),
15612            3
15613        );
15614        assert_eq!(
15615            atn.add_state(AtnStateKind::RuleStart, Some(1))
15616                .expect("state")
15617                .index(),
15618            4
15619        );
15620        assert_eq!(
15621            atn.add_state(AtnStateKind::RuleStop, Some(1))
15622                .expect("state")
15623                .index(),
15624            5
15625        );
15626        atn.set_rule_to_start_state(vec![0, 4])
15627            .expect("rule start states");
15628        atn.set_rule_to_stop_state(vec![3, 5])
15629            .expect("rule stop states");
15630        atn.add_transition(
15631            1,
15632            ParserTransitionSpec::Rule {
15633                target: 4,
15634                rule_index: 1,
15635                follow_state: 2,
15636                precedence: 0,
15637            },
15638        )
15639        .expect("transition");
15640        atn.add_transition(
15641            2,
15642            ParserTransitionSpec::Atom {
15643                target: 3,
15644                label: TOKEN_EOF,
15645            },
15646        )
15647        .expect("transition");
15648        finish_atn(atn)
15649    }
15650
15651    fn complement_set_atn() -> Atn {
15652        let mut atn = ParserAtnBuilder::new(1);
15653        assert_eq!(
15654            atn.add_state(AtnStateKind::RuleStart, Some(0))
15655                .expect("state")
15656                .index(),
15657            0
15658        );
15659        assert_eq!(
15660            atn.add_state(AtnStateKind::RuleStop, Some(0))
15661                .expect("state")
15662                .index(),
15663            1
15664        );
15665        atn.set_rule_to_start_state(vec![0])
15666            .expect("rule start states");
15667        atn.set_rule_to_stop_state(vec![1])
15668            .expect("rule stop states");
15669        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
15670        atn.add_transition(
15671            0,
15672            ParserTransitionSpec::NotSet {
15673                target: 1,
15674                set: excluded,
15675            },
15676        )
15677        .expect("transition");
15678        finish_atn(atn)
15679    }
15680
15681    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
15682    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
15683    fn wildcard_then_eof_atn() -> Atn {
15684        let mut atn = ParserAtnBuilder::new(1);
15685        assert_eq!(
15686            atn.add_state(AtnStateKind::RuleStart, Some(0))
15687                .expect("state")
15688                .index(),
15689            0
15690        );
15691        assert_eq!(
15692            atn.add_state(AtnStateKind::RuleStop, Some(0))
15693                .expect("state")
15694                .index(),
15695            1
15696        );
15697        assert_eq!(
15698            atn.add_state(AtnStateKind::Basic, Some(0))
15699                .expect("state")
15700                .index(),
15701            2
15702        );
15703        atn.set_rule_to_start_state(vec![0])
15704            .expect("rule start states");
15705        atn.set_rule_to_stop_state(vec![1])
15706            .expect("rule stop states");
15707        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
15708            .expect("transition");
15709        atn.add_transition(
15710            2,
15711            ParserTransitionSpec::Atom {
15712                target: 1,
15713                label: TOKEN_EOF,
15714            },
15715        )
15716        .expect("transition");
15717        finish_atn(atn)
15718    }
15719
15720    #[test]
15721    fn parser_matches_token_and_reports_mismatch() {
15722        let source = Source {
15723            tokens: vec![
15724                TestToken::new(1).with_text("x"),
15725                TestToken::eof("parser-test", 1, 1, 1),
15726            ],
15727            index: 0,
15728        };
15729        let data = RecognizerData::new(
15730            "Mini.g4",
15731            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
15732        );
15733        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
15734        let matched = parser.match_token(1).expect("token 1 should match");
15735        assert_eq!(parser.node(matched).text(), "x");
15736        assert!(parser.match_token(1).is_err());
15737    }
15738
15739    #[test]
15740    fn parser_matches_token_sets() {
15741        let mut parser = mini_parser(vec![
15742            TestToken::new(1).with_text("x"),
15743            TestToken::eof("parser-test", 1, 1, 1),
15744        ]);
15745
15746        let matched = parser
15747            .match_set(&[(1, 1), (3, 4)])
15748            .expect("token set should match");
15749        assert_eq!(parser.node(matched).text(), "x");
15750        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
15751    }
15752
15753    #[test]
15754    fn generated_rule_api_tracks_state_and_precedence() {
15755        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15756
15757        let context = parser.enter_rule(7, 2);
15758        assert_eq!(context.rule_index(), 2);
15759        assert_eq!(parser.state(), 7);
15760        assert_eq!(
15761            parser.rule_context_stack,
15762            vec![RuleContextFrame {
15763                rule_index: 2,
15764                invoking_state: 7
15765            }]
15766        );
15767
15768        let recursive = parser.enter_recursion_rule(11, 3, 4);
15769        assert_eq!(recursive.rule_index(), 3);
15770        assert!(parser.precpred(4));
15771        assert!(parser.precpred(5));
15772        assert!(!parser.precpred(3));
15773
15774        let next = parser.push_new_recursion_context(13, 3);
15775        assert_eq!(next.invoking_state(), 13);
15776        parser.unroll_recursion_context();
15777        assert_eq!(parser.precedence_stack, vec![0]);
15778        assert_eq!(
15779            parser.rule_context_stack,
15780            vec![RuleContextFrame {
15781                rule_index: 2,
15782                invoking_state: 7
15783            }]
15784        );
15785
15786        parser.exit_rule();
15787        assert!(parser.rule_context_stack.is_empty());
15788    }
15789
15790    #[test]
15791    fn reset_rewinds_input_and_clears_parser_owned_parse_state() {
15792        let mut parser = mini_parser(vec![
15793            TestToken::new(1).with_text("x"),
15794            TestToken::eof("parser-test", 1, 1, 1),
15795        ]);
15796        let matched = parser.match_token(1).expect("token should match");
15797        assert_eq!(parser.node(matched).text(), "x");
15798        parser.record_generated_syntax_error();
15799        parser.set_int_member(7, 11);
15800        parser.set_build_parse_trees(false);
15801        parser.set_report_diagnostic_errors(true);
15802        parser.set_prediction_mode(PredictionMode::Sll);
15803        parser.set_bail_on_error(true);
15804        let _context = parser.enter_recursion_rule(9, 0, 4);
15805        parser.pending_invoking_states.push(5);
15806        parser.unknown_predicate_hits.push((0, 1));
15807        parser.unhandled_action_hits.push((0, 2));
15808
15809        parser.reset();
15810
15811        assert_eq!(parser.input.index(), 0);
15812        assert_eq!(parser.la(1), 1);
15813        assert_eq!(parser.state(), -1);
15814        assert_eq!(parser.number_of_syntax_errors(), 0);
15815        assert_eq!(parser.parse_tree_storage().node_count(), 0);
15816        assert!(parser.rule_context_stack.is_empty());
15817        assert!(parser.pending_invoking_states.is_empty());
15818        assert_eq!(parser.precedence_stack, [0]);
15819        assert!(parser.unknown_predicate_hits.is_empty());
15820        assert!(parser.unhandled_action_hits.is_empty());
15821        assert_eq!(parser.int_member(7), Some(11));
15822        assert!(!parser.build_parse_trees());
15823        assert!(parser.report_diagnostic_errors());
15824        assert_eq!(parser.prediction_mode(), PredictionMode::Sll);
15825        assert!(parser.bail_on_error());
15826    }
15827
15828    #[test]
15829    fn set_token_stream_replaces_input_and_resets_parser() {
15830        let mut parser = mini_parser(vec![
15831            TestToken::new(1).with_text("old"),
15832            TestToken::eof("parser-test", 1, 1, 1),
15833        ]);
15834        parser.consume();
15835        parser.record_generated_syntax_error();
15836        let replacement = CommonTokenStream::new(Source {
15837            tokens: vec![
15838                TestToken::new(2).with_text("new"),
15839                TestToken::eof("parser-test", 1, 1, 1),
15840            ],
15841            index: 0,
15842        });
15843
15844        parser.set_token_stream(replacement);
15845
15846        assert_eq!(parser.input.index(), 0);
15847        assert_eq!(parser.la(1), 2);
15848        assert_eq!(parser.input.text_all(), "new");
15849        assert_eq!(parser.number_of_syntax_errors(), 0);
15850    }
15851
15852    #[test]
15853    fn active_invocation_states_exclude_the_root_frame() {
15854        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15855
15856        let _root = parser.enter_rule(0, 0);
15857        assert!(parser.active_invocation_states().is_empty());
15858
15859        let marker = parser.push_invoking_state(6);
15860        let _child = parser.enter_rule(2, 1);
15861        parser.discard_invoking_state(marker);
15862        assert_eq!(parser.active_invocation_states(), [6]);
15863
15864        let marker = parser.push_invoking_state(13);
15865        let _grandchild = parser.enter_rule(4, 2);
15866        parser.discard_invoking_state(marker);
15867        assert_eq!(parser.active_invocation_states(), [13, 6]);
15868
15869        parser.exit_rule();
15870        parser.exit_rule();
15871        parser.exit_rule();
15872    }
15873
15874    #[test]
15875    fn parser_predicates_support_token_adjacency() {
15876        let mut parser = mini_parser(vec![
15877            TestToken::new(1).with_text("=").with_span(0, 0),
15878            TestToken::new(1).with_text(">").with_span(1, 1),
15879            TestToken::eof("parser-test", 2, 1, 2),
15880        ]);
15881        parser.consume();
15882        parser.consume();
15883
15884        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
15885
15886        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
15887
15888        let mut parser = mini_parser(vec![
15889            TestToken::new(1).with_text("=").with_span(0, 0),
15890            TestToken::new(1)
15891                .with_text(" ")
15892                .with_channel(HIDDEN_CHANNEL)
15893                .with_span(1, 1),
15894            TestToken::new(1).with_text(">").with_span(2, 2),
15895            TestToken::eof("parser-test", 3, 1, 3),
15896        ]);
15897        parser.consume();
15898        parser.consume();
15899
15900        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
15901    }
15902
15903    #[test]
15904    fn parser_predicates_support_context_child_text_checks() {
15905        let mut parser = mini_parser(vec![
15906            TestToken::new(1).with_text("var"),
15907            TestToken::eof("parser-test", 1, 1, 1),
15908        ]);
15909        let mut context = ParserRuleContext::new(1, 0);
15910        let mut child_context = ParserRuleContext::new(2, 0);
15911        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
15912        parser.tree.add_child(&mut child_context, terminal);
15913        let child = parser.rule_node(child_context);
15914        parser.tree.add_child(&mut context, child);
15915        let predicates = [(
15916            1,
15917            0,
15918            ParserPredicate::ContextChildRuleTextNotEquals {
15919                rule_index: 2,
15920                text: "var",
15921            },
15922        )];
15923
15924        assert!(
15925            !parser.parser_semantic_predicate_matches_with_context_and_local(
15926                &predicates,
15927                1,
15928                0,
15929                &context,
15930                0,
15931            )
15932        );
15933    }
15934
15935    #[test]
15936    fn context_expected_symbols_walks_nullable_parent_contexts() {
15937        let atn = nested_nullable_context_atn();
15938        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15939        parser.rule_context_stack = vec![
15940            RuleContextFrame {
15941                rule_index: 0,
15942                invoking_state: 0,
15943            },
15944            RuleContextFrame {
15945                rule_index: 1,
15946                invoking_state: 1,
15947            },
15948            RuleContextFrame {
15949                rule_index: 2,
15950                invoking_state: 2,
15951            },
15952        ];
15953
15954        let expected = parser.context_expected_symbols(&atn);
15955
15956        assert!(expected.contains(&1));
15957        assert!(expected.contains(&TOKEN_EOF));
15958    }
15959
15960    #[test]
15961    fn prediction_context_return_states_track_rule_stack_changes() {
15962        let atn = nested_nullable_context_atn();
15963        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
15964        parser.rule_context_stack = vec![
15965            RuleContextFrame {
15966                rule_index: 0,
15967                invoking_state: 0,
15968            },
15969            RuleContextFrame {
15970                rule_index: 1,
15971                invoking_state: 1,
15972            },
15973            RuleContextFrame {
15974                rule_index: 2,
15975                invoking_state: 2,
15976            },
15977        ];
15978
15979        let initial_version = parser.rule_context_version();
15980        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15981        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15982        assert_eq!(first, second);
15983        assert_eq!(parser.rule_context_version(), initial_version);
15984
15985        parser.exit_rule();
15986        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
15987        assert_ne!(first, after_pop);
15988        assert_ne!(parser.rule_context_version(), initial_version);
15989    }
15990
15991    #[test]
15992    fn generated_match_token_recovers_missing_token_from_context_follow() {
15993        let atn = generated_match_recovery_atn();
15994        let data = RecognizerData::new(
15995            "Mini.g4",
15996            Vocabulary::new(
15997                [None, Some("'X'"), Some("'Y'")],
15998                [None, Some("X"), Some("Y")],
15999                [None::<&str>, None, None],
16000            ),
16001        );
16002        let mut parser = BaseParser::new(
16003            CommonTokenStream::new(Source {
16004                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
16005                index: 0,
16006            }),
16007            data,
16008        );
16009        parser.rule_context_stack = vec![
16010            RuleContextFrame {
16011                rule_index: 0,
16012                invoking_state: 0,
16013            },
16014            RuleContextFrame {
16015                rule_index: 1,
16016                invoking_state: 1,
16017            },
16018        ];
16019        assert_eq!(parser.number_of_syntax_errors(), 0);
16020
16021        let node = parser
16022            .match_token_recovering(2, 5, &atn)
16023            .expect("generated match should insert missing token");
16024
16025        assert_eq!(node.children().len(), 1);
16026        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
16027        assert_eq!(
16028            node.clone()
16029                .into_child_iter()
16030                .map(|child| parser.node(child).text())
16031                .collect::<Vec<_>>(),
16032            ["<missing 'Y'>"]
16033        );
16034        // Single-token insertion synthesizes a missing token and consumes nothing,
16035        // so no EOF terminal is consumed even though lookahead is EOF.
16036        assert!(!node.consumed_eof());
16037        assert_eq!(parser.la(1), TOKEN_EOF);
16038        assert_eq!(parser.number_of_syntax_errors(), 1);
16039        assert_eq!(
16040            parser.generated_parser_diagnostics,
16041            [ParserDiagnostic {
16042                line: 1,
16043                column: 3,
16044                message: "missing 'Y' at '<EOF>'".to_owned(),
16045                offending: parser.input.lt_id(1),
16046            }]
16047        );
16048    }
16049
16050    #[test]
16051    fn generated_match_token_counts_single_token_deletion_recovery() {
16052        let atn = generated_match_recovery_atn();
16053        let data = RecognizerData::new(
16054            "Mini.g4",
16055            Vocabulary::new(
16056                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
16057                [None, Some("X"), Some("Y"), Some("Z")],
16058                [None::<&str>, None, None, None],
16059            ),
16060        );
16061        let mut parser = BaseParser::new(
16062            CommonTokenStream::new(Source {
16063                tokens: vec![
16064                    TestToken::new(3).with_text("z"),
16065                    TestToken::new(2).with_text("y"),
16066                    TestToken::eof("parser-test", 3, 1, 3),
16067                ],
16068                index: 0,
16069            }),
16070            data,
16071        );
16072
16073        let node = parser
16074            .match_token_recovering(2, 5, &atn)
16075            .expect("generated match should delete the extraneous token");
16076
16077        assert_eq!(node.children().len(), 2);
16078        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
16079        assert_eq!(parser.node(node.children()[0]).text(), "z");
16080        assert_eq!(parser.node(node.children()[1]).text(), "y");
16081        assert_eq!(
16082            node.into_child_iter()
16083                .map(|child| parser.node(child).text())
16084                .collect::<Vec<_>>(),
16085            ["z", "y"]
16086        );
16087        assert_eq!(parser.number_of_syntax_errors(), 1);
16088    }
16089
16090    #[test]
16091    fn generated_match_token_iterates_single_success_without_a_children_vec() {
16092        let atn = generated_match_recovery_atn();
16093        let data = RecognizerData::new(
16094            "Mini.g4",
16095            Vocabulary::new(
16096                [None, Some("'X'"), Some("'Y'")],
16097                [None, Some("X"), Some("Y")],
16098                [None::<&str>, None, None],
16099            ),
16100        );
16101        let mut parser = BaseParser::new(
16102            CommonTokenStream::new(Source {
16103                tokens: vec![
16104                    TestToken::new(2).with_text("y"),
16105                    TestToken::eof("parser-test", 1, 1, 1),
16106                ],
16107                index: 0,
16108            }),
16109            data,
16110        );
16111
16112        let node = parser
16113            .match_token_recovering(2, 5, &atn)
16114            .expect("generated match should consume the expected token");
16115
16116        assert_eq!(
16117            node.into_child_iter()
16118                .map(|child| parser.node(child).text())
16119                .collect::<Vec<_>>(),
16120            ["y"]
16121        );
16122        assert_eq!(parser.number_of_syntax_errors(), 0);
16123    }
16124
16125    #[test]
16126    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
16127        let atn = generated_match_recovery_atn();
16128        let data = RecognizerData::new(
16129            "Mini.g4",
16130            Vocabulary::new(
16131                [None, Some("'X'"), Some("'Y'")],
16132                [None, Some("X"), Some("Y")],
16133                [None::<&str>, None, None],
16134            ),
16135        );
16136        let mut parser = BaseParser::new(
16137            CommonTokenStream::new(Source {
16138                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
16139                index: 0,
16140            }),
16141            data,
16142        );
16143        parser.rule_context_stack = vec![
16144            RuleContextFrame {
16145                rule_index: 0,
16146                invoking_state: 0,
16147            },
16148            RuleContextFrame {
16149                rule_index: 1,
16150                invoking_state: 1,
16151            },
16152        ];
16153        let marker = parser.generated_diagnostics_checkpoint();
16154
16155        let _ = parser
16156            .match_token_recovering(2, 5, &atn)
16157            .expect("generated match should insert missing token");
16158        assert_eq!(parser.number_of_syntax_errors(), 1);
16159
16160        parser.restore_generated_diagnostics(marker);
16161
16162        assert_eq!(parser.number_of_syntax_errors(), 0);
16163        assert!(parser.generated_parser_diagnostics.is_empty());
16164    }
16165
16166    #[test]
16167    fn generated_prediction_diagnostics_use_adaptive_context() {
16168        let atn = two_alt_decision_atn();
16169        let data = RecognizerData::new(
16170            "Mini.g4",
16171            Vocabulary::new(
16172                [None, Some("'x'"), Some("'y'")],
16173                [None, Some("X"), Some("Y")],
16174                [None::<&str>, None, None],
16175            ),
16176        )
16177        .with_rule_names(["s"]);
16178        let mut parser = BaseParser::new(
16179            CommonTokenStream::new(Source {
16180                tokens: vec![
16181                    TestToken::new(1)
16182                        .with_text("x")
16183                        .with_position(1, 0)
16184                        .with_span(0, 0),
16185                    TestToken::new(2)
16186                        .with_text("y")
16187                        .with_position(1, 2)
16188                        .with_span(1, 1),
16189                    TestToken::eof("parser-test", 2, 1, 3),
16190                ],
16191                index: 0,
16192            }),
16193            data,
16194        );
16195        parser.set_report_diagnostic_errors(true);
16196
16197        parser.record_generated_prediction_diagnostic(
16198            &atn,
16199            1,
16200            &ParserAtnPrediction {
16201                alt: 1,
16202                requires_full_context: true,
16203                has_semantic_context: false,
16204                diagnostic: Some(ParserAtnPredictionDiagnostic {
16205                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
16206                    start_index: 0,
16207                    sll_stop_index: 1,
16208                    ll_stop_index: 0,
16209                    conflicting_alts: vec![1, 2],
16210                    exact: false,
16211                }),
16212            },
16213        );
16214        // Ambiguities from the default LL prediction mode are non-exact, so —
16215        // matching Java's exactOnly DiagnosticErrorListener — only the
16216        // attempting-full-context line is reported. Exact-ambiguity mode
16217        // reports the ambiguity itself.
16218        parser.record_generated_prediction_diagnostic(
16219            &atn,
16220            1,
16221            &ParserAtnPrediction {
16222                alt: 1,
16223                requires_full_context: true,
16224                has_semantic_context: false,
16225                diagnostic: Some(ParserAtnPredictionDiagnostic {
16226                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
16227                    start_index: 0,
16228                    sll_stop_index: 1,
16229                    ll_stop_index: 1,
16230                    conflicting_alts: vec![1, 2],
16231                    exact: false,
16232                }),
16233            },
16234        );
16235
16236        // The full-context/context-sensitivity diagnostic trace (order + decision + input windows)
16237        // is one snapshot rather than three ParserDiagnostic literals.
16238        insta::assert_debug_snapshot!(
16239            "generated_prediction_diagnostics_use_adaptive_context",
16240            parser.generated_parser_diagnostics
16241        );
16242    }
16243
16244    #[test]
16245    fn generated_match_not_set_recovers_empty_complement_at_eof() {
16246        let atn = complement_set_atn();
16247        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16248        parser.rule_context_stack = vec![RuleContextFrame {
16249            rule_index: 0,
16250            invoking_state: 0,
16251        }];
16252
16253        let node = parser
16254            .match_not_token_set_recovering(
16255                atn.token_set(0).expect("excluded token set"),
16256                1,
16257                1,
16258                1,
16259                &atn,
16260            )
16261            .expect("empty complement should recover at EOF");
16262
16263        assert_eq!(node.children().len(), 1);
16264        // Recovery synthesizes a missing token without consuming EOF, so the
16265        // enclosing rule must not record EOF as its stop token.
16266        assert!(!node.consumed_eof());
16267        assert_eq!(parser.la(1), TOKEN_EOF);
16268        assert_eq!(
16269            parser.generated_parser_diagnostics,
16270            [ParserDiagnostic {
16271                line: 1,
16272                column: 1,
16273                message: "missing {} at '<EOF>'".to_owned(),
16274                offending: parser.input.lt_id(1),
16275            }]
16276        );
16277    }
16278
16279    #[test]
16280    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
16281        // `start : . EOF ;` on empty input. The wildcard is modeled as an
16282        // empty-complement not-set; at EOF the follow state (the explicit EOF
16283        // match) expects EOF, so even in the start rule recovery must perform
16284        // single-token insertion (`<missing ...>`) rather than aborting — matching
16285        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
16286        let atn = wildcard_then_eof_atn();
16287        let data = RecognizerData::new(
16288            "Mini.g4",
16289            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
16290        );
16291        let mut parser = BaseParser::new(
16292            CommonTokenStream::new(Source {
16293                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
16294                index: 0,
16295            }),
16296            data,
16297        );
16298        parser.rule_context_stack = vec![RuleContextFrame {
16299            rule_index: 0,
16300            invoking_state: 0,
16301        }];
16302
16303        let node = parser
16304            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
16305            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
16306
16307        // A single `<missing ...>` error node is inserted; EOF is not consumed.
16308        assert_eq!(node.children().len(), 1);
16309        assert!(!node.consumed_eof());
16310        assert!(
16311            parser
16312                .node(node.children()[0])
16313                .text()
16314                .starts_with("<missing")
16315        );
16316        assert_eq!(parser.la(1), TOKEN_EOF);
16317        assert_eq!(
16318            parser.generated_parser_diagnostics,
16319            [ParserDiagnostic {
16320                line: 1,
16321                column: 1,
16322                message: "missing 'x' at '<EOF>'".to_owned(),
16323                offending: parser.input.lt_id(1),
16324            }]
16325        );
16326    }
16327
16328    #[test]
16329    fn generated_rule_recovery_consumes_to_parent_follow() {
16330        let atn = generated_match_recovery_atn();
16331        let data = RecognizerData::new(
16332            "Mini.g4",
16333            Vocabulary::new(
16334                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
16335                [None, Some("X"), Some("Y"), Some("Z")],
16336                [None::<&str>, None, None, None],
16337            ),
16338        );
16339        let mut parser = BaseParser::new(
16340            CommonTokenStream::new(Source {
16341                tokens: vec![
16342                    TestToken::new(3).with_text("z"),
16343                    TestToken::eof("parser-test", 1, 1, 1),
16344                ],
16345                index: 0,
16346            }),
16347            data,
16348        );
16349        let _parent = parser.enter_rule(0, 0);
16350        let marker = parser.push_invoking_state(1);
16351        let mut child = parser.enter_rule(4, 1);
16352        parser.discard_invoking_state(marker);
16353
16354        // The anchor recorded where the error was built must survive into the
16355        // dispatched diagnostic even though recovery consumes past it below.
16356        let offending = parser.input.lt_id(1);
16357        assert!(offending.is_some(), "the 'z' token should be buffered");
16358        parser.recover_generated_rule(
16359            &mut child,
16360            &atn,
16361            AntlrError::ParserError {
16362                line: 1,
16363                column: 0,
16364                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
16365                offending,
16366            },
16367        );
16368        let tree = parser.finish_rule(child, false);
16369
16370        assert_eq!(parser.la(1), TOKEN_EOF);
16371        assert_eq!(
16372            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
16373            "(a z)"
16374        );
16375        assert_eq!(parser.number_of_syntax_errors(), 1);
16376        assert_eq!(
16377            parser.generated_parser_diagnostics,
16378            [ParserDiagnostic {
16379                line: 1,
16380                column: 0,
16381                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
16382                offending,
16383            }]
16384        );
16385        parser.exit_rule();
16386    }
16387
16388    #[test]
16389    fn generated_rule_recovery_forces_progress_after_repeated_error_state() {
16390        let atn = nested_nullable_context_atn();
16391        let mut parser = mini_parser(vec![
16392            TestToken::new(1).with_text("x"),
16393            TestToken::eof("parser-test", 1, 1, 1),
16394        ]);
16395        parser.rule_context_stack = vec![
16396            RuleContextFrame {
16397                rule_index: 0,
16398                invoking_state: 0,
16399            },
16400            RuleContextFrame {
16401                rule_index: 1,
16402                invoking_state: 1,
16403            },
16404            RuleContextFrame {
16405                rule_index: 2,
16406                invoking_state: 2,
16407            },
16408        ];
16409        parser.set_state(20);
16410        let mut context = ParserRuleContext::new(2, 2);
16411
16412        parser.recover_generated_rule(
16413            &mut context,
16414            &atn,
16415            AntlrError::NoViableAlternative {
16416                input: "'x'".to_owned(),
16417            },
16418        );
16419        assert_eq!(parser.input.index(), 0);
16420
16421        parser.set_state(21);
16422        parser.recover_generated_rule(
16423            &mut context,
16424            &atn,
16425            AntlrError::NoViableAlternative {
16426                input: "'x'".to_owned(),
16427            },
16428        );
16429        assert_eq!(parser.input.index(), 0);
16430        assert_eq!(
16431            parser.generated_recovery_error_states,
16432            BTreeSet::from([20, 21])
16433        );
16434
16435        parser.set_state(20);
16436        parser.recover_generated_rule(
16437            &mut context,
16438            &atn,
16439            AntlrError::NoViableAlternative {
16440                input: "'x'".to_owned(),
16441            },
16442        );
16443
16444        assert_eq!(parser.input.index(), 1);
16445        assert_eq!(parser.la(1), TOKEN_EOF);
16446        assert!(context.has_matched_child());
16447        assert_eq!(parser.generated_recovery_error_states, BTreeSet::from([20]));
16448
16449        parser.match_eof().expect("EOF should match");
16450        assert_eq!(parser.generated_recovery_error_index, None);
16451        assert!(parser.generated_recovery_error_states.is_empty());
16452    }
16453
16454    #[test]
16455    fn greedy_ll1_alt_handles_nullable_loop_exit() {
16456        let mut body_symbols = TokenBitSet::default();
16457        body_symbols.insert(1);
16458        let entry = DecisionLookahead {
16459            transitions: vec![
16460                TransitionLookSet {
16461                    symbols: body_symbols,
16462                    nullable: false,
16463                },
16464                TransitionLookSet {
16465                    symbols: TokenBitSet::default(),
16466                    nullable: true,
16467                },
16468            ],
16469        };
16470
16471        assert_eq!(ll1_unique_alt(&entry, 2), None);
16472        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
16473        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
16474        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
16475    }
16476
16477    #[test]
16478    fn ordinary_repetition_builds_tree_in_input_order() {
16479        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16480            let mut parser = mini_parser(repeated_x_tokens(3));
16481            let tree = parser
16482                .parse_atn_rule(&atn, 0)
16483                .expect("ordinary repetition should parse");
16484
16485            let root = parser
16486                .node(tree)
16487                .as_rule()
16488                .expect("entry result should be a rule");
16489            let body_rules = root.child_rules(1).collect::<Vec<_>>();
16490            assert_eq!(root.text(), "xxx<EOF>");
16491            assert_eq!(body_rules.len(), 3);
16492            assert_eq!(
16493                body_rules
16494                    .iter()
16495                    .map(|rule| rule.start_id().expect("body start").index())
16496                    .collect::<Vec<_>>(),
16497                [0, 1, 2]
16498            );
16499            assert_eq!(
16500                body_rules
16501                    .iter()
16502                    .map(|rule| rule.stop_id().expect("body stop").index())
16503                    .collect::<Vec<_>>(),
16504                [0, 1, 2]
16505            );
16506            assert_eq!(parser.number_of_syntax_errors(), 0);
16507        }
16508    }
16509
16510    #[test]
16511    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
16512        const DEPTH: usize = 20_000;
16513
16514        std::thread::Builder::new()
16515            .name("deferred-rule-materialization".to_owned())
16516            .stack_size(256 * 1024)
16517            .spawn(|| {
16518                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16519                let mut root = FastDeferredNodeId::EMPTY;
16520                for depth in 0..DEPTH {
16521                    root = parser
16522                        .recognition_arena
16523                        .deferred_rule_node(FastDeferredRule {
16524                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
16525                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
16526                            start_index: 0,
16527                            stop_index: None,
16528                            deferred_children: root,
16529                            children: NodeSeqId::EMPTY,
16530                        });
16531                }
16532
16533                let (mut children, alt_number) =
16534                    parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
16535                assert_eq!(alt_number, 0);
16536                for expected_rule in (0..DEPTH).rev() {
16537                    let mut nodes = parser.recognition_arena.iter(children);
16538                    let node = nodes.next().expect("nested rule node");
16539                    assert!(nodes.next().is_none(), "each rule has one child");
16540                    let ArenaRecognizedNode::Rule {
16541                        rule_index,
16542                        children: nested,
16543                        ..
16544                    } = parser.recognition_arena.node(node)
16545                    else {
16546                        panic!("expected nested rule");
16547                    };
16548                    assert_eq!(rule_index as usize, expected_rule);
16549                    children = nested;
16550                }
16551                assert!(children.is_empty());
16552            })
16553            .expect("small-stack thread should start")
16554            .join()
16555            .expect("deferred rules should materialize without recursion");
16556    }
16557
16558    #[test]
16559    fn deferred_alternatives_preserve_left_recursive_contexts() {
16560        let mut parser = mini_parser(vec![
16561            TestToken::new(1).with_text("1"),
16562            TestToken::new(2).with_text("+"),
16563            TestToken::new(1).with_text("2"),
16564            TestToken::eof("parser-test", 3, 1, 3),
16565        ]);
16566        let base = parser.arena_token_node(0, false);
16567        let operator = parser.arena_token_node(1, false);
16568        let right = parser.arena_token_node(2, false);
16569
16570        let base = parser.recognition_arena.prepend(NodeSeqId::EMPTY, base);
16571        let base = parser.recognition_arena.deferred_fragment(base);
16572        let operator = parser.recognition_arena.prepend(NodeSeqId::EMPTY, operator);
16573        let operator = parser.recognition_arena.deferred_fragment(operator);
16574        let right = parser.recognition_arena.prepend(NodeSeqId::EMPTY, right);
16575        let right = parser.recognition_arena.deferred_fragment(right);
16576        let base_alt = parser.recognition_arena.deferred_alternative(1);
16577        let boundary = parser.recognition_arena.deferred_left_recursive_boundary(0);
16578        let operator_alt = parser.recognition_arena.deferred_alternative(6);
16579
16580        let mut deferred = FastDeferredNodeId::EMPTY;
16581        for fragment in [base_alt, base, boundary, operator_alt, operator, right] {
16582            deferred = parser
16583                .recognition_arena
16584                .concat_deferred_nodes(deferred, fragment);
16585        }
16586        let (nodes, root_alt_number) =
16587            parser.materialize_fast_deferred_nodes(deferred, NodeSeqId::EMPTY);
16588        let nodes = parser
16589            .recognition_arena
16590            .fold_left_recursive_boundaries(nodes);
16591
16592        let mut root = ParserRuleContext::new(0, -1);
16593        root.set_context_alt_number(root_alt_number);
16594        let mut cursor = nodes;
16595        while let Some(link) = parser.recognition_arena.link(cursor) {
16596            let child = parser
16597                .arena_recognized_node_tree(link.head, false, true)
16598                .expect("materialized child should become a public tree");
16599            parser.tree.add_child(&mut root, child);
16600            cursor = link.tail;
16601        }
16602        let tree = parser.rule_node(root);
16603        let contexts = parser
16604            .node(tree)
16605            .descendants()
16606            .filter_map(Node::as_rule)
16607            .map(|rule| {
16608                (
16609                    rule.rule_index(),
16610                    rule.alt_number(),
16611                    rule.context_alt_number(),
16612                    rule.text(),
16613                )
16614            })
16615            .collect::<Vec<_>>();
16616
16617        insta::assert_debug_snapshot!(
16618            "deferred_alternatives_preserve_left_recursive_contexts",
16619            contexts
16620        );
16621    }
16622
16623    #[test]
16624    fn fast_recognizer_preserves_labeled_left_recursive_operator_context() {
16625        let atn = labeled_left_recursive_operator_atn();
16626        let mut parser = mini_parser(vec![
16627            TestToken::new(1).with_text("a"),
16628            TestToken::new(3).with_text("+"),
16629            TestToken::new(1).with_text("b"),
16630            TestToken::eof("parser-test", 3, 1, 3),
16631        ]);
16632
16633        let (tree, _) = parser
16634            .parse_atn_rule_with_runtime_options(
16635                &atn,
16636                0,
16637                ParserRuntimeOptions {
16638                    track_context_alt_numbers: true,
16639                    ..ParserRuntimeOptions::default()
16640                },
16641            )
16642            .expect("labeled left-recursive addition should parse");
16643        let contexts = parser
16644            .node(tree)
16645            .descendants()
16646            .filter_map(Node::as_rule)
16647            .map(|rule| {
16648                let operator = rule
16649                    .children()
16650                    .next()
16651                    .and_then(Node::as_rule)
16652                    .is_some_and(|child| child.rule_index() == rule.rule_index());
16653                (operator, rule.context_alt_number(), rule.text())
16654            })
16655            .collect::<Vec<_>>();
16656
16657        insta::assert_debug_snapshot!(
16658            "fast_recognizer_preserves_labeled_left_recursive_operator_context",
16659            contexts
16660        );
16661        assert!(!parser.recognition_arena.deferred_nodes.is_empty());
16662        assert_eq!(parser.number_of_syntax_errors(), 0);
16663    }
16664
16665    #[test]
16666    fn deeply_nested_rule_calls_grow_the_stack() {
16667        const DEPTH: usize = 4_096;
16668        const STACK_SIZE: usize = 256 * 1024;
16669        let atn = nested_rule_chain_atn(DEPTH);
16670        std::thread::Builder::new()
16671            .name("nested-adaptive-set-rules".to_owned())
16672            .stack_size(STACK_SIZE)
16673            .spawn(move || {
16674                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
16675                parser.set_build_parse_trees(false);
16676                // This test isolates recognizer depth from the separately
16677                // cached FIRST-set metadata walk.
16678                parser.fast_first_set_prefilter = false;
16679                parser
16680                    .parse_atn_rule(&atn, 0)
16681                    .expect("nested rule chain should grow the native stack");
16682                assert_eq!(parser.input.index(), 1);
16683            })
16684            .expect("small-stack thread should start")
16685            .join()
16686            .expect("nested rule chain should not overflow its stack");
16687    }
16688
16689    #[test]
16690    fn deeply_nested_branching_rules_grow_the_stack() {
16691        const DEPTH: usize = 4_096;
16692        const STACK_SIZE: usize = 256 * 1024;
16693        let atn = nested_rule_graph_atn(DEPTH, true, false);
16694        std::thread::Builder::new()
16695            .name("nested-branching-rules".to_owned())
16696            .stack_size(STACK_SIZE)
16697            .spawn(move || {
16698                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
16699                parser.set_build_parse_trees(false);
16700                parser
16701                    .parse_atn_rule(&atn, 0)
16702                    .expect("branching rule chain should grow the native stack");
16703                assert_eq!(parser.input.index(), 1);
16704            })
16705            .expect("small-stack thread should start")
16706            .join()
16707            .expect("branching rule chain should not overflow its stack");
16708    }
16709
16710    #[test]
16711    fn deeply_nested_rule_follows_grow_the_stack() {
16712        const DEPTH: usize = 4_096;
16713        const STACK_SIZE: usize = 256 * 1024;
16714        let atn = nested_rule_graph_atn(DEPTH, false, true);
16715        std::thread::Builder::new()
16716            .name("nested-rule-follows".to_owned())
16717            .stack_size(STACK_SIZE)
16718            .spawn(move || {
16719                let mut parser = mini_parser(repeated_x_tokens(DEPTH));
16720                parser.set_build_parse_trees(false);
16721                parser.fast_first_set_prefilter = false;
16722                parser
16723                    .parse_atn_rule(&atn, 0)
16724                    .expect("rule follow chain should grow the native stack");
16725                assert_eq!(parser.input.index(), DEPTH);
16726            })
16727            .expect("small-stack thread should start")
16728            .join()
16729            .expect("nested rule follow chain should not overflow its stack");
16730    }
16731
16732    #[test]
16733    fn deeply_nested_recovery_grows_the_stack() {
16734        const DEPTH: usize = 4_096;
16735        const STACK_SIZE: usize = 256 * 1024;
16736        let atn = nested_rule_chain_atn(DEPTH);
16737        std::thread::Builder::new()
16738            .name("nested-rule-recovery".to_owned())
16739            .stack_size(STACK_SIZE)
16740            .spawn(move || {
16741                let mut parser = mini_parser(vec![
16742                    TestToken::new(2).with_text("z"),
16743                    TestToken::new(1).with_text("x"),
16744                    TestToken::eof("parser-test", 2, 1, 2),
16745                ]);
16746                parser.set_build_parse_trees(false);
16747                parser.fast_first_set_prefilter = false;
16748                parser
16749                    .parse_atn_rule(&atn, 0)
16750                    .expect("nested recovery should grow the native stack");
16751                assert_eq!(parser.input.index(), 2);
16752                assert_eq!(parser.number_of_syntax_errors(), 1);
16753            })
16754            .expect("small-stack thread should start")
16755            .join()
16756            .expect("nested rule recovery should not overflow its stack");
16757    }
16758
16759    #[test]
16760    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
16761        const REPETITIONS: usize = 64;
16762
16763        let atn = ambiguous_ordinary_star_loop_atn();
16764        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16765        let tree = parser
16766            .parse_atn_rule(&atn, 0)
16767            .expect("ambiguous ordinary repetition should parse");
16768
16769        let root = parser
16770            .node(tree)
16771            .as_rule()
16772            .expect("entry result should be a rule");
16773        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
16774        assert_eq!(parser.input.index(), REPETITIONS);
16775        assert!(
16776            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
16777            "equivalent segmentations should keep deferred storage linear"
16778        );
16779        assert_eq!(parser.number_of_syntax_errors(), 0);
16780    }
16781
16782    #[test]
16783    fn long_ordinary_repetition_does_not_consume_native_stack() {
16784        const REPETITIONS: usize = 20_000;
16785
16786        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16787            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16788            parser.set_build_parse_trees(false);
16789            parser
16790                .parse_atn_rule(&atn, 0)
16791                .expect("long ordinary repetition should parse");
16792
16793            assert_eq!(parser.input.index(), REPETITIONS);
16794            assert_eq!(parser.number_of_syntax_errors(), 0);
16795        }
16796    }
16797
16798    #[test]
16799    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
16800        const REPETITIONS: usize = 2_000;
16801        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
16802
16803        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
16804            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
16805            let tree = parser
16806                .parse_atn_rule(&atn, 0)
16807                .expect("long rule 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(), expected_text);
16814            assert_eq!(root.child_rules(1).count(), REPETITIONS);
16815            let first_body = root.child_rules(1).next().expect("first body rule");
16816            let last_body = root.child_rules(1).next_back().expect("last body rule");
16817            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
16818            assert_eq!(
16819                last_body.stop_id().expect("last body stop").index(),
16820                REPETITIONS - 1
16821            );
16822
16823            let stats = parser.recognition_arena_stats();
16824            assert_eq!(
16825                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
16826                (REPETITIONS, REPETITIONS, 0)
16827            );
16828            assert_eq!(
16829                (stats.total_links, stats.live_links, stats.dead_links),
16830                (REPETITIONS, REPETITIONS, 0)
16831            );
16832            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
16833            assert_eq!(
16834                parser.recognition_arena.deferred_nodes.len(),
16835                REPETITIONS * 2 - 1
16836            );
16837            assert_eq!(parser.number_of_syntax_errors(), 0);
16838        }
16839    }
16840
16841    #[test]
16842    fn clean_memo_probe_selects_sparse_promote_and_reprobe_modes() {
16843        let key = |state_number| FastRecognizeKey {
16844            state_number,
16845            stop_state: 10,
16846            index: state_number,
16847            rule_start_index: 0,
16848            decision_start_index: None,
16849            precedence: 0,
16850            recovery_symbols_id: 0,
16851            recovery_state: None,
16852        };
16853
16854        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16855        for state_number in 0..(CLEAN_MEMO_PROBE_LIMIT - 1) {
16856            assert!(sparse.clean_memo_enabled_for_key(&key(state_number)));
16857        }
16858        assert!(!sparse.clean_memo_enabled_for_key(&key(CLEAN_MEMO_PROBE_LIMIT)));
16859        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Sparse);
16860
16861        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
16862        let repeated = key(1);
16863        for _ in 0..=CLEAN_MEMO_REPEAT_LIMIT {
16864            assert!(promote.clean_memo_enabled_for_key(&repeated));
16865        }
16866        assert_eq!(promote.clean_memo_mode, CleanMemoMode::Promote);
16867
16868        for _ in 1..CLEAN_MEMO_REPROBE_INTERVAL {
16869            assert!(!sparse.clean_memo_enabled_for_key(&repeated));
16870        }
16871        assert!(sparse.clean_memo_enabled_for_key(&repeated));
16872        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Probe);
16873        for _ in 0..CLEAN_MEMO_REPEAT_LIMIT {
16874            assert!(sparse.clean_memo_enabled_for_key(&repeated));
16875        }
16876        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Promote);
16877    }
16878
16879    #[test]
16880    fn fast_recognize_memo_capacity_scales_from_small_floor_to_bounded_maximum() {
16881        assert_eq!(
16882            fast_recognize_memo_capacity(0),
16883            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
16884        );
16885        assert_eq!(
16886            fast_recognize_memo_capacity(FAST_RECOGNIZE_MIN_MEMO_CAPACITY / 8),
16887            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
16888        );
16889        assert_eq!(fast_recognize_memo_capacity(1_000), 8_000);
16890        assert_eq!(
16891            fast_recognize_memo_capacity(usize::MAX),
16892            FAST_RECOGNIZE_MAX_MEMO_CAPACITY
16893        );
16894    }
16895
16896    #[test]
16897    fn fast_recognize_scratch_reuses_small_tables_and_releases_oversized_memo() {
16898        let mut scratch = FastRecognizeTopScratch::default();
16899        scratch.prepare(FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
16900        let retained_capacity = scratch.memo.capacity();
16901        assert!(retained_capacity >= FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
16902        assert!(retained_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16903
16904        let larger_capacity = retained_capacity + 1;
16905        scratch.prepare(larger_capacity);
16906        let grown_capacity = scratch.memo.capacity();
16907        assert!(grown_capacity >= larger_capacity);
16908        assert!(grown_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16909
16910        scratch.memo.insert(
16911            FastRecognizeKey {
16912                state_number: 0,
16913                stop_state: 0,
16914                index: 0,
16915                rule_start_index: 0,
16916                decision_start_index: None,
16917                precedence: 0,
16918                recovery_symbols_id: 0,
16919                recovery_state: None,
16920            },
16921            Rc::from([FastRecognizeOutcome {
16922                index: 0,
16923                consumed_eof: false,
16924                diagnostics: DiagnosticSeqId::EMPTY,
16925                deferred_nodes: FastDeferredNodeId::EMPTY,
16926                nodes: NodeSeqId::EMPTY,
16927            }]),
16928        );
16929        scratch.release_oversized_memo();
16930        assert!(scratch.memo.is_empty());
16931        assert_eq!(scratch.memo.capacity(), grown_capacity);
16932
16933        scratch
16934            .memo
16935            .reserve(FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY * 2);
16936        assert!(scratch.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
16937
16938        scratch.release_oversized_memo();
16939        assert!(scratch.memo.is_empty());
16940        assert_eq!(scratch.memo.capacity(), 0);
16941    }
16942
16943    #[test]
16944    fn clean_empty_multi_alt_outcomes_are_memoized() {
16945        let mut atn = ParserAtnBuilder::new(2);
16946        assert_eq!(
16947            atn.add_state(AtnStateKind::RuleStart, Some(0))
16948                .expect("state")
16949                .index(),
16950            0
16951        );
16952        assert_eq!(
16953            atn.add_state(AtnStateKind::BlockStart, Some(0))
16954                .expect("state")
16955                .index(),
16956            1
16957        );
16958        assert_eq!(
16959            atn.add_state(AtnStateKind::RuleStop, Some(0))
16960                .expect("state")
16961                .index(),
16962            2
16963        );
16964        atn.set_rule_to_start_state(vec![0])
16965            .expect("rule start states");
16966        atn.set_rule_to_stop_state(vec![2])
16967            .expect("rule stop states");
16968        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
16969            .expect("transition");
16970        atn.add_transition(
16971            1,
16972            ParserTransitionSpec::Atom {
16973                target: 2,
16974                label: 1,
16975            },
16976        )
16977        .expect("transition");
16978        atn.add_transition(
16979            1,
16980            ParserTransitionSpec::Atom {
16981                target: 2,
16982                label: 2,
16983            },
16984        )
16985        .expect("transition");
16986        let atn = finish_atn(atn);
16987
16988        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
16989        parser.fast_recovery_enabled = false;
16990        let mut visiting = FxHashSet::default();
16991        let mut memo = FxHashMap::default();
16992        let mut expected = ExpectedTokens::default();
16993        let outcomes = parser.recognize_state_fast(
16994            &atn,
16995            FastRecognizeRequest {
16996                state_number: 1,
16997                stop_state: 2,
16998                index: 0,
16999                rule_start_index: 0,
17000                decision_start_index: None,
17001                precedence: 0,
17002                depth: 0,
17003                recovery_symbols: parser.empty_recovery_symbols(),
17004                recovery_state: None,
17005            },
17006            FastRecognizeScratch {
17007                predicate_context: None,
17008                visiting: &mut visiting,
17009                memo: &mut memo,
17010                expected: &mut expected,
17011                native_depth: 0,
17012            },
17013        );
17014
17015        assert!(outcomes.is_empty());
17016        assert_eq!(memo.len(), 1);
17017        assert!(memo.values().next().expect("memo entry").is_empty());
17018
17019        parser.clean_memo_mode = CleanMemoMode::Sparse;
17020        visiting.clear();
17021        memo.clear();
17022        expected = ExpectedTokens::default();
17023        let sparse_outcomes = parser.recognize_state_fast(
17024            &atn,
17025            FastRecognizeRequest {
17026                state_number: 1,
17027                stop_state: 2,
17028                index: 0,
17029                rule_start_index: 0,
17030                decision_start_index: None,
17031                precedence: 0,
17032                depth: 0,
17033                recovery_symbols: parser.empty_recovery_symbols(),
17034                recovery_state: None,
17035            },
17036            FastRecognizeScratch {
17037                predicate_context: None,
17038                visiting: &mut visiting,
17039                memo: &mut memo,
17040                expected: &mut expected,
17041                native_depth: 0,
17042            },
17043        );
17044
17045        assert!(sparse_outcomes.is_empty());
17046        assert!(memo.is_empty());
17047    }
17048
17049    #[test]
17050    fn wildcard_matches_non_eof_only() {
17051        let mut parser = mini_parser(vec![
17052            TestToken::new(1).with_text("x"),
17053            TestToken::eof("parser-test", 1, 1, 1),
17054        ]);
17055        let matched = parser.match_wildcard().expect("wildcard");
17056        assert_eq!(parser.node(matched).text(), "x");
17057        assert!(parser.match_wildcard().is_err());
17058    }
17059
17060    #[test]
17061    fn add_parse_child_records_match_even_without_tree_building() {
17062        // `sync_decision`'s "is the current context empty" flag must reflect real
17063        // matches, not parse-tree children: when `build_parse_trees(false)`,
17064        // `children` stays empty but `has_matched_child` must still flip so nested
17065        // recovery does not wrongly suppress single-token deletion.
17066        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
17067        let token = TestToken::new(1).with_text("x");
17068
17069        parser.set_build_parse_trees(false);
17070        let mut ctx = ParserRuleContext::new(0, 0);
17071        assert!(!ctx.has_matched_child());
17072        let child = parser.terminal_tree(token.id);
17073        parser.add_parse_child(&mut ctx, child);
17074        // Tree building is off, so no child is stored...
17075        assert_eq!(ctx.child_count(), 0);
17076        assert_eq!(parser.parse_tree_storage().node_count(), 0);
17077        // ...but the match is recorded, so the context is no longer "empty".
17078        assert!(ctx.has_matched_child());
17079
17080        // With tree building on, the child is stored and the match is recorded.
17081        parser.set_build_parse_trees(true);
17082        let mut ctx = ParserRuleContext::new(0, 0);
17083        let child = parser.terminal_tree(token.id);
17084        parser.add_parse_child(&mut ctx, child);
17085        assert_eq!(ctx.child_count(), 1);
17086        assert!(ctx.has_matched_child());
17087    }
17088
17089    #[test]
17090    fn disabled_tree_building_does_not_grow_flat_storage() {
17091        let mut parser = mini_parser(vec![
17092            TestToken::new(1).with_text("x"),
17093            TestToken::new(1).with_text("y"),
17094            TestToken::eof("parser-test", 2, 1, 2),
17095        ]);
17096        parser.set_build_parse_trees(false);
17097        let mut context = ParserRuleContext::new(0, -1);
17098
17099        for _ in 0..2 {
17100            let child = parser.match_token(1).expect("token should match");
17101            parser.add_parse_child(&mut context, child);
17102        }
17103        let current = parser.input.lt_id(1).expect("EOF token");
17104        let error = parser.error_tree(current);
17105        parser.add_parse_child(&mut context, error);
17106        let root = parser.rule_node(context);
17107
17108        assert_eq!(
17109            parser.parse_tree_storage().stats(),
17110            ParseTreeStats::default()
17111        );
17112        assert!(
17113            parser
17114                .parse_tree_storage()
17115                .node(parser.token_store(), root)
17116                .is_none(),
17117            "the no-tree sentinel must not resolve to stored data"
17118        );
17119    }
17120
17121    #[test]
17122    fn disabled_tree_building_skips_recognition_rule_node_storage() {
17123        let atn = ordinary_star_loop_atn();
17124        let mut parser = mini_parser(repeated_x_tokens(3));
17125        parser.set_build_parse_trees(false);
17126
17127        parser
17128            .parse_atn_rule(&atn, 0)
17129            .expect("ordinary repetition should parse without a tree");
17130
17131        assert_eq!(parser.input.index(), 3);
17132        assert!(parser.recognition_arena.nodes.is_empty());
17133        assert!(parser.recognition_arena.seq_links.is_empty());
17134        assert!(parser.recognition_arena.deferred_nodes.is_empty());
17135        assert!(parser.recognition_arena.deferred_rules.is_empty());
17136        assert!(!parser.fast_token_nodes_enabled);
17137        assert!(parser.fast_recognize_scratch.memo.is_empty());
17138    }
17139
17140    #[test]
17141    fn parser_interprets_simple_atn_rule() {
17142        let atn = token_then_eof_atn();
17143        let mut parser = mini_parser(vec![
17144            TestToken::new(1).with_text("x"),
17145            TestToken::eof("parser-test", 1, 1, 1),
17146        ]);
17147
17148        let tree = parser
17149            .parse_atn_rule(&atn, 0)
17150            .expect("artificial parser rule should parse");
17151        assert_eq!(parser.node(tree).text(), "x<EOF>");
17152        assert_eq!(parser.number_of_syntax_errors(), 0);
17153        assert_eq!(
17154            parser
17155                .node(tree)
17156                .first_rule_stop(0)
17157                .expect("rule should stop at EOF")
17158                .token_type(),
17159            TOKEN_EOF
17160        );
17161
17162        let mut parser = mini_parser(vec![
17163            TestToken::new(1).with_text("x"),
17164            TestToken::eof("parser-test", 1, 1, 1),
17165        ]);
17166        let (tree, actions) = parser
17167            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17168            .expect("runtime-option parser rule should parse");
17169        assert!(actions.is_empty());
17170        assert_eq!(
17171            parser
17172                .node(tree)
17173                .first_rule_stop(0)
17174                .expect("rule should stop at EOF")
17175                .token_type(),
17176            TOKEN_EOF
17177        );
17178    }
17179
17180    #[test]
17181    fn runtime_options_default_ignores_noop_action_transitions() {
17182        let atn = noop_action_then_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, actions) = parser
17189            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17190            .expect("no-op parser action should not force action replay");
17191
17192        assert_eq!(parser.node(tree).text(), "x<EOF>");
17193        assert!(
17194            actions.is_empty(),
17195            "action_index=None transitions are ANTLR metadata, not replay actions"
17196        );
17197        assert_eq!(parser.number_of_syntax_errors(), 0);
17198    }
17199
17200    #[test]
17201    fn parser_exposes_buffered_token_stream_after_parse() {
17202        let atn = token_then_eof_atn();
17203        let mut parser = mini_parser(vec![
17204            TestToken::new(1).with_text("x"),
17205            TestToken::eof("parser-test", 1, 1, 1),
17206        ]);
17207
17208        let tree = parser
17209            .parse_atn_rule(&atn, 0)
17210            .expect("artificial parser rule should parse");
17211        assert_eq!(parser.node(tree).text(), "x<EOF>");
17212
17213        let stream = parser.token_stream();
17214        let source_index_after_parse = stream.token_source().index;
17215        let buffered = stream.tokens().collect::<Vec<_>>();
17216        assert_eq!(buffered.len(), 2);
17217        assert_eq!(buffered[0].text(), Some("x"));
17218        assert_eq!(buffered[0].token_id().index(), 0);
17219        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
17220        assert_eq!(stream.token_source().index, source_index_after_parse);
17221        drop(buffered);
17222
17223        let stream = parser.into_token_stream();
17224        assert_eq!(stream.token_source().index, source_index_after_parse);
17225        assert_eq!(
17226            stream.tokens().next().expect("first token").text(),
17227            Some("x")
17228        );
17229        assert_eq!(
17230            stream.tokens().nth(1).expect("EOF token").token_type(),
17231            TOKEN_EOF
17232        );
17233    }
17234
17235    #[test]
17236    fn parsed_file_exposes_all_buffered_tokens() {
17237        let atn = token_then_eof_atn();
17238        let mut parser = mini_parser(vec![
17239            TestToken::new(99)
17240                .with_text(" comment")
17241                .with_channel(HIDDEN_CHANNEL),
17242            TestToken::new(1).with_text("x"),
17243            TestToken::eof("parser-test", 9, 1, 9),
17244        ]);
17245
17246        let tree = parser
17247            .parse_atn_rule(&atn, 0)
17248            .expect("artificial parser rule should parse");
17249        let parsed = parser.into_parsed_file(tree);
17250
17251        // Snapshot the full buffered stream — hidden-channel comment, default-channel token, EOF —
17252        // as (type, channel, text) triples; contents make the count self-evident.
17253        insta::assert_debug_snapshot!(
17254            "parsed_file_exposes_all_buffered_tokens",
17255            parsed
17256                .tokens()
17257                .iter()
17258                .map(|token| (token.token_type(), token.channel(), token.text()))
17259                .collect::<Vec<_>>()
17260        );
17261        assert_eq!(parsed.tokens().into_iter().count(), 3);
17262    }
17263
17264    #[test]
17265    fn parser_syntax_error_count_tracks_interpreted_recovery() {
17266        let atn = token_then_eof_atn();
17267        let mut parser = mini_parser(vec![
17268            TestToken::new(1).with_text("x"),
17269            TestToken::new(2).with_text("y"),
17270            TestToken::eof("parser-test", 2, 1, 2),
17271        ]);
17272
17273        let tree = parser
17274            .parse_atn_rule(&atn, 0)
17275            .expect("invalid token should recover into an error node");
17276
17277        assert_eq!(parser.number_of_syntax_errors(), 1);
17278        assert_eq!(
17279            parser
17280                .node(tree)
17281                .first_error_token()
17282                .expect("recovery should embed an error token")
17283                .text(),
17284            Some("y")
17285        );
17286    }
17287
17288    #[test]
17289    fn failed_interpreted_parse_notifies_error_listener() {
17290        let atn = token_then_eof_atn();
17291        let mut parser = mini_parser(vec![
17292            TestToken::new(2)
17293                .with_text("y")
17294                .with_span(0, 0)
17295                .with_position(3, 5),
17296            TestToken::eof("parser-test", 1, 1, 1),
17297        ]);
17298        parser.remove_error_listeners();
17299        let diagnostics = Arc::new(Mutex::new(Vec::new()));
17300        parser.add_error_listener(RecordingErrorListener {
17301            diagnostics: Arc::clone(&diagnostics),
17302        });
17303
17304        let error = parser
17305            .parse_atn_rule(&atn, 0)
17306            .expect_err("start-rule mismatch should remain a parser error");
17307
17308        assert_eq!(parser.number_of_syntax_errors(), 1);
17309        assert!(matches!(&error, AntlrError::ParserError { .. }));
17310        insta::assert_debug_snapshot!(
17311            "failed_interpreted_parse_notifies_error_listener",
17312            *diagnostics.lock().expect("recorded diagnostics lock")
17313        );
17314    }
17315
17316    #[test]
17317    fn adaptive_direct_rule_uses_simulator_decision() {
17318        let atn = two_alt_decision_atn();
17319        let mut simulator = ParserAtnSimulator::new(&atn);
17320        let mut parser = mini_parser(vec![
17321            TestToken::new(2).with_text("y"),
17322            TestToken::eof("parser-test", 1, 1, 1),
17323        ]);
17324
17325        let tree = parser
17326            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
17327            .expect("direct adaptive rule should parse");
17328
17329        assert_eq!(parser.node(tree).text(), "y");
17330        assert_eq!(parser.input.index(), 1);
17331    }
17332
17333    #[test]
17334    fn adaptive_direct_rule_restores_input_on_fallback() {
17335        let atn = predicate_after_token_atn();
17336        let mut simulator = ParserAtnSimulator::new(&atn);
17337        let mut parser = mini_parser(vec![
17338            TestToken::new(1).with_text("x"),
17339            TestToken::new(2).with_text("y"),
17340            TestToken::eof("parser-test", 2, 1, 2),
17341        ]);
17342
17343        let tree = parser
17344            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
17345            .expect("fallback recognizer should parse");
17346
17347        assert_eq!(parser.node(tree).text(), "xy");
17348        assert_eq!(parser.input.index(), 2);
17349        let stats = parser.parse_tree_storage().stats();
17350        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
17351        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
17352        assert_eq!(stats.scratch_links, 0);
17353    }
17354
17355    #[test]
17356    fn unknown_predicate_policy_defaults_to_assume_true() {
17357        let atn = predicate_after_token_atn();
17358        let mut parser = mini_parser(vec![
17359            TestToken::new(1).with_text("x"),
17360            TestToken::new(2).with_text("y"),
17361            TestToken::eof("parser-test", 2, 1, 2),
17362        ]);
17363
17364        let (tree, _) = parser
17365            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17366            .expect("unknown predicate should pass under the default policy");
17367
17368        assert_eq!(parser.node(tree).text(), "xy");
17369        assert_eq!(parser.number_of_syntax_errors(), 0);
17370    }
17371
17372    #[test]
17373    fn private_context_alt_tracking_keeps_fast_predicate_recognition() {
17374        let atn = predicate_gated_same_lookahead_atn([0, 1]);
17375        let mut parser = mini_parser(vec![
17376            TestToken::new(1).with_text("x"),
17377            TestToken::eof("parser-test", 1, 1, 1),
17378        ]);
17379
17380        let (tree, _) = parser
17381            .parse_atn_rule_with_runtime_options(
17382                &atn,
17383                0,
17384                ParserRuntimeOptions {
17385                    predicates: &[
17386                        (0, 0, ParserPredicate::False),
17387                        (0, 1, ParserPredicate::True),
17388                    ],
17389                    track_context_alt_numbers: true,
17390                    ..ParserRuntimeOptions::default()
17391                },
17392            )
17393            .expect("the second predicate-gated alternative should match");
17394
17395        let root = parser.node(tree).as_rule().expect("entry result is a rule");
17396        insta::assert_debug_snapshot!(
17397            "private_context_alt_tracking_keeps_fast_predicate_recognition",
17398            (root.alt_number(), root.context_alt_number(), root.text())
17399        );
17400        assert_eq!(parser.number_of_syntax_errors(), 0);
17401        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
17402        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
17403    }
17404
17405    #[test]
17406    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
17407        // A generated parent may record an unknown-predicate coordinate, then
17408        // descend into an interpreted child. The child's interpreter entry must
17409        // not wipe the parent's recorded hit before the top-level surfaces it.
17410        let atn = token_then_eof_atn();
17411        let mut parser = mini_parser(vec![
17412            TestToken::new(1).with_text("x"),
17413            TestToken::eof("parser-test", 1, 1, 1),
17414        ]);
17415
17416        // Simulate the parent having recorded a fail-loud coordinate.
17417        parser.unknown_predicate_hits.push((7, 3));
17418
17419        // Run an interpreted child parse that records no coordinate of its own.
17420        parser
17421            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17422            .expect("child rule parses");
17423
17424        // The parent's coordinate must still be present for the top-level entry.
17425        let error = parser
17426            .take_unknown_semantic_error()
17427            .expect("parent's recorded coordinate must survive the nested interpreted parse");
17428        let AntlrError::Unsupported(message) = error else {
17429            panic!("expected AntlrError::Unsupported, got {error:?}");
17430        };
17431        assert!(message.contains("pred_index=3"), "message: {message}");
17432    }
17433
17434    #[test]
17435    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
17436        let atn = predicate_after_token_atn();
17437        let mut parser = mini_parser(vec![
17438            TestToken::new(1).with_text("x"),
17439            TestToken::new(2).with_text("y"),
17440            TestToken::eof("parser-test", 2, 1, 2),
17441        ]);
17442
17443        let result = parser.parse_atn_rule_with_runtime_options(
17444            &atn,
17445            0,
17446            ParserRuntimeOptions {
17447                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
17448                ..ParserRuntimeOptions::default()
17449            },
17450        );
17451
17452        assert!(
17453            result.is_err(),
17454            "the only path is predicate-guarded, so assume-false must fail the parse"
17455        );
17456    }
17457
17458    #[test]
17459    fn predicate_failure_message_keeps_semantic_recovery_path() {
17460        let atn = predicate_after_token_atn();
17461        let mut parser = mini_parser(vec![
17462            TestToken::new(1).with_text("x"),
17463            TestToken::new(2).with_text("y"),
17464            TestToken::eof("parser-test", 2, 1, 2),
17465        ]);
17466
17467        let (tree, _) = parser
17468            .parse_atn_rule_with_runtime_options(
17469                &atn,
17470                0,
17471                ParserRuntimeOptions {
17472                    predicates: &[(
17473                        0,
17474                        0,
17475                        ParserPredicate::FalseWithMessage {
17476                            message: "predicate rejected input",
17477                        },
17478                    )],
17479                    ..ParserRuntimeOptions::default()
17480                },
17481            )
17482            .expect("failure-message predicates recover through the semantic interpreter");
17483
17484        assert_eq!(parser.node(tree).text(), "xy");
17485        assert_eq!(parser.number_of_syntax_errors(), 1);
17486        assert!(
17487            parser.fast_predicate_cache.is_empty(),
17488            "failure-message predicates need the semantic interpreter's recovery outcome"
17489        );
17490    }
17491
17492    #[test]
17493    fn unknown_predicate_policy_error_names_the_coordinate() {
17494        let atn = predicate_after_token_atn();
17495        let mut parser = mini_parser(vec![
17496            TestToken::new(1).with_text("x"),
17497            TestToken::new(2).with_text("y"),
17498            TestToken::eof("parser-test", 2, 1, 2),
17499        ]);
17500
17501        let error = parser
17502            .parse_atn_rule_with_runtime_options(
17503                &atn,
17504                0,
17505                ParserRuntimeOptions {
17506                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17507                    ..ParserRuntimeOptions::default()
17508                },
17509            )
17510            .expect_err("evaluating an unknown predicate under Error policy must fail");
17511
17512        let AntlrError::Unsupported(message) = error else {
17513            panic!("expected AntlrError::Unsupported, got {error:?}");
17514        };
17515        assert!(
17516            message.contains("unsupported semantic predicate"),
17517            "message should name the failure class: {message}"
17518        );
17519        assert!(
17520            message.contains("pred_index=0"),
17521            "message should carry the coordinate: {message}"
17522        );
17523    }
17524
17525    #[test]
17526    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
17527        // A parser reused after a fail-loud parse must not carry the old
17528        // coordinates into a later parse. The fail-loud return keeps the hits
17529        // (so a generated parent can surface a recovered child's coordinate),
17530        // and the next parse's entry stashes/replaces them, so a subsequent
17531        // clean parse surfaces no stale error.
17532        let atn = predicate_after_token_atn();
17533        let mut parser = mini_parser(vec![
17534            TestToken::new(1).with_text("x"),
17535            TestToken::new(2).with_text("y"),
17536            TestToken::eof("parser-test", 2, 1, 2),
17537        ]);
17538
17539        parser
17540            .parse_atn_rule_with_runtime_options(
17541                &atn,
17542                0,
17543                ParserRuntimeOptions {
17544                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17545                    ..ParserRuntimeOptions::default()
17546                },
17547            )
17548            .expect_err("first parse fails loud under the Error policy");
17549
17550        // The failed parse kept its coordinate on the parser (so a generated
17551        // parent could surface a recovered child). A top-level reuse resets the
17552        // hits — generated parsers call `reset_unknown_semantic_hits` at their
17553        // public entry; direct interpreter-API callers do the same.
17554        parser.reset_unknown_semantic_hits();
17555        assert!(
17556            parser.take_unknown_semantic_error().is_none(),
17557            "reset must drop stale unknown-predicate coordinates before a reused parse"
17558        );
17559    }
17560
17561    #[derive(Debug, Default)]
17562    struct RecordingHooks {
17563        predicates: Vec<(usize, usize, usize, Option<String>)>,
17564        actions: Vec<(usize, String, Option<String>)>,
17565        action_trees: Vec<Option<String>>,
17566    }
17567
17568    impl SemanticHooks for RecordingHooks {
17569        fn sempred<S>(
17570            &mut self,
17571            ctx: &mut ParserSemCtx<'_, S>,
17572            rule_index: usize,
17573            pred_index: usize,
17574        ) -> Option<bool>
17575        where
17576            S: TokenSource,
17577        {
17578            self.predicates.push((
17579                ctx.input_index(),
17580                rule_index,
17581                pred_index,
17582                ctx.token_text(1)
17583                    .and_then(|token| token.text().map(str::to_owned)),
17584            ));
17585            Some(true)
17586        }
17587
17588        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
17589        where
17590            S: TokenSource,
17591        {
17592            self.actions.push((
17593                action.source_state(),
17594                ctx.action_text(),
17595                ctx.rule_name().map(str::to_owned),
17596            ));
17597            self.action_trees.push(ctx.tree().map(Node::text));
17598            true
17599        }
17600    }
17601
17602    #[derive(Debug, Default)]
17603    struct RejectingPredicateHooks {
17604        predicates: Vec<(usize, usize, usize, Option<String>)>,
17605    }
17606
17607    impl SemanticHooks for RejectingPredicateHooks {
17608        fn sempred<S>(
17609            &mut self,
17610            ctx: &mut ParserSemCtx<'_, S>,
17611            rule_index: usize,
17612            pred_index: usize,
17613        ) -> Option<bool>
17614        where
17615            S: TokenSource,
17616        {
17617            self.predicates.push((
17618                ctx.input_index(),
17619                rule_index,
17620                pred_index,
17621                ctx.token_text(1)
17622                    .and_then(|token| token.text().map(str::to_owned)),
17623            ));
17624            Some(false)
17625        }
17626    }
17627
17628    #[test]
17629    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
17630        let atn = predicate_gated_same_lookahead_atn([0, 0]);
17631        let mut parser = mini_parser_with_hooks(
17632            vec![
17633                TestToken::new(1).with_text("x"),
17634                TestToken::eof("parser-test", 1, 1, 1),
17635            ],
17636            RecordingHooks::default(),
17637        );
17638
17639        let (tree, _) = parser
17640            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17641            .expect("both alternatives share one replay-safe predicate result");
17642
17643        assert_eq!(parser.node(tree).text(), "x<EOF>");
17644        assert_eq!(
17645            parser.semantic_hooks.predicates,
17646            vec![(0, 0, 0, Some("x".to_owned()))]
17647        );
17648        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
17649    }
17650
17651    #[test]
17652    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
17653        let atn = predicate_after_token_atn();
17654        let mut parser = mini_parser_with_hooks(
17655            vec![
17656                TestToken::new(1).with_text("x"),
17657                TestToken::new(2).with_text("y"),
17658                TestToken::eof("parser-test", 2, 1, 2),
17659            ],
17660            RecordingHooks::default(),
17661        );
17662
17663        let (tree, _) = parser
17664            .parse_atn_rule_with_runtime_options(
17665                &atn,
17666                0,
17667                ParserRuntimeOptions {
17668                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17669                    ..ParserRuntimeOptions::default()
17670                },
17671            )
17672            .expect("hook supplies the missing predicate result");
17673
17674        assert_eq!(parser.node(tree).text(), "xy");
17675        assert_eq!(
17676            parser.semantic_hooks.predicates,
17677            vec![(1, 0, 0, Some("y".to_owned()))]
17678        );
17679        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
17680    }
17681
17682    #[test]
17683    fn runtime_options_default_preserves_semantic_hook_predicates() {
17684        let atn = predicate_after_token_atn();
17685        let mut parser = mini_parser_with_hooks(
17686            vec![
17687                TestToken::new(1).with_text("x"),
17688                TestToken::new(2).with_text("y"),
17689                TestToken::eof("parser-test", 2, 1, 2),
17690            ],
17691            RejectingPredicateHooks::default(),
17692        );
17693
17694        let result =
17695            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
17696
17697        assert!(
17698            result.is_err(),
17699            "default runtime options must not bypass semantic hooks for predicate ATNs"
17700        );
17701        assert_eq!(
17702            parser.semantic_hooks.predicates,
17703            vec![(1, 0, 0, Some("y".to_owned()))]
17704        );
17705        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
17706    }
17707
17708    #[test]
17709    fn semantic_hook_handles_committed_parser_action() {
17710        let atn = token_then_eof_atn();
17711        let mut parser = mini_parser_with_hooks(
17712            vec![
17713                TestToken::new(1).with_text("x"),
17714                TestToken::eof("parser-test", 1, 1, 1),
17715            ],
17716            RecordingHooks::default(),
17717        );
17718        let (tree, _) = parser
17719            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17720            .expect("rule parses before action hook is tested");
17721
17722        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17723        assert_eq!(
17724            parser.semantic_hooks.actions,
17725            vec![(42, "x".to_owned(), Some("s".to_owned()))]
17726        );
17727        assert_eq!(
17728            parser.semantic_hooks.action_trees,
17729            [Some("x<EOF>".to_owned())]
17730        );
17731    }
17732
17733    #[test]
17734    fn unhandled_committed_action_fails_loud_under_error_policy() {
17735        // An action offered to the hook that no hook handles (returns false)
17736        // must be recorded and surfaced as `AntlrError::Unsupported` under the
17737        // Error policy, so a `hook`-disposed action is not silently dropped.
17738        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17739        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
17740        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
17741
17742        // DecliningHooks::action returns false (unhandled).
17743        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17744
17745        let error = parser
17746            .take_unknown_semantic_error()
17747            .expect("an unhandled committed action under Error policy must fail loud");
17748        let AntlrError::Unsupported(message) = error else {
17749            panic!("expected AntlrError::Unsupported, got {error:?}");
17750        };
17751        assert!(
17752            message.contains("unhandled semantic action") && message.contains("state=42"),
17753            "message should name the dropped action coordinate: {message}"
17754        );
17755
17756        // Under the default (assume-true) policy the same miss is not recorded.
17757        let mut lenient =
17758            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17759        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
17760        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
17761        assert!(lenient.take_unknown_semantic_error().is_none());
17762    }
17763
17764    #[test]
17765    fn translated_predicate_is_unaffected_by_error_policy() {
17766        let atn = predicate_after_token_atn();
17767        let mut parser = mini_parser(vec![
17768            TestToken::new(1).with_text("x"),
17769            TestToken::new(2).with_text("y"),
17770            TestToken::eof("parser-test", 2, 1, 2),
17771        ]);
17772
17773        let (tree, _) = parser
17774            .parse_atn_rule_with_runtime_options(
17775                &atn,
17776                0,
17777                ParserRuntimeOptions {
17778                    predicates: &[(0, 0, ParserPredicate::True)],
17779                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17780                    ..ParserRuntimeOptions::default()
17781                },
17782            )
17783            .expect("a predicate covered by the table is not an unknown coordinate");
17784
17785        assert_eq!(parser.node(tree).text(), "xy");
17786    }
17787
17788    /// Stack-valued member statements must execute on the parser's speculative
17789    /// replay path, not just the lexer's committed one (issue #206). This drives
17790    /// `apply_member_actions` -> `ParserTableSemCtx` -> `MemberEnv` directly,
17791    /// which is the path a generated parser's `@members` stack state takes.
17792    #[test]
17793    fn parser_speculative_replay_threads_stack_member_state() {
17794        let mut ir = SemIr::new();
17795        let one = ir.expr(PExpr::Int(1));
17796        let push = ir.stmt(AStmt::PushMember(0, one));
17797        let pop = ir.stmt(AStmt::PopMember(0));
17798        let semantics = ParserSemantics {
17799            ir,
17800            predicates: Vec::new(),
17801            actions: vec![
17802                ParserSemanticAction {
17803                    source_state: 1,
17804                    rule_index: usize::MAX,
17805                    stmt: push,
17806                    speculative: true,
17807                },
17808                ParserSemanticAction {
17809                    source_state: 2,
17810                    rule_index: usize::MAX,
17811                    stmt: pop,
17812                    speculative: true,
17813                },
17814            ],
17815        };
17816
17817        // Replaying the push state must be visible to a later read...
17818        let pushed = member_values_after_action(1, &[], Some(&semantics), &MemberEnv::new());
17819        assert_eq!(pushed.stack_top(0), Some(1));
17820        assert_eq!(pushed.stack_len(0), 1);
17821
17822        // ...and must not mutate the caller's env: speculative paths are
17823        // path-local, so an abandoned branch cannot leak state to its sibling.
17824        assert_eq!(MemberEnv::new().stack_len(0), 0);
17825
17826        // Replaying the pop state restores the empty, canonical env, so the
17827        // resulting memo key matches an equivalent untouched path.
17828        let popped = member_values_after_action(2, &[], Some(&semantics), &pushed);
17829        assert_eq!(popped.stack_top(0), None);
17830        assert_eq!(popped, MemberEnv::new(), "emptied stack must canonicalize");
17831
17832        // An unbalanced pop is a defined no-op rather than a panic.
17833        let underflowed = member_values_after_action(2, &[], Some(&semantics), &MemberEnv::new());
17834        assert_eq!(underflowed, MemberEnv::new());
17835    }
17836
17837    /// Hooks that decline (`None`) must fall through to the configured policy
17838    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
17839    /// legacy table path. Regression for the `unwrap_or(false)` that silently
17840    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
17841    fn hook_predicate_semantics() -> ParserSemantics {
17842        let mut ir = SemIr::new();
17843        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
17844        ParserSemantics {
17845            ir,
17846            predicates: vec![ParserSemanticPredicate {
17847                rule_index: 0,
17848                pred_index: 0,
17849                expr,
17850                failure_message: None,
17851            }],
17852            actions: Vec::new(),
17853        }
17854    }
17855
17856    #[derive(Debug, Default)]
17857    struct DecliningHooks;
17858
17859    impl SemanticHooks for DecliningHooks {}
17860
17861    #[test]
17862    fn semir_hook_none_falls_through_to_assume_true() {
17863        let atn = predicate_after_token_atn();
17864        let semantics = hook_predicate_semantics();
17865        let mut parser = mini_parser_with_hooks(
17866            vec![
17867                TestToken::new(1).with_text("x"),
17868                TestToken::new(2).with_text("y"),
17869                TestToken::eof("parser-test", 2, 1, 2),
17870            ],
17871            DecliningHooks,
17872        );
17873
17874        let (tree, _) = parser
17875            .parse_atn_rule_with_runtime_options(
17876                &atn,
17877                0,
17878                ParserRuntimeOptions {
17879                    semantics: Some(&semantics),
17880                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
17881                    ..ParserRuntimeOptions::default()
17882                },
17883            )
17884            .expect("a declined SemIR hook must pass under assume-true");
17885
17886        assert_eq!(parser.node(tree).text(), "xy");
17887    }
17888
17889    #[test]
17890    fn semir_hook_none_falls_through_to_assume_false() {
17891        let atn = predicate_after_token_atn();
17892        let semantics = hook_predicate_semantics();
17893        let mut parser = mini_parser_with_hooks(
17894            vec![
17895                TestToken::new(1).with_text("x"),
17896                TestToken::new(2).with_text("y"),
17897                TestToken::eof("parser-test", 2, 1, 2),
17898            ],
17899            DecliningHooks,
17900        );
17901
17902        let result = parser.parse_atn_rule_with_runtime_options(
17903            &atn,
17904            0,
17905            ParserRuntimeOptions {
17906                semantics: Some(&semantics),
17907                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
17908                ..ParserRuntimeOptions::default()
17909            },
17910        );
17911
17912        assert!(
17913            result.is_err(),
17914            "a declined SemIR hook must fail the only guarded path under assume-false"
17915        );
17916    }
17917
17918    #[test]
17919    fn semir_hook_none_records_coordinate_under_error_policy() {
17920        let atn = predicate_after_token_atn();
17921        let semantics = hook_predicate_semantics();
17922        let mut parser = mini_parser_with_hooks(
17923            vec![
17924                TestToken::new(1).with_text("x"),
17925                TestToken::new(2).with_text("y"),
17926                TestToken::eof("parser-test", 2, 1, 2),
17927            ],
17928            DecliningHooks,
17929        );
17930
17931        let error = parser
17932            .parse_atn_rule_with_runtime_options(
17933                &atn,
17934                0,
17935                ParserRuntimeOptions {
17936                    semantics: Some(&semantics),
17937                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
17938                    ..ParserRuntimeOptions::default()
17939                },
17940            )
17941            .expect_err("a declined SemIR hook under Error policy must fail the parse");
17942
17943        let AntlrError::Unsupported(message) = error else {
17944            panic!("expected AntlrError::Unsupported, got {error:?}");
17945        };
17946        assert!(
17947            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
17948            "message should name the unresolved coordinate: {message}"
17949        );
17950    }
17951
17952    #[test]
17953    fn generated_direct_predicate_honors_installed_policy() {
17954        // The generated recursive-descent path calls
17955        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
17956        // going through `ParserRuntimeOptions`, so the policy must be installed
17957        // via `set_unknown_predicate_policy` (as the generated constructor now
17958        // does). A declining hook must then honor it rather than the default.
17959        let semantics = hook_predicate_semantics();
17960        let context = ParserRuleContext::new(0, -1);
17961
17962        let mut assume_true =
17963            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17964        assert!(
17965            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
17966                &semantics, 0, 0, &context, 0
17967            ),
17968            "default AssumeTrue accepts a declined hook"
17969        );
17970        assert!(assume_true.take_unknown_semantic_error().is_none());
17971
17972        let mut error_policy =
17973            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
17974        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
17975        assert!(
17976            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
17977                &semantics, 0, 0, &context, 0
17978            ),
17979            "Error policy rejects a declined hook on the generated-direct path"
17980        );
17981        let error = error_policy
17982            .take_unknown_semantic_error()
17983            .expect("Error policy records the unresolved coordinate for the generated path");
17984        let AntlrError::Unsupported(message) = error else {
17985            panic!("expected AntlrError::Unsupported, got {error:?}");
17986        };
17987        assert!(message.contains("pred_index=0"), "message: {message}");
17988    }
17989
17990    #[test]
17991    fn parser_rule_start_skips_leading_hidden_tokens() {
17992        let atn = token_then_eof_atn();
17993        let mut parser = mini_parser(vec![
17994            TestToken::new(99)
17995                .with_text(" ")
17996                .with_channel(HIDDEN_CHANNEL),
17997            TestToken::new(1).with_text("x"),
17998            TestToken::eof("parser-test", 2, 1, 2),
17999        ]);
18000
18001        let tree = parser
18002            .parse_atn_rule(&atn, 0)
18003            .expect("artificial parser rule should parse");
18004        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
18005            panic!("rule node should be present");
18006        };
18007        assert_eq!(
18008            rule.start()
18009                .expect("rule should have a start token")
18010                .token_type(),
18011            1
18012        );
18013    }
18014
18015    #[test]
18016    fn parser_action_after_eof_stops_at_eof_token() {
18017        let atn = eof_then_action_atn();
18018        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
18019
18020        let (_, actions) = parser
18021            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
18022            .expect("EOF action rule should parse");
18023
18024        assert_eq!(actions.len(), 1);
18025        assert_eq!(actions[0].stop_index(), Some(0));
18026        assert_eq!(
18027            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
18028            ""
18029        );
18030    }
18031
18032    #[test]
18033    fn after_action_stop_uses_rule_context_stop_not_cursor() {
18034        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
18035        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
18036        // but the rule did not consume it. The @after stop must follow the rule
18037        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
18038        let mut id = TestToken::new(1).with_text("x");
18039        id.set_token_index(0);
18040        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
18041        eof.set_token_index(1);
18042        let mut parser = mini_parser(vec![id.clone(), eof]);
18043        // Advance the cursor onto EOF, as it would be after `a` matched ID.
18044        parser.consume();
18045        assert_eq!(parser.la(1), TOKEN_EOF);
18046
18047        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
18048        // exactly what finish_rule(consumed_eof = false) records.
18049        let mut ctx = ParserRuleContext::new(0, 0);
18050        parser.set_context_stop(
18051            &mut ctx,
18052            parser.token_id_at(0).expect("ID token should be buffered"),
18053        );
18054        let tree = parser.rule_node(ctx);
18055
18056        let current_index = parser.input.index();
18057        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
18058        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
18059        // ...but the tree-aware helper follows the rule context stop (ID).
18060        assert_eq!(
18061            parser.after_action_stop_index_for_tree(tree, current_index),
18062            Some(0)
18063        );
18064    }
18065
18066    #[test]
18067    fn after_action_start_uses_rule_context_start_not_cursor() {
18068        // A rule that begins after leading hidden-channel tokens: the rule context
18069        // start (set by `enter_rule`) is the first visible token, not the raw cursor
18070        // that may still point at the hidden prefix. The @after start must follow
18071        // the context start so `$start`/`$text` excludes the hidden prefix.
18072        let mut parser = mini_parser(vec![
18073            TestToken::new(9)
18074                .with_text(" ")
18075                .with_channel(HIDDEN_CHANNEL),
18076            TestToken::new(9)
18077                .with_text(" ")
18078                .with_channel(HIDDEN_CHANNEL),
18079            TestToken::new(1).with_text("x"),
18080            TestToken::eof("parser-test", 3, 1, 3),
18081        ]);
18082
18083        let mut ctx = ParserRuleContext::new(0, 0);
18084        parser.set_context_start(
18085            &mut ctx,
18086            parser.token_id_at(2).expect("ID token should be buffered"),
18087        );
18088        let tree = parser.rule_node(ctx);
18089
18090        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
18091        // ...but the tree-aware helper follows the rule context start (index 2).
18092        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
18093
18094        // With no rule start recorded, it falls back to the provided index.
18095        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
18096        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
18097    }
18098
18099    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
18100        FastRecognizeOutcome {
18101            index,
18102            consumed_eof,
18103            diagnostics: DiagnosticSeqId::EMPTY,
18104            deferred_nodes: FastDeferredNodeId::EMPTY,
18105            nodes: NodeSeqId(marker),
18106        }
18107    }
18108
18109    #[test]
18110    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
18111        let mut outcomes = vec![
18112            clean_fast_outcome(4, false, 0),
18113            clean_fast_outcome(2, false, 1),
18114            clean_fast_outcome(4, false, 2),
18115            clean_fast_outcome(4, true, 3),
18116            clean_fast_outcome(2, false, 4),
18117        ];
18118        let mut scratch = FastOutcomeDedupScratch::default();
18119
18120        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
18121
18122        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
18123        assert_eq!(
18124            outcomes
18125                .iter()
18126                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
18127                .collect::<Vec<_>>(),
18128            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
18129        );
18130        assert!(scratch.dense_words.is_empty());
18131        assert!(scratch.sparse_keys.is_empty());
18132    }
18133
18134    #[test]
18135    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
18136        let mut scratch = FastOutcomeDedupScratch::default();
18137        let mut outcomes = (100..109)
18138            .flat_map(|index| {
18139                [
18140                    clean_fast_outcome(
18141                        index,
18142                        false,
18143                        u32::try_from(index).expect("test index fits in u32"),
18144                    ),
18145                    clean_fast_outcome(index, false, u32::MAX),
18146                ]
18147            })
18148            .collect();
18149
18150        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
18151
18152        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
18153        assert_eq!(outcomes.len(), 9);
18154        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
18155        let dense_capacity = scratch.dense_words.capacity();
18156
18157        let mut reused = (1_000..1_009)
18158            .map(|index| {
18159                clean_fast_outcome(
18160                    index,
18161                    false,
18162                    u32::try_from(index).expect("test index fits in u32"),
18163                )
18164            })
18165            .collect();
18166        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
18167
18168        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
18169        assert_eq!(reused.len(), 9);
18170        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
18171    }
18172
18173    #[test]
18174    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
18175        let mut scratch = FastOutcomeDedupScratch::default();
18176        let sparse_indexes = [
18177            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
18178        ];
18179        let mut outcomes = sparse_indexes
18180            .into_iter()
18181            .chain([400_000])
18182            .enumerate()
18183            .map(|(marker, index)| {
18184                clean_fast_outcome(
18185                    index,
18186                    false,
18187                    u32::try_from(marker).expect("test marker fits in u32"),
18188                )
18189            })
18190            .collect();
18191
18192        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
18193
18194        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
18195        assert_eq!(outcomes.len(), sparse_indexes.len());
18196        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
18197        let sparse_capacity = scratch.sparse_keys.capacity();
18198
18199        let mut reused = sparse_indexes
18200            .into_iter()
18201            .map(|index| {
18202                clean_fast_outcome(
18203                    index,
18204                    false,
18205                    u32::try_from(index).expect("test index fits in u32"),
18206                )
18207            })
18208            .collect();
18209        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
18210
18211        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
18212        assert_eq!(reused.len(), sparse_indexes.len());
18213        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
18214    }
18215
18216    #[test]
18217    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
18218        let mut scratch = FastOutcomeDedupScratch::default();
18219        scratch
18220            .sparse_keys
18221            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
18222        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
18223        let mut outcomes = (0..9)
18224            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
18225            .collect();
18226
18227        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
18228
18229        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
18230        assert!(scratch.sparse_keys.is_empty());
18231        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
18232    }
18233
18234    #[test]
18235    fn fast_outcome_selection_respects_sll_tie_order() {
18236        let mut arena = RecognitionArena::default();
18237        let first = FastRecognizeOutcome {
18238            index: 1,
18239            consumed_eof: false,
18240            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18241                line: 1,
18242                column: 0,
18243                message: "mismatched input 'x'".to_owned(),
18244                offending: None,
18245            }]),
18246            deferred_nodes: FastDeferredNodeId::EMPTY,
18247            nodes: NodeSeqId::EMPTY,
18248        };
18249        let second = FastRecognizeOutcome {
18250            index: first.index,
18251            consumed_eof: first.consumed_eof,
18252            diagnostics: DiagnosticSeqId::EMPTY,
18253            deferred_nodes: FastDeferredNodeId::EMPTY,
18254            nodes: NodeSeqId::EMPTY,
18255        };
18256
18257        let selected = select_best_fast_outcome(
18258            [first, second].into_iter(),
18259            PredictionMode::Sll,
18260            None,
18261            |_| panic!("caller-follow token probe should not run"),
18262            &arena,
18263        )
18264        .expect("one outcome should be selected");
18265        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
18266        let eof_second = FastRecognizeOutcome {
18267            index: second.index,
18268            consumed_eof: true,
18269            diagnostics: DiagnosticSeqId::EMPTY,
18270            deferred_nodes: FastDeferredNodeId::EMPTY,
18271            nodes: NodeSeqId::EMPTY,
18272        };
18273        let selected = select_best_fast_outcome(
18274            [first, eof_second].into_iter(),
18275            PredictionMode::Sll,
18276            None,
18277            |_| panic!("caller-follow token probe should not run"),
18278            &arena,
18279        )
18280        .expect("one outcome should be selected");
18281        assert!(!selected.consumed_eof);
18282        let selected = select_best_fast_outcome(
18283            [first, second].into_iter(),
18284            PredictionMode::Ll,
18285            None,
18286            |_| panic!("caller-follow token probe should not run"),
18287            &arena,
18288        )
18289        .expect("one outcome should be selected");
18290        assert!(selected.diagnostics.is_empty());
18291    }
18292
18293    #[test]
18294    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
18295        let mut arena = RecognitionArena::default();
18296        let first = FastRecognizeOutcome {
18297            index: 3,
18298            consumed_eof: false,
18299            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18300                line: 1,
18301                column: 0,
18302                message: "mismatched input 'x' expecting 'a'".to_owned(),
18303                offending: None,
18304            }]),
18305            deferred_nodes: FastDeferredNodeId::EMPTY,
18306            nodes: NodeSeqId::EMPTY,
18307        };
18308        let same_rank = FastRecognizeOutcome {
18309            index: first.index,
18310            consumed_eof: first.consumed_eof,
18311            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18312                line: 1,
18313                column: 0,
18314                message: "mismatched input 'x' expecting 'b'".to_owned(),
18315                offending: None,
18316            }]),
18317            deferred_nodes: FastDeferredNodeId::EMPTY,
18318            nodes: NodeSeqId::EMPTY,
18319        };
18320        let better_rank = FastRecognizeOutcome {
18321            index: first.index,
18322            consumed_eof: first.consumed_eof,
18323            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
18324                line: 1,
18325                column: 0,
18326                message: "missing 'a' at 'x'".to_owned(),
18327                offending: None,
18328            }]),
18329            deferred_nodes: FastDeferredNodeId::EMPTY,
18330            nodes: NodeSeqId::EMPTY,
18331        };
18332        let mut outcomes = vec![first, same_rank, better_rank];
18333
18334        dedupe_fast_outcomes(&mut outcomes, &arena);
18335
18336        assert_eq!(outcomes.len(), 2);
18337        assert_eq!(
18338            arena
18339                .diagnostics(outcomes[0].diagnostics)
18340                .next()
18341                .expect("first diagnostic")
18342                .message,
18343            "mismatched input 'x' expecting 'a'"
18344        );
18345        assert_eq!(
18346            arena
18347                .diagnostics(outcomes[1].diagnostics)
18348                .next()
18349                .expect("second diagnostic")
18350                .message,
18351            "missing 'a' at 'x'"
18352        );
18353    }
18354
18355    #[test]
18356    fn fast_outcome_selection_prefers_generated_caller_follow() {
18357        let arena = RecognitionArena::default();
18358        let earlier = FastRecognizeOutcome {
18359            index: 7,
18360            consumed_eof: false,
18361            diagnostics: DiagnosticSeqId::EMPTY,
18362            deferred_nodes: FastDeferredNodeId::EMPTY,
18363            nodes: NodeSeqId::EMPTY,
18364        };
18365        let later = FastRecognizeOutcome {
18366            index: 8,
18367            consumed_eof: false,
18368            diagnostics: DiagnosticSeqId::EMPTY,
18369            deferred_nodes: FastDeferredNodeId::EMPTY,
18370            nodes: NodeSeqId::EMPTY,
18371        };
18372        let mut follow = TokenBitSet::default();
18373        follow.insert(5);
18374
18375        let selected = select_best_fast_outcome(
18376            [later, earlier].into_iter(),
18377            PredictionMode::Ll,
18378            Some(&follow),
18379            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
18380            &arena,
18381        )
18382        .expect("one outcome should be selected");
18383        assert_eq!(selected.index, 7);
18384
18385        let selected = select_best_fast_outcome(
18386            [later, earlier].into_iter(),
18387            PredictionMode::Ll,
18388            Some(&follow),
18389            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
18390            &arena,
18391        )
18392        .expect("one outcome should be selected");
18393        assert_eq!(selected.index, 8);
18394
18395        let indented_next_statement = FastRecognizeOutcome {
18396            index: 9,
18397            consumed_eof: false,
18398            diagnostics: DiagnosticSeqId::EMPTY,
18399            deferred_nodes: FastDeferredNodeId::EMPTY,
18400            nodes: NodeSeqId::EMPTY,
18401        };
18402        let selected = select_best_fast_outcome(
18403            [indented_next_statement, earlier].into_iter(),
18404            PredictionMode::Ll,
18405            Some(&follow),
18406            |index| {
18407                let is_boundary = index == 7;
18408                let is_boundary_gap = matches!(index, 7 | 8);
18409                (
18410                    if index == 7 { 5 } else { TOKEN_EOF },
18411                    is_boundary,
18412                    is_boundary_gap,
18413                )
18414            },
18415            &arena,
18416        )
18417        .expect("one outcome should be selected");
18418        assert_eq!(selected.index, 7);
18419
18420        let continuation = FastRecognizeOutcome {
18421            index: 10,
18422            consumed_eof: false,
18423            diagnostics: DiagnosticSeqId::EMPTY,
18424            deferred_nodes: FastDeferredNodeId::EMPTY,
18425            nodes: NodeSeqId::EMPTY,
18426        };
18427        let selected = select_best_fast_outcome(
18428            [continuation, earlier].into_iter(),
18429            PredictionMode::Ll,
18430            Some(&follow),
18431            |index| {
18432                let is_boundary = matches!(index, 7 | 9);
18433                (
18434                    if index == 7 { 5 } else { TOKEN_EOF },
18435                    is_boundary,
18436                    is_boundary,
18437                )
18438            },
18439            &arena,
18440        )
18441        .expect("one outcome should be selected");
18442        assert_eq!(selected.index, 10);
18443
18444        let selected = select_best_fast_outcome(
18445            [earlier, later].into_iter(),
18446            PredictionMode::Sll,
18447            Some(&follow),
18448            |_| panic!("caller-follow token probe should not run in SLL mode"),
18449            &arena,
18450        )
18451        .expect("one outcome should be selected");
18452        assert_eq!(selected.index, 8);
18453    }
18454
18455    #[test]
18456    fn caller_follow_boundary_text_requires_separator_shape() {
18457        assert!(is_caller_follow_boundary_text(";"));
18458        assert!(is_caller_follow_boundary_text("\n"));
18459        assert!(is_caller_follow_boundary_text("\r\n  "));
18460        assert!(is_caller_follow_boundary_text(";\n"));
18461        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
18462        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
18463        assert!(!is_caller_follow_boundary_text("identifier"));
18464        assert!(is_caller_follow_boundary_gap_text(" \t "));
18465        assert!(is_caller_follow_boundary_gap_text("\n  "));
18466        assert!(is_caller_follow_boundary_gap_text(";\t"));
18467        assert!(!is_caller_follow_boundary_gap_text(
18468            "\"\"\"line1\nline2\"\"\""
18469        ));
18470        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
18471    }
18472
18473    #[test]
18474    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
18475        let mut parser = mini_parser(vec![
18476            TestToken::new(5).with_text("\n"),
18477            TestToken::new(6)
18478                .with_text("// comment\n")
18479                .with_channel(HIDDEN_CHANNEL),
18480            TestToken::new(1).with_text("x"),
18481            TestToken::eof("parser-test", 1, 2, 0),
18482        ]);
18483
18484        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
18485        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
18486        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
18487    }
18488
18489    #[test]
18490    fn caller_follow_token_info_uses_stream_visible_channel() {
18491        let source = Source {
18492            tokens: vec![
18493                TestToken::new(5).with_text("\n").with_channel(2),
18494                TestToken::new(1).with_text("x").with_channel(2),
18495                TestToken::new(6)
18496                    .with_text("// comment\n")
18497                    .with_channel(HIDDEN_CHANNEL),
18498                TestToken::eof("parser-test", 1, 2, 0),
18499            ],
18500            index: 0,
18501        };
18502        let data = RecognizerData::new(
18503            "Mini.g4",
18504            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
18505        );
18506        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
18507
18508        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
18509        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
18510        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
18511    }
18512
18513    #[test]
18514    fn reset_per_parse_caches_clears_state_expected_token_cache() {
18515        let atn = token_then_eof_atn();
18516        let mut parser = mini_parser(Vec::new());
18517
18518        let _ = parser.cached_state_expected_token_set(&atn, 0);
18519        assert!(!parser.state_expected_token_cache.is_empty());
18520
18521        parser.reset_per_parse_caches();
18522        assert!(parser.state_expected_token_cache.is_empty());
18523    }
18524
18525    #[test]
18526    fn empty_cycle_cache_survives_reset_and_invalidates_for_a_different_atn() {
18527        let cyclic = epsilon_cycle_atn();
18528        let acyclic = token_then_eof_atn();
18529        let mut parser = mini_parser(Vec::new());
18530
18531        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
18532        assert_eq!(
18533            parser.empty_cycle_cache_atn,
18534            Some(SharedAtnCacheKey::for_atn(&cyclic))
18535        );
18536        assert_eq!(parser.empty_cycle_cache[1], Some(true));
18537
18538        parser.reset_per_parse_caches();
18539        assert_eq!(parser.empty_cycle_cache[1], Some(true));
18540        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
18541
18542        assert!(!parser.state_can_reenter_without_consuming(&acyclic, 1));
18543        assert_eq!(
18544            parser.empty_cycle_cache_atn,
18545            Some(SharedAtnCacheKey::for_atn(&acyclic))
18546        );
18547        assert_eq!(parser.empty_cycle_cache[1], Some(false));
18548    }
18549
18550    #[test]
18551    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
18552        let source = Source {
18553            tokens: vec![
18554                TestToken::new(1).with_text("x"),
18555                TestToken::eof("parser-test", 1, 1, 1),
18556            ],
18557            index: 0,
18558        };
18559        let data = RecognizerData::new(
18560            "Mini.g4",
18561            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
18562        );
18563        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
18564        let expected = ExpectedTokens {
18565            index: Some(0),
18566            symbols: BTreeSet::new(),
18567            no_viable: None,
18568        };
18569
18570        let (_, message) = parser.expected_error_message(0, 0, &expected);
18571
18572        assert_eq!(message, "mismatched input 'x'");
18573    }
18574
18575    #[test]
18576    fn eof_rule_stop_index_points_at_eof_token() {
18577        let source = Source {
18578            tokens: vec![
18579                TestToken::new(1).with_text("x"),
18580                TestToken::eof("parser-test", 1, 1, 1),
18581            ],
18582            index: 0,
18583        };
18584        let data = RecognizerData::new(
18585            "Mini.g4",
18586            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
18587        );
18588        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
18589
18590        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
18591        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
18592    }
18593
18594    #[test]
18595    fn generated_parser_action_uses_current_rule_stop_boundary() {
18596        let mut parser = mini_parser(vec![
18597            TestToken::new(1).with_text("x"),
18598            TestToken::eof("parser-test", 1, 1, 1),
18599        ]);
18600
18601        parser.match_token(1).expect("token should match");
18602        let action = parser.parser_action_at_current(7, 0, 0, false);
18603        assert_eq!(action.source_state(), 7);
18604        assert_eq!(action.rule_index(), 0);
18605        assert_eq!(action.start_index(), 0);
18606        assert_eq!(action.stop_index(), Some(0));
18607
18608        parser.match_eof().expect("EOF should match");
18609        let action = parser.parser_action_at_current(8, 0, 0, true);
18610        assert_eq!(action.stop_index(), Some(1));
18611    }
18612
18613    #[test]
18614    fn folds_left_recursive_boundary_into_rule_node() {
18615        let mut arena = RecognitionArena::default();
18616        let first = arena.push_node(ArenaRecognizedNode::Token {
18617            token: TokenId::try_from(0).expect("test token ID"),
18618        });
18619        let boundary = arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
18620            rule_index: 1,
18621            alt_number: 3,
18622        });
18623        let second = arena.push_node(ArenaRecognizedNode::Token {
18624            token: TokenId::try_from(1).expect("test token ID"),
18625        });
18626        let mut nodes = NodeSeqId::EMPTY;
18627        for node in [first, boundary, second].into_iter().rev() {
18628            nodes = arena.prepend(nodes, node);
18629        }
18630
18631        let folded = arena.fold_left_recursive_boundaries(nodes);
18632        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
18633
18634        assert_eq!(folded_nodes.len(), 2);
18635        let ArenaRecognizedNode::Rule {
18636            rule_index,
18637            invoking_state,
18638            alt_number,
18639            start_index,
18640            stop_index,
18641            children,
18642            ..
18643        } = arena.node(folded_nodes[0])
18644        else {
18645            panic!("first folded node should be a rule");
18646        };
18647        // The folded rule node's scalar shape (rule/invoking-state/alt/start/stop) is one snapshot;
18648        // child resolution and the sibling identity below stay explicit — a node Debug prints the
18649        // children handle, not the resolved sequence they assert on.
18650        insta::assert_debug_snapshot!(
18651            "folds_left_recursive_boundary_into_rule_node",
18652            (
18653                rule_index,
18654                invoking_state,
18655                alt_number,
18656                start_index,
18657                stop_index
18658            )
18659        );
18660        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
18661        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
18662
18663        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
18664        assert_eq!(
18665            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18666            (4, 3, 1)
18667        );
18668        assert_eq!(
18669            (stats.total_links, stats.live_links, stats.dead_links),
18670            (9, 3, 6)
18671        );
18672    }
18673
18674    #[test]
18675    fn recognition_arena_reports_live_dead_and_retained_capacity() {
18676        let mut arena = RecognitionArena::default();
18677        let token = arena.push_node(ArenaRecognizedNode::Token {
18678            token: TokenId::try_from(0).expect("test token ID"),
18679        });
18680        let extra = arena.push_extra(RecognitionExtra::MissingToken {
18681            token_type: 2,
18682            at_index: 1,
18683            text: "<missing X>".to_owned(),
18684        });
18685        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
18686        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
18687            token: TokenId::try_from(1).expect("test token ID"),
18688        });
18689        let mut live = NodeSeqId::EMPTY;
18690        live = arena.prepend(live, missing);
18691        live = arena.prepend(live, token);
18692        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
18693        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
18694            line: 1,
18695            column: 0,
18696            message: "missing X".to_owned(),
18697            offending: None,
18698        }]);
18699        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
18700            line: 1,
18701            column: 1,
18702            message: "discarded".to_owned(),
18703            offending: None,
18704        }]);
18705        let deferred_children = arena.deferred_fragment(live);
18706        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
18707            rule_index: 0,
18708            invoking_state: -1,
18709            start_index: 0,
18710            stop_index: Some(1),
18711            deferred_children,
18712            children: NodeSeqId::EMPTY,
18713        });
18714
18715        let stats = arena.stats(live, live_diagnostics);
18716
18717        assert_eq!(
18718            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18719            (3, 2, 1)
18720        );
18721        assert_eq!(
18722            (stats.total_links, stats.live_links, stats.dead_links),
18723            (5, 3, 2)
18724        );
18725        assert_eq!(
18726            (stats.total_extras, stats.live_extras, stats.dead_extras),
18727            (3, 2, 1)
18728        );
18729        assert!(size_of::<SeqLink>() <= 8);
18730        assert!(size_of::<DiagnosticLink>() <= 8);
18731        assert!(size_of::<FastDeferredNode>() <= 12);
18732        assert!(size_of::<FastDeferredRule>() <= 28);
18733        assert!(size_of::<FastRecognizeOutcome>() <= 24);
18734        let capacities = (
18735            stats.node_capacity,
18736            stats.link_capacity,
18737            stats.extra_capacity,
18738        );
18739        let deferred_capacities = (
18740            arena.deferred_nodes.capacity(),
18741            arena.deferred_rules.capacity(),
18742        );
18743
18744        arena.reset();
18745        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
18746        assert_eq!(
18747            (reset.total_nodes, reset.total_links, reset.total_extras),
18748            (0, 0, 0)
18749        );
18750        assert_eq!(
18751            (
18752                reset.node_capacity,
18753                reset.link_capacity,
18754                reset.extra_capacity,
18755            ),
18756            capacities
18757        );
18758        assert!(arena.deferred_nodes.is_empty());
18759        assert!(arena.deferred_rules.is_empty());
18760        assert_eq!(
18761            (
18762                arena.deferred_nodes.capacity(),
18763                arena.deferred_rules.capacity(),
18764            ),
18765            deferred_capacities
18766        );
18767    }
18768
18769    #[test]
18770    fn parser_computes_recognition_arena_stats_on_demand() {
18771        let mut parser = mini_parser(Vec::new());
18772        let live = parser
18773            .recognition_arena
18774            .push_node(ArenaRecognizedNode::Token {
18775                token: TokenId::try_from(0).expect("test token ID"),
18776            });
18777        let discarded = parser
18778            .recognition_arena
18779            .push_node(ArenaRecognizedNode::ErrorToken {
18780                token: TokenId::try_from(1).expect("test token ID"),
18781            });
18782        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
18783        let _discarded_root = parser
18784            .recognition_arena
18785            .prepend(NodeSeqId::EMPTY, discarded);
18786        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
18787
18788        let stats = parser.recognition_arena_stats();
18789
18790        assert_eq!(
18791            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18792            (2, 1, 1)
18793        );
18794        assert_eq!(
18795            (stats.total_links, stats.live_links, stats.dead_links),
18796            (2, 1, 1)
18797        );
18798    }
18799
18800    #[test]
18801    fn recognition_arena_drops_capacity_above_retention_limit() {
18802        let mut storage = Vec::<u8>::with_capacity(4);
18803        storage.extend([1, 2, 3]);
18804
18805        reset_arena_vec(&mut storage, 3);
18806
18807        assert!(storage.is_empty());
18808        assert_eq!(storage.capacity(), 0);
18809    }
18810
18811    #[test]
18812    fn recognition_arena_concatenates_diagnostics_in_source_order() {
18813        let mut arena = RecognitionArena::default();
18814        let prefix = arena.diagnostic_sequence([
18815            ParserDiagnostic {
18816                line: 1,
18817                column: 0,
18818                message: "first".to_owned(),
18819                offending: None,
18820            },
18821            ParserDiagnostic {
18822                line: 1,
18823                column: 1,
18824                message: "second".to_owned(),
18825                offending: None,
18826            },
18827        ]);
18828        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
18829            line: 1,
18830            column: 2,
18831            message: "third".to_owned(),
18832            offending: None,
18833        }]);
18834        let extras_before = arena.extras.len();
18835
18836        let combined = arena.concat_diagnostics(prefix, suffix);
18837        let messages = arena
18838            .diagnostics(combined)
18839            .map(|diagnostic| diagnostic.message.as_str())
18840            .collect::<Vec<_>>();
18841
18842        assert_eq!(messages, ["first", "second", "third"]);
18843        assert_eq!(arena.extras.len(), extras_before);
18844    }
18845
18846    #[test]
18847    fn outcome_ties_keep_later_non_recursive_alternative() {
18848        let arena = RecognitionArena::default();
18849        let first = RecognizeOutcome {
18850            index: 1,
18851            consumed_eof: false,
18852            alt_number: 0,
18853            member_values: MemberEnv::new(),
18854            return_values: BTreeMap::new(),
18855            diagnostics: DiagnosticSeqId::EMPTY,
18856            decisions: Vec::new(),
18857            actions: vec![ParserAction::new(1, 0, 0, None)],
18858            nodes: NodeSeqId::EMPTY,
18859        };
18860        let second = RecognizeOutcome {
18861            actions: vec![ParserAction::new(2, 0, 0, None)],
18862            ..first.clone()
18863        };
18864
18865        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18866            .expect("one outcome should be selected");
18867        assert_eq!(selected.actions[0].source_state(), 2);
18868    }
18869
18870    #[test]
18871    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
18872        let arena = RecognitionArena::default();
18873        let first = RecognizeOutcome {
18874            index: 1,
18875            consumed_eof: false,
18876            alt_number: 0,
18877            member_values: MemberEnv::new(),
18878            return_values: BTreeMap::new(),
18879            diagnostics: DiagnosticSeqId::EMPTY,
18880            decisions: Vec::new(),
18881            actions: vec![ParserAction::new(1, 0, 0, None)],
18882            nodes: NodeSeqId::EMPTY,
18883        };
18884        let second = RecognizeOutcome {
18885            actions: vec![
18886                ParserAction::new(2, 0, 0, None),
18887                ParserAction::new(3, 0, 0, None),
18888            ],
18889            ..first.clone()
18890        };
18891
18892        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
18893            .expect("one outcome should be selected");
18894        assert_eq!(selected.actions.len(), 2);
18895    }
18896
18897    #[test]
18898    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
18899        let arena = RecognitionArena::default();
18900        let first = RecognizeOutcome {
18901            index: 7,
18902            consumed_eof: false,
18903            alt_number: 0,
18904            member_values: MemberEnv::new(),
18905            return_values: BTreeMap::new(),
18906            diagnostics: DiagnosticSeqId::EMPTY,
18907            decisions: vec![1, 0],
18908            actions: vec![
18909                ParserAction::new(23, 2, 2, Some(4)),
18910                ParserAction::new(23, 2, 0, Some(6)),
18911            ],
18912            nodes: NodeSeqId::EMPTY,
18913        };
18914        let second = RecognizeOutcome {
18915            decisions: vec![0, 1],
18916            actions: vec![
18917                ParserAction::new(23, 2, 2, Some(6)),
18918                ParserAction::new(23, 2, 0, Some(6)),
18919            ],
18920            ..first.clone()
18921        };
18922
18923        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18924            .expect("one outcome should be selected");
18925        assert_eq!(selected.actions[0].stop_index(), Some(6));
18926    }
18927
18928    #[test]
18929    fn outcome_ties_keep_first_recursive_tree_shape() {
18930        let mut arena = RecognitionArena::default();
18931        let token = arena.push_node(ArenaRecognizedNode::Token {
18932            token: TokenId::try_from(0).expect("test token ID"),
18933        });
18934        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
18935        let inner = arena.push_node(ArenaRecognizedNode::Rule {
18936            rule_index: 1,
18937            invoking_state: -1,
18938            alt_number: 0,
18939            start_index: 0,
18940            stop_index: Some(0),
18941            return_values: None,
18942            children: token_children,
18943        });
18944        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
18945        let outer = arena.push_node(ArenaRecognizedNode::Rule {
18946            rule_index: 1,
18947            invoking_state: -1,
18948            alt_number: 0,
18949            start_index: 0,
18950            stop_index: Some(0),
18951            return_values: None,
18952            children: inner_children,
18953        });
18954        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
18955        let first = RecognizeOutcome {
18956            index: 1,
18957            consumed_eof: false,
18958            alt_number: 0,
18959            member_values: MemberEnv::new(),
18960            return_values: BTreeMap::new(),
18961            diagnostics: DiagnosticSeqId::EMPTY,
18962            decisions: Vec::new(),
18963            actions: vec![ParserAction::new(1, 0, 0, None)],
18964            nodes: recursive_nodes,
18965        };
18966        let second = RecognizeOutcome {
18967            index: 1,
18968            consumed_eof: false,
18969            alt_number: 0,
18970            member_values: MemberEnv::new(),
18971            return_values: BTreeMap::new(),
18972            diagnostics: DiagnosticSeqId::EMPTY,
18973            decisions: Vec::new(),
18974            actions: vec![ParserAction::new(2, 0, 0, None)],
18975            nodes: recursive_nodes,
18976        };
18977
18978        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
18979            .expect("one outcome should be selected");
18980        assert_eq!(selected.actions[0].source_state(), 1);
18981    }
18982
18983    #[test]
18984    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
18985        let mut arena = RecognitionArena::default();
18986        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
18987            line: 1,
18988            column: 3,
18989            message: "missing 'Y' at '<EOF>'".to_owned(),
18990            offending: None,
18991        }]);
18992        let first_alt = RecognizeOutcome {
18993            index: 2,
18994            consumed_eof: true,
18995            alt_number: 0,
18996            member_values: MemberEnv::new(),
18997            return_values: BTreeMap::new(),
18998            diagnostics: recovered_diagnostics,
18999            decisions: vec![0],
19000            actions: vec![ParserAction::new(1, 0, 0, None)],
19001            nodes: NodeSeqId::EMPTY,
19002        };
19003        let second_alt = RecognizeOutcome {
19004            diagnostics: DiagnosticSeqId::EMPTY,
19005            decisions: vec![1],
19006            actions: vec![ParserAction::new(2, 0, 0, None)],
19007            ..first_alt.clone()
19008        };
19009
19010        let selected = select_best_outcome(
19011            [second_alt, first_alt].into_iter(),
19012            PredictionMode::Sll,
19013            &arena,
19014        )
19015        .expect("one outcome should be selected");
19016        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
19017        assert_eq!(selected.decisions, [0]);
19018    }
19019}