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    ParserAtnSimulatorError, ParserSemanticCandidate,
79};
80use crate::atn::parser_atn::{
81    ParserAtn as Atn, ParserAtnState as AtnState, ParserIntervalSet, ParserTransition,
82    ParserTransitionData as Transition, ParserTransitionKind,
83};
84#[cfg(test)]
85use crate::atn::parser_atn::{ParserAtnBuilder, ParserTransitionSpec};
86use crate::char_stream::CharStream;
87use crate::errors::{AntlrError, SyntaxErrorEvent};
88use crate::int_stream::IntStream;
89use crate::lexer::{LexerCustomAction, LexerLifecycleCtx, LexerSemCtx};
90use crate::prediction::SemanticContext;
91use crate::recognizer::{Recognizer, RecognizerData};
92use crate::semir::{self, AStmt, ArithOp, CmpOp, ExprId, HookId, MemberEnv, PExpr, SemIr, StmtId};
93use crate::token::{
94    TOKEN_EOF, Token, TokenId, TokenSource, TokenSourceError, TokenSpec, TokenStore, TokenView,
95};
96use crate::token_stream::CommonTokenStream;
97use crate::tree::{
98    Node, NodeId, ParseTreeCheckpoint, ParseTreeStorage, ParsedFile, ParserRuleContext,
99};
100use crate::vocabulary::Vocabulary;
101
102type ParseTree = NodeId;
103
104/// Upper bound for the recursive metadata recognizer before it treats a path as
105/// non-viable. Long expression-regression descriptors legitimately walk tens
106/// of thousands of ATN edges.
107const RECOGNITION_DEPTH_LIMIT: usize = 32_768;
108/// Preserve the recursive hot path while checking native stack capacity often
109/// enough that one unchecked group cannot cross the protected red zone.
110const FAST_RECOGNIZE_STACK_CHECK_INTERVAL: usize = 8;
111const FAST_RECOGNIZE_RED_ZONE: usize = 1024 * 1024;
112const FAST_RECOGNIZE_STACK_SIZE: usize = 4 * 1024 * 1024;
113/// Generated recursive-descent rule methods map grammar-rule nesting onto
114/// native call depth. Their `_dispatch` boundary samples remaining stack
115/// capacity once per this many rule-context frames, so between two samples at
116/// most this many rule bodies of native growth can occur — far below the
117/// red zone.
118const GENERATED_RULE_STACK_CHECK_INTERVAL: usize = 8;
119/// Whole-rule direct adaptive execution is allowed to give up and fall back to
120/// the existing recognizer. Keep the guard at the same order of magnitude as
121/// speculative recognition so malformed cyclic ATNs cannot spin forever.
122const ADAPTIVE_DIRECT_STEP_LIMIT: usize = RECOGNITION_DEPTH_LIMIT;
123
124/// Runs a generated rule body after ensuring native stack capacity, growing
125/// onto a segmented stack when remaining capacity enters the red zone.
126///
127/// Generated `parse_generated_rule_*_dispatch` methods call this when
128/// [`BaseParser::generated_rule_stack_check_due`] fires so deeply nested input
129/// parses (or reports a syntax error) instead of aborting the process.
130pub fn grow_generated_rule_stack<R>(body: impl FnOnce() -> R) -> R {
131    stacker::maybe_grow(FAST_RECOGNIZE_RED_ZONE, FAST_RECOGNIZE_STACK_SIZE, body)
132}
133
134/// Shared lifecycle and recovery shell for generated parser rules.
135///
136/// This is an implementation detail of `antlr4-rust-gen`, not a stable
137/// hand-written parser API. The binders supplied by generated code keep
138/// grammar-specific locals and steps inline while this macro owns the fixed
139/// dispatch, entry, recovery, and exit state machine.
140#[doc(hidden)]
141#[macro_export]
142macro_rules! __antlr4_rust_generated_rule {
143    (
144        dispatch $parser:ident, $rule:expr, $fatal:path;
145        $body:expr
146    ) => {{
147        if let Some(error) = $parser.base.rule_depth_cap_violation() {
148            return Err($fatal(error));
149        }
150        if let Some(error) = $parser.base.parse_listener_enter_rule($rule) {
151            return Err($fatal(error));
152        }
153        let __listener_result = if $parser.base.generated_rule_stack_check_due() {
154            $crate::grow_generated_rule_stack(|| $body)
155        } else {
156            $body
157        };
158        $parser.base.parse_listener_exit_rule($rule);
159        __listener_result
160    }};
161    (
162        ordinary $parser:ident, $state:expr, $rule:expr, $allow_fallback:expr,
163        $atn:expr, $fatal:path;
164        retry [$($retry:tt)*];
165        bind ($ctx:ident, $rule_start:ident, $consumed_eof:ident, $sync_error:ident);
166        setup { $($setup:tt)* }
167        body { $($body:tt)* }
168        success { $($success:tt)* }
169        recovery { $($recovery:tt)* }
170    ) => {
171        $crate::__antlr4_rust_generated_rule! {
172            @body
173            parser $parser;
174            enter $parser.base.enter_rule($state, $rule);
175            finish finish_rule;
176            abort exit_rule;
177            allow_fallback $allow_fallback;
178            atn $atn;
179            fatal $fatal;
180            retry [$($retry)*];
181            bind ($ctx, $rule_start, $consumed_eof, $sync_error);
182            setup { $($setup)* }
183            body { $($body)* }
184            success { $($success)* }
185            recovery { $($recovery)* }
186        }
187    };
188    (
189        recursive $parser:ident, $state:expr, $rule:expr, $precedence:expr,
190        $allow_fallback:expr, $atn:expr, $fatal:path;
191        retry [$($retry:tt)*];
192        bind ($ctx:ident, $rule_start:ident, $consumed_eof:ident, $sync_error:ident);
193        setup { $($setup:tt)* }
194        body { $($body:tt)* }
195        success { $($success:tt)* }
196        recovery { $($recovery:tt)* }
197    ) => {
198        $crate::__antlr4_rust_generated_rule! {
199            @body
200            parser $parser;
201            enter $parser.base.enter_recursion_rule($state, $rule, $precedence);
202            finish finish_recursion_rule;
203            abort unroll_recursion_context;
204            allow_fallback $allow_fallback;
205            atn $atn;
206            fatal $fatal;
207            retry [$($retry)*];
208            bind ($ctx, $rule_start, $consumed_eof, $sync_error);
209            setup { $($setup)* }
210            body { $($body)* }
211            success { $($success)* }
212            recovery { $($recovery)* }
213        }
214    };
215    (
216        @body
217        parser $parser:ident;
218        enter $enter:expr;
219        finish $finish:ident;
220        abort $abort:ident;
221        allow_fallback $allow_fallback:expr;
222        atn $atn:expr;
223        fatal $fatal:path;
224        retry [$($retry:tt)*];
225        bind ($ctx:ident, $rule_start:ident, $consumed_eof:ident, $sync_error:ident);
226        setup { $($setup:tt)* }
227        body { $($body:tt)* }
228        success { $($success:tt)* }
229        recovery { $($recovery:tt)* }
230    ) => {{
231        let __generated_diagnostic_marker =
232            $parser.base.generated_diagnostics_checkpoint();
233        let mut $ctx = $enter;
234        let $rule_start = $crate::IntStream::index($parser.base.input());
235        $($setup)*
236        let mut $consumed_eof = false;
237        let mut $sync_error: Option<$crate::AntlrError> = None;
238        // The body has its own Result boundary: `?` and `return` exit only this
239        // closure, with errors entering recovery. Parser borrows must not escape it.
240        let __result = (|| -> Result<(), $crate::AntlrError> {
241            $($body)*
242            Ok(())
243        })();
244        match __result {
245            Ok(()) => {
246                $($success)*
247                let __tree = $parser.base.$finish($ctx, $consumed_eof);
248                Ok(__tree)
249            }
250            Err(__error) => {
251                $crate::__antlr4_rust_generated_rule! {
252                    @retry
253                    [$($retry)*]
254                    parser $parser;
255                    marker __generated_diagnostic_marker;
256                    abort $abort;
257                }
258                let __error = if let Some(__sync_error) = $sync_error {
259                    if $allow_fallback {
260                        $parser.base.$abort();
261                        $parser
262                            .base
263                            .rollback_generated_tree(__generated_diagnostic_marker);
264                        $parser.base.record_generated_syntax_error();
265                        return Err($fatal(__sync_error));
266                    }
267                    __sync_error
268                } else {
269                    __error
270                };
271                $parser
272                    .base
273                    .recover_generated_rule(&mut $ctx, $atn, __error);
274                $($recovery)*
275                let __tree = $parser.base.$finish($ctx, $consumed_eof);
276                Ok(__tree)
277            }
278        }
279    }};
280    (
281        @retry
282        [none]
283        parser $parser:ident;
284        marker $marker:ident;
285        abort $abort:ident;
286    ) => {};
287    (
288        @retry
289        [$condition:expr => $retry_error:expr]
290        parser $parser:ident;
291        marker $marker:ident;
292        abort $abort:ident;
293    ) => {
294        if $condition {
295            $parser.base.$abort();
296            $parser.base.restore_generated_diagnostics($marker);
297            return Err($retry_error);
298        }
299    };
300}
301
302/// Pushes invoking state, evaluates a subrule call, discards the marker on
303/// both success and error paths, propagates the error, and appends the child.
304///
305/// Replaces the 5-line generated motif:
306/// ```ignore
307/// let __invoking_marker = self.base.push_invoking_state(STATE);
308/// let __child = CALL;
309/// self.base.discard_invoking_state(__invoking_marker);
310/// let __child = __child?;
311/// self.base.add_parse_child(&mut __ctx, __child);
312/// ```
313///
314/// The macro is necessary because the child call borrows `self` (the generated
315/// parser), which contains `base`, so the push/discard cannot be a single
316/// method call on `BaseParser`.
317#[macro_export]
318#[doc(hidden)]
319macro_rules! __antlr4_rust_invoke_subrule {
320    ($parser:ident, $state:expr, $call:expr, $ctx:ident) => {{
321        let __invoking_marker = $parser.base.push_invoking_state($state);
322        let __child = $call;
323        $parser.base.discard_invoking_state(__invoking_marker);
324        let __child = __child?;
325        $parser.base.add_parse_child(&mut $ctx, __child);
326    }};
327}
328
329/// Receives committed rule enter/exit events during recognition, matching
330/// ANTLR's `addParseListener` contract ([`Parser::add_parse_listener`],
331/// also inherent on [`BaseParser`] and generated parsers).
332///
333/// Events fire on the generated recursive-descent path as rules are entered
334/// and exited, with left-recursive operator loops following upstream's
335/// timing exactly: each loop pass first exits the outgoing iteration
336/// (`recRuleSetPrevCtx`) and then enters the new expansion
337/// (`pushNewRecursionContext` firing `triggerEnterRuleEvent`), so live
338/// listener depth never accumulates across a flat operator chain —
339/// `a + a + … + a` peaks at depth 2 like every ANTLR target. On expansion
340/// events, [`EnterRuleEvent::current`] anchors at the operator-side
341/// lookahead (the token the expansion starts at), whereas Java's
342/// `ctx.start` reaches back to the whole expression's first token — anchor
343/// diagnostics accordingly. Enter events fire in registration order and
344/// exit events in reverse registration order, matching upstream. Enter/exit
345/// calls balance on every completed path, including error recovery and
346/// aborts inside operator loops — with one exception shared with Java: an
347/// ordinary rule's enter that returns `Err` receives no matching exit
348/// (upstream calls `enterRule` outside the generated `try`/`finally`, so a
349/// throwing listener skips `exitRule` the same way). Listener state shared
350/// across parses via `Arc` should be reset after an abort (the unmatched
351/// ordinary-rule enter leaves counters one high).
352///
353/// Divergence from Java to know about: upstream generated rule methods run
354/// only on the committed parse, while this runtime may re-enter a rule while
355/// recovering from a syntax error — such retries deliver additional balanced
356/// enter/exit pairs. Depth counters and resource bounds (the primary use
357/// case) are unaffected; exact once-per-node collectors should prefer the
358/// post-parse tree walker.
359///
360/// `enter_every_rule` is fallible: returning `Err` aborts the parse with
361/// that error. The abort is sticky through rule-level recovery — the parse
362/// fails even when recovery could have produced a tree, mirroring how a
363/// thrown exception escapes ANTLR's `triggerEnterRuleEvent`. Rules the
364/// generator emitted no body for (interpreter-only fallback) do not fire
365/// events; when any parse listener is registered, generated dispatch routes
366/// ATN-preferred rules through their generated bodies so real grammars
367/// observe every rule.
368///
369/// Cost: with no listener registered, dispatch pays one emptiness check per
370/// rule boundary (benchmarked at baseline). With one registered, dispatch
371/// itself is a few percent; on grammars where the generator classified rules
372/// ATN-preferred, the dominant cost is the routing override above — the same
373/// one [`Parser::set_max_rule_depth`] takes — which trades that fast path
374/// for observability. Grammars without ATN-preferred rules (most small DSLs)
375/// pay only the dispatch.
376pub trait ParseListener: Send {
377    /// Called when a generated rule is entered, before its body runs, and
378    /// once per left-recursive operator expansion.
379    ///
380    /// Returning `Err` aborts the parse with the given error.
381    fn enter_every_rule(&mut self, event: &EnterRuleEvent<'_>) -> Result<(), AntlrError>;
382
383    /// Called when a generated rule exits, after its body (and any rule-level
384    /// error recovery) finished, and once per left-recursive operator
385    /// expansion as the rule unrolls.
386    fn exit_every_rule(&mut self, rule_index: usize) {
387        let _ = rule_index;
388    }
389}
390
391/// Boxed listeners forward to their inner implementation, so the boxes
392/// returned by [`Parser::remove_parse_listeners`] can be re-registered
393/// through [`Parser::add_parse_listener`] unchanged.
394impl<T: ParseListener + ?Sized> ParseListener for Box<T> {
395    fn enter_every_rule(&mut self, event: &EnterRuleEvent<'_>) -> Result<(), AntlrError> {
396        (**self).enter_every_rule(event)
397    }
398
399    fn exit_every_rule(&mut self, rule_index: usize) {
400        (**self).exit_every_rule(rule_index);
401    }
402}
403
404/// A rule-entry event delivered to [`ParseListener::enter_every_rule`].
405///
406/// Non-exhaustive so future fields (alt number, invoking state, a context
407/// handle) extend the event without breaking implementors.
408#[derive(Debug)]
409#[non_exhaustive]
410pub struct EnterRuleEvent<'a> {
411    /// Index of the rule being entered (compare against the generated
412    /// `RULE_*` constants).
413    pub rule_index: usize,
414    /// The lookahead token the rule starts at — its line/column/offsets
415    /// anchor listener diagnostics — or `None` at end of input.
416    pub current: Option<TokenView<'a>>,
417}
418
419struct ParseListenerSlot(Box<dyn ParseListener>);
420
421impl std::fmt::Debug for ParseListenerSlot {
422    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
423        f.write_str("ParseListener")
424    }
425}
426/// Probe window for deciding whether clean-pass memo entries are reusable
427/// enough to keep caching. High-cardinality parses mostly produce one-shot
428/// entries; compact ambiguous loops repeatedly hit the same keys.
429const CLEAN_MEMO_PROBE_LIMIT: usize = 4096;
430const CLEAN_MEMO_REPEAT_LIMIT: usize = 8;
431/// Sparse parses periodically reopen the bounded probe so a repeat-heavy
432/// region that starts later in the token stream can promote memoization.
433const CLEAN_MEMO_REPROBE_INTERVAL: usize = 262_144;
434const FAST_RECOGNIZE_VISITING_CAPACITY: usize = 256;
435const FAST_RECOGNIZE_MIN_MEMO_CAPACITY: usize = 256;
436const FAST_RECOGNIZE_MAX_MEMO_CAPACITY: usize = 524_288;
437const FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY: usize = 65_536;
438
439#[derive(Clone, Copy, Debug, Eq, PartialEq)]
440enum CleanMemoMode {
441    Probe,
442    Promote,
443    Sparse,
444}
445
446fn interval_set_contains(intervals: &[(i32, i32)], symbol: i32) -> bool {
447    intervals
448        .iter()
449        .any(|(start, stop)| (*start..=*stop).contains(&symbol))
450}
451
452fn interval_symbols(intervals: &[(i32, i32)]) -> BTreeSet<i32> {
453    let mut symbols = BTreeSet::new();
454    for (start, stop) in intervals {
455        symbols.extend(*start..=*stop);
456    }
457    symbols
458}
459
460fn interval_complement_symbols(
461    intervals: &[(i32, i32)],
462    min_vocabulary: i32,
463    max_vocabulary: i32,
464) -> BTreeSet<i32> {
465    (min_vocabulary..=max_vocabulary)
466        .filter(|symbol| !interval_set_contains(intervals, *symbol))
467        .collect()
468}
469
470#[cfg(feature = "perf-counters")]
471mod perf_counters {
472    use std::cell::Cell;
473    thread_local! {
474        pub(super) static RFS_CALLS: Cell<u64> = const { Cell::new(0) };
475        pub(super) static RFS_MEMO_HITS: Cell<u64> = const { Cell::new(0) };
476        pub(super) static RFS_MEMO_MISSES: Cell<u64> = const { Cell::new(0) };
477        pub(super) static RFS_VISITING_CYCLE: Cell<u64> = const { Cell::new(0) };
478        pub(super) static MEMO_INSERTED: Cell<u64> = const { Cell::new(0) };
479        pub(super) static OUTCOMES_PUSHED: Cell<u64> = const { Cell::new(0) };
480        pub(super) static OUTCOMES_CLONED: Cell<u64> = const { Cell::new(0) };
481        pub(super) static OUTCOME_DEDUPE_INPUTS: Cell<u64> = const { Cell::new(0) };
482        pub(super) static OUTCOME_DEDUPE_REMOVED: Cell<u64> = const { Cell::new(0) };
483        pub(super) static OUTCOME_DEDUPE_INLINE: Cell<u64> = const { Cell::new(0) };
484        pub(super) static OUTCOME_DEDUPE_DENSE: Cell<u64> = const { Cell::new(0) };
485        pub(super) static OUTCOME_DEDUPE_SPARSE: Cell<u64> = const { Cell::new(0) };
486        pub(super) static OUTCOME_DEDUPE_DENSE_WORDS: Cell<u64> = const { Cell::new(0) };
487    }
488    pub(super) fn inc(c: &'static std::thread::LocalKey<Cell<u64>>, n: u64) {
489        c.with(|v| v.set(v.get() + n));
490    }
491    thread_local! {
492        pub(super) static EPSILON_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
493        pub(super) static RULE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
494        pub(super) static ATOM_RANGE_TRANSITIONS: Cell<u64> = const { Cell::new(0) };
495        pub(super) static SINGLE_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
496        pub(super) static MULTI_TRANS_BODY: Cell<u64> = const { Cell::new(0) };
497        pub(super) static SINGLE_TRANS_RULE: Cell<u64> = const { Cell::new(0) };
498        pub(super) static SINGLE_TRANS_ATOM: Cell<u64> = const { Cell::new(0) };
499        pub(super) static SINGLE_TRANS_OTHER: Cell<u64> = const { Cell::new(0) };
500        pub(super) static OUTCOMES_RETURN_0: Cell<u64> = const { Cell::new(0) };
501        pub(super) static OUTCOMES_RETURN_1: Cell<u64> = const { Cell::new(0) };
502        pub(super) static OUTCOMES_RETURN_N: Cell<u64> = const { Cell::new(0) };
503    }
504    pub(super) fn snapshot() -> [(&'static str, u64); 24] {
505        [
506            ("rfs_calls", RFS_CALLS.with(Cell::get)),
507            ("rfs_memo_hits", RFS_MEMO_HITS.with(Cell::get)),
508            ("rfs_memo_misses", RFS_MEMO_MISSES.with(Cell::get)),
509            ("rfs_visiting_cycle", RFS_VISITING_CYCLE.with(Cell::get)),
510            ("memo_inserted", MEMO_INSERTED.with(Cell::get)),
511            ("outcomes_pushed", OUTCOMES_PUSHED.with(Cell::get)),
512            ("outcomes_cloned", OUTCOMES_CLONED.with(Cell::get)),
513            (
514                "outcome_dedupe_inputs",
515                OUTCOME_DEDUPE_INPUTS.with(Cell::get),
516            ),
517            (
518                "outcome_dedupe_removed",
519                OUTCOME_DEDUPE_REMOVED.with(Cell::get),
520            ),
521            (
522                "outcome_dedupe_inline",
523                OUTCOME_DEDUPE_INLINE.with(Cell::get),
524            ),
525            ("outcome_dedupe_dense", OUTCOME_DEDUPE_DENSE.with(Cell::get)),
526            (
527                "outcome_dedupe_sparse",
528                OUTCOME_DEDUPE_SPARSE.with(Cell::get),
529            ),
530            (
531                "outcome_dedupe_dense_words",
532                OUTCOME_DEDUPE_DENSE_WORDS.with(Cell::get),
533            ),
534            ("epsilon_transitions", EPSILON_TRANSITIONS.with(Cell::get)),
535            ("rule_transitions", RULE_TRANSITIONS.with(Cell::get)),
536            (
537                "atom_range_transitions",
538                ATOM_RANGE_TRANSITIONS.with(Cell::get),
539            ),
540            ("single_trans_body", SINGLE_TRANS_BODY.with(Cell::get)),
541            ("multi_trans_body", MULTI_TRANS_BODY.with(Cell::get)),
542            ("single_trans_rule", SINGLE_TRANS_RULE.with(Cell::get)),
543            ("single_trans_atom", SINGLE_TRANS_ATOM.with(Cell::get)),
544            ("single_trans_other", SINGLE_TRANS_OTHER.with(Cell::get)),
545            ("outcomes_return_0", OUTCOMES_RETURN_0.with(Cell::get)),
546            ("outcomes_return_1", OUTCOMES_RETURN_1.with(Cell::get)),
547            ("outcomes_return_n", OUTCOMES_RETURN_N.with(Cell::get)),
548        ]
549    }
550    pub fn reset() {
551        RFS_CALLS.with(|c| c.set(0));
552        RFS_MEMO_HITS.with(|c| c.set(0));
553        RFS_MEMO_MISSES.with(|c| c.set(0));
554        RFS_VISITING_CYCLE.with(|c| c.set(0));
555        MEMO_INSERTED.with(|c| c.set(0));
556        OUTCOMES_PUSHED.with(|c| c.set(0));
557        OUTCOMES_CLONED.with(|c| c.set(0));
558        OUTCOME_DEDUPE_INPUTS.with(|c| c.set(0));
559        OUTCOME_DEDUPE_REMOVED.with(|c| c.set(0));
560        OUTCOME_DEDUPE_INLINE.with(|c| c.set(0));
561        OUTCOME_DEDUPE_DENSE.with(|c| c.set(0));
562        OUTCOME_DEDUPE_SPARSE.with(|c| c.set(0));
563        OUTCOME_DEDUPE_DENSE_WORDS.with(|c| c.set(0));
564        EPSILON_TRANSITIONS.with(|c| c.set(0));
565        RULE_TRANSITIONS.with(|c| c.set(0));
566        ATOM_RANGE_TRANSITIONS.with(|c| c.set(0));
567        SINGLE_TRANS_BODY.with(|c| c.set(0));
568        MULTI_TRANS_BODY.with(|c| c.set(0));
569        SINGLE_TRANS_RULE.with(|c| c.set(0));
570        SINGLE_TRANS_ATOM.with(|c| c.set(0));
571        SINGLE_TRANS_OTHER.with(|c| c.set(0));
572        OUTCOMES_RETURN_0.with(|c| c.set(0));
573        OUTCOMES_RETURN_1.with(|c| c.set(0));
574        OUTCOMES_RETURN_N.with(|c| c.set(0));
575    }
576    pub fn dump() {
577        for (name, value) in snapshot() {
578            #[allow(clippy::print_stderr)]
579            {
580                eprintln!("perf {name}={value}");
581            }
582        }
583    }
584}
585
586#[cfg(feature = "perf-counters")]
587pub use perf_counters::{dump as dump_perf_counters, reset as reset_perf_counters};
588/// Preserve lazy lexing for short or failing inputs, but eagerly fill once the
589/// fast recognizer has probed far enough that per-token stream sync dominates.
590/// Sixty-four tokens is a small rule-sized window: it keeps startup lazy while
591/// switching long inputs to the cheaper filled-stream path before large fanout.
592const FAST_RECOGNIZER_DEFERRED_FILL_AT: usize = 64;
593/// Parser semantic action reached while recognizing one ATN path.
594///
595/// Generated parsers use `source_state` to dispatch back to the grammar action
596/// rendered for that ATN action transition. The token interval is the current
597/// rule's input span at the action site, which covers common target templates
598/// such as `$text`. Rule-init actions do not have an ATN action source state,
599/// so they are marked separately and may carry an ATN state for expected-token
600/// rendering.
601#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
602pub struct ParserAction {
603    source_state: usize,
604    rule_index: usize,
605    action_index: Option<usize>,
606    start_index: usize,
607    stop_index: Option<usize>,
608    rule_init: bool,
609    expected_state: Option<usize>,
610}
611
612impl ParserAction {
613    /// Creates an action event for a recognized parser path.
614    pub const fn new(
615        source_state: usize,
616        rule_index: usize,
617        start_index: usize,
618        stop_index: Option<usize>,
619    ) -> Self {
620        Self {
621            source_state,
622            rule_index,
623            action_index: None,
624            start_index,
625            stop_index,
626            rule_init: false,
627            expected_state: None,
628        }
629    }
630
631    /// Creates an indexed action event for a recognized parser path.
632    pub const fn new_indexed(
633        source_state: usize,
634        rule_index: usize,
635        action_index: usize,
636        start_index: usize,
637        stop_index: Option<usize>,
638    ) -> Self {
639        Self {
640            source_state,
641            rule_index,
642            action_index: Some(action_index),
643            start_index,
644            stop_index,
645            rule_init: false,
646            expected_state: None,
647        }
648    }
649
650    /// Creates an action event for a rule-level `@init` action.
651    pub const fn new_rule_init(
652        rule_index: usize,
653        start_index: usize,
654        expected_state: Option<usize>,
655    ) -> Self {
656        Self {
657            source_state: usize::MAX,
658            rule_index,
659            action_index: None,
660            start_index,
661            stop_index: None,
662            rule_init: true,
663            expected_state,
664        }
665    }
666
667    /// ATN state that owns the semantic-action transition.
668    pub const fn source_state(&self) -> usize {
669        self.source_state
670    }
671
672    /// Grammar rule index recorded by the serialized ATN action transition.
673    pub const fn rule_index(&self) -> usize {
674        self.rule_index
675    }
676
677    /// Stable source-order action index in the grammar.
678    pub const fn action_index(&self) -> Option<usize> {
679        self.action_index
680    }
681
682    /// Token-stream index where the active rule began.
683    pub const fn start_index(&self) -> usize {
684        self.start_index
685    }
686
687    /// Last token-stream index consumed before the action was reached.
688    pub const fn stop_index(&self) -> Option<usize> {
689        self.stop_index
690    }
691
692    /// Reports whether this event represents a rule-level `@init` action.
693    pub const fn is_rule_init(&self) -> bool {
694        self.rule_init
695    }
696
697    /// ATN state used to compute expected-token display for this action.
698    pub const fn expected_state(&self) -> Option<usize> {
699        self.expected_state
700    }
701}
702
703/// Runtime view passed to parser semantic hooks.
704///
705/// The context is intentionally read-only with respect to parser structure:
706/// predicates may run speculatively during prediction, and hooks can be called
707/// more than once for paths that are later abandoned. Lookahead methods may
708/// buffer tokens from the underlying token source, matching normal parser
709/// prediction behavior.
710pub struct ParserSemCtx<'a, S>
711where
712    S: TokenSource,
713{
714    input: &'a mut CommonTokenStream<S>,
715    tree_storage: &'a ParseTreeStorage,
716    rule_index: usize,
717    coordinate_index: usize,
718    rule_name: Option<String>,
719    context: Option<&'a ParserRuleContext>,
720    tree: Option<ParseTree>,
721    local_int_arg: Option<(usize, i64)>,
722    member_values: &'a MemberEnv,
723    action: Option<ParserAction>,
724}
725
726impl<S> std::fmt::Debug for ParserSemCtx<'_, S>
727where
728    S: TokenSource,
729{
730    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
731        f.debug_struct("ParserSemCtx")
732            .field("rule_index", &self.rule_index)
733            .field("coordinate_index", &self.coordinate_index)
734            .field("rule_name", &self.rule_name)
735            .field("context", &self.context)
736            .field("tree", &self.tree)
737            .field("local_int_arg", &self.local_int_arg)
738            .field("member_values", &self.member_values)
739            .field("action", &self.action)
740            .finish_non_exhaustive()
741    }
742}
743
744impl<'a, S> ParserSemCtx<'a, S>
745where
746    S: TokenSource,
747{
748    /// Rule index that owns the predicate/action coordinate.
749    #[must_use]
750    pub const fn rule_index(&self) -> usize {
751        self.rule_index
752    }
753
754    /// Rule name that owns the coordinate, when recognizer metadata has it.
755    #[must_use]
756    pub fn rule_name(&self) -> Option<&str> {
757        self.rule_name.as_deref()
758    }
759
760    /// Predicate/action index inside the owning rule. Legacy parser actions
761    /// without source-index metadata report `usize::MAX`.
762    #[must_use]
763    pub const fn coordinate_index(&self) -> usize {
764        self.coordinate_index
765    }
766
767    /// Current token-stream index.
768    #[must_use]
769    pub fn input_index(&self) -> usize {
770        self.input.index()
771    }
772
773    /// Token type at one-based lookahead/lookbehind offset.
774    pub fn la(&mut self, offset: isize) -> i32 {
775        self.input.la(offset)
776    }
777
778    /// Token at one-based lookahead/lookbehind offset.
779    pub fn lt(&self, offset: isize) -> Option<TokenView<'_>> {
780        self.input.lt(offset)
781    }
782
783    /// Borrowing token view for text inspection at a one-based offset.
784    pub fn token_text(&self, offset: isize) -> Option<TokenView<'_>> {
785        self.lt(offset)
786    }
787
788    /// Token at an absolute buffered index, including hidden/custom channels.
789    ///
790    /// Unlike [`Self::lt`], this does not apply the token stream's channel
791    /// filter and does not move its cursor. It is intended for semantic helpers
792    /// such as automatic-semicolon-insertion checks that inspect trivia
793    /// immediately before the current visible token.
794    pub fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
795        self.input.get(index)
796    }
797
798    /// Current generated rule context, when a generated rule predicate supplied
799    /// one.
800    #[must_use]
801    pub const fn context(&self) -> Option<&'a ParserRuleContext> {
802        self.context
803    }
804
805    /// Flat tree storage containing completed children visible to this hook.
806    #[must_use]
807    pub const fn parse_tree_storage(&self) -> &'a ParseTreeStorage {
808        self.tree_storage
809    }
810
811    /// Canonical token store used by completed flat-tree nodes.
812    #[must_use]
813    pub const fn token_store(&self) -> &TokenStore {
814        self.input.token_store()
815    }
816
817    /// Completed parse-tree root ID passed to a replayed action hook.
818    #[must_use]
819    pub const fn tree_id(&self) -> Option<NodeId> {
820        self.tree
821    }
822
823    /// Completed parse tree passed to an action hook, if the action is being
824    /// replayed after recognition.
825    #[must_use]
826    pub fn tree(&self) -> Option<Node<'_>> {
827        self.tree
828            .and_then(|id| self.tree_storage.node(self.input.token_store(), id))
829    }
830
831    /// Integer local argument visible to this predicate coordinate.
832    #[must_use]
833    pub fn local_int_arg(&self) -> Option<i64> {
834        self.local_int_arg.map(|(_, value)| value)
835    }
836
837    /// Integer member value observed on the current speculative path.
838    #[must_use]
839    pub fn member_int(&self, member: usize) -> Option<i64> {
840        self.member_values.scalar(member)
841    }
842
843    /// Top of a stack-valued member slot on the current speculative path;
844    /// `None` when the stack is empty or was never pushed.
845    #[must_use]
846    pub fn member_stack_top(&self, member: usize) -> Option<i64> {
847        self.member_values.stack_top(member)
848    }
849
850    /// Depth of a stack-valued member slot on the current speculative path.
851    #[must_use]
852    pub fn member_stack_len(&self, member: usize) -> usize {
853        self.member_values.stack_len(member)
854    }
855
856    /// Parser action event being replayed, when this context belongs to an
857    /// action hook.
858    #[must_use]
859    pub const fn action(&self) -> Option<ParserAction> {
860        self.action
861    }
862
863    /// Text covered by a parser action event.
864    ///
865    /// Mirrors [`BaseParser::text_interval`] / `$text`: when the stop token is
866    /// EOF the interval ends at the previous *visible* token, so trailing hidden
867    /// tokens (and the EOF marker) are excluded rather than blindly subtracting
868    /// one, which could point at hidden whitespace. `CommonTokenStream::text`
869    /// itself guards `start > stop`, so an empty interval yields `""`.
870    pub fn action_text(&self) -> String {
871        let Some(action) = self.action else {
872            return String::new();
873        };
874        let Some(stop) = action.stop_index() else {
875            return String::new();
876        };
877        let stop = if self
878            .input
879            .get(stop)
880            .is_some_and(|token| token.token_type() == TOKEN_EOF)
881        {
882            let Some(previous) = self.input.previous_visible_token_index(stop) else {
883                return String::new();
884            };
885            previous
886        } else {
887            stop
888        };
889        self.input.text(action.start_index(), stop)
890    }
891}
892
893/// User extension point for parser semantic predicates and actions that the
894/// metadata generator did not translate into built-in runtime metadata.
895///
896/// Returning `None`/`false` says "not handled", so the runtime falls through
897/// to the configured [`UnknownSemanticPolicy`]. Predicate hooks may run during
898/// speculative prediction and must be replay-safe.
899pub trait SemanticHooks {
900    /// Whether generated lexers should route lifecycle callbacks through this
901    /// hook object.
902    ///
903    /// User hook implementations opt in by default. [`NoSemanticHooks`]
904    /// overrides this to keep generated lexers on the direct no-extension
905    /// token path.
906    const ENABLES_LEXER_LIFECYCLE: bool = true;
907
908    /// Whether this hook object may observe parser predicate transitions.
909    ///
910    /// Custom hooks default to conservative predicate handling so the fast
911    /// recognizer does not bypass a `sempred` implementation.
912    fn observes_parser_predicates(&self) -> bool {
913        true
914    }
915
916    /// Whether this hook object may override interpreted parser decisions.
917    ///
918    /// This remains disabled by default so ordinary generated parsers retain
919    /// the fast recognizer path.
920    fn observes_parser_decisions(&self) -> bool {
921        false
922    }
923
924    /// Overrides one interpreted parser decision with a one-based alternative.
925    ///
926    /// Returning `None` leaves normal adaptive prediction in control. Hooks
927    /// that return an alternative own any one-shot or input-index filtering
928    /// they require.
929    fn parser_decision_override(
930        &mut self,
931        decision: usize,
932        input_index: usize,
933        alternative_count: usize,
934    ) -> Option<usize> {
935        let _ = (decision, input_index, alternative_count);
936        None
937    }
938
939    fn sempred<S>(
940        &mut self,
941        ctx: &mut ParserSemCtx<'_, S>,
942        rule_index: usize,
943        pred_index: usize,
944    ) -> Option<bool>
945    where
946        S: TokenSource,
947    {
948        let _ = (ctx, rule_index, pred_index);
949        None
950    }
951
952    fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
953    where
954        S: TokenSource,
955    {
956        let _ = (ctx, action);
957        false
958    }
959
960    fn lexer_sempred<I>(
961        &mut self,
962        ctx: &mut LexerSemCtx<'_, I>,
963        rule_index: usize,
964        pred_index: usize,
965    ) -> Option<bool>
966    where
967        I: CharStream,
968    {
969        let _ = (ctx, rule_index, pred_index);
970        None
971    }
972
973    /// Runs a lexer custom action on the committed lexing path. Returns whether
974    /// the hook handled the action.
975    ///
976    /// The action runs post-accept, so `ctx` carries a mutable lexer borrow: a
977    /// hook may change lexer state, including [`LexerSemCtx::set_type`],
978    /// [`LexerSemCtx::set_channel`], mode changes, input consumption, and
979    /// queued prefix tokens, just like the closure-based `custom_action` API.
980    /// (The speculative predicate context in [`Self::lexer_sempred`] is a shared
981    /// borrow, so those mutators are inert there.)
982    fn lexer_action<I>(&mut self, ctx: &mut LexerSemCtx<'_, I>, action: LexerCustomAction) -> bool
983    where
984        I: CharStream,
985    {
986        let _ = (ctx, action);
987        false
988    }
989
990    /// Runs after runtime-owned lexer state has been reset for reuse.
991    ///
992    /// Implementations should clear extension-owned transient state here.
993    fn lexer_reset<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
994    where
995        I: CharStream,
996    {
997        let _ = ctx;
998    }
999
1000    /// Runs before the runtime returns a queued token or starts a new ATN
1001    /// token match.
1002    ///
1003    /// The callback also runs between internal `skip`/`more` matches, so it
1004    /// observes every point where another ATN match may start.
1005    fn lexer_before_token<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
1006    where
1007        I: CharStream,
1008    {
1009        let _ = ctx;
1010    }
1011
1012    /// Runs after the accepted path's portable and custom actions, but before
1013    /// the token span is finalized and emitted.
1014    ///
1015    /// Accepted paths that selected `skip` or `more` are included, and the hook
1016    /// may observe or override that pending token type.
1017    ///
1018    /// This callback has no synthetic ATN coordinate. It therefore also runs
1019    /// for accepted rules that contain no action or predicate.
1020    fn lexer_after_accept<I>(&mut self, ctx: &mut LexerLifecycleCtx<'_, I>)
1021    where
1022        I: CharStream,
1023    {
1024        let _ = ctx;
1025    }
1026
1027    /// Observes a token after committed lexer actions and portable commands
1028    /// have run and the token has been emitted, immediately before it is
1029    /// returned to the token stream.
1030    ///
1031    /// Hidden and custom-channel tokens are included. `skip` and intermediate
1032    /// `more` matches do not produce callbacks.
1033    fn lexer_token_emitted(&mut self, token: TokenView<'_>) {
1034        let _ = token;
1035    }
1036}
1037
1038/// Default hook object used by parsers that do not need user-supplied
1039/// semantics.
1040#[derive(Clone, Copy, Debug, Default)]
1041pub struct NoSemanticHooks;
1042
1043impl SemanticHooks for NoSemanticHooks {
1044    const ENABLES_LEXER_LIFECYCLE: bool = false;
1045
1046    fn observes_parser_predicates(&self) -> bool {
1047        false
1048    }
1049}
1050
1051/// Parser semantic predicate rendered from a supported target template.
1052///
1053/// The metadata recognizer evaluates these at the token-stream index where the
1054/// predicate transition is reached. Unsupported or absent predicate templates
1055/// remain unconditional so existing generated parsers keep their previous
1056/// behavior unless the generator opts into this table.
1057#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1058pub enum ParserPredicate {
1059    True,
1060    False,
1061    /// Predicate that always fails and carries ANTLR's `<fail='...'>` message.
1062    FalseWithMessage {
1063        message: &'static str,
1064    },
1065    /// Target-template test helper that reports predicate evaluation before
1066    /// returning the wrapped boolean value.
1067    Invoke {
1068        value: bool,
1069    },
1070    LookaheadTextEquals {
1071        offset: isize,
1072        text: &'static str,
1073    },
1074    LookaheadNotEquals {
1075        offset: isize,
1076        token_type: i32,
1077    },
1078    /// Checks that the last two consumed visible tokens were adjacent in the
1079    /// token stream. Used by C# parser predicates for split operator tokens.
1080    TokenPairAdjacent,
1081    /// Checks a generated parser context child by rule index and text.
1082    ///
1083    /// If the child is absent the predicate succeeds, matching target helpers
1084    /// that treat incomplete or non-matching contexts as non-restrictive.
1085    ContextChildRuleTextNotEquals {
1086        rule_index: usize,
1087        text: &'static str,
1088    },
1089    /// Compares the current rule invocation's integer argument with a literal
1090    /// value from a supported `ValEquals("$i", "...")` target template.
1091    LocalIntEquals {
1092        value: i64,
1093    },
1094    /// Checks ANTLR-style raw predicates like `5 >= $_p` against the current
1095    /// rule invocation's integer argument.
1096    LocalIntLessOrEqual {
1097        value: i64,
1098    },
1099    /// Compares a generated parser integer member modulo a literal value.
1100    MemberModuloEquals {
1101        member: usize,
1102        modulus: i64,
1103        value: i64,
1104        equals: bool,
1105    },
1106    /// Compares a generated parser integer member with a literal value.
1107    MemberEquals {
1108        member: usize,
1109        value: i64,
1110        equals: bool,
1111    },
1112}
1113
1114impl ParserPredicate {
1115    /// Lowers the legacy predicate metadata variant into `SemIR`.
1116    ///
1117    /// This is the compatibility adapter for generated parsers produced while
1118    /// the runtime still emitted closed enum tables. Newer generated parsers
1119    /// emit `SemIR` directly.
1120    pub fn lower_into_semir(self, ir: &mut SemIr) -> ExprId {
1121        match self {
1122            Self::True => ir.expr(PExpr::Bool(true)),
1123            Self::False | Self::FalseWithMessage { .. } => ir.expr(PExpr::Bool(false)),
1124            Self::Invoke { value } => ir.expr(PExpr::EvalTrace(value)),
1125            Self::LookaheadTextEquals { offset, text } => {
1126                let token = ir.expr(PExpr::TokenText(offset));
1127                let text = ir.intern(text);
1128                let text = ir.expr(PExpr::Str(text));
1129                ir.expr(PExpr::Cmp(CmpOp::Eq, token, text))
1130            }
1131            Self::LookaheadNotEquals { offset, token_type } => {
1132                let actual = ir.expr(PExpr::La(offset));
1133                let expected = ir.expr(PExpr::Int(i64::from(token_type)));
1134                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
1135            }
1136            Self::TokenPairAdjacent => ir.expr(PExpr::TokenIndexAdjacent),
1137            Self::ContextChildRuleTextNotEquals { rule_index, text } => {
1138                let actual = ir.expr(PExpr::CtxRuleText(rule_index));
1139                let expected = ir.intern(text);
1140                let expected = ir.expr(PExpr::Str(expected));
1141                ir.expr(PExpr::Cmp(CmpOp::Ne, actual, expected))
1142            }
1143            Self::LocalIntEquals { value } => local_arg_comparison(ir, CmpOp::Eq, value),
1144            Self::LocalIntLessOrEqual { value } => local_arg_comparison(ir, CmpOp::Le, value),
1145            Self::MemberModuloEquals {
1146                member,
1147                modulus,
1148                value,
1149                equals,
1150            } => {
1151                if modulus == 0 {
1152                    return ir.expr(PExpr::Bool(false));
1153                }
1154                let member = ir.expr(PExpr::Member(member));
1155                let modulus = ir.expr(PExpr::Int(modulus));
1156                let actual = ir.expr(PExpr::Arith(ArithOp::Mod, member, modulus));
1157                let expected = ir.expr(PExpr::Int(value));
1158                ir.expr(PExpr::Cmp(
1159                    if equals { CmpOp::Eq } else { CmpOp::Ne },
1160                    actual,
1161                    expected,
1162                ))
1163            }
1164            Self::MemberEquals {
1165                member,
1166                value,
1167                equals,
1168            } => {
1169                let actual = ir.expr(PExpr::Member(member));
1170                let expected = ir.expr(PExpr::Int(value));
1171                ir.expr(PExpr::Cmp(
1172                    if equals { CmpOp::Eq } else { CmpOp::Ne },
1173                    actual,
1174                    expected,
1175                ))
1176            }
1177        }
1178    }
1179
1180    #[must_use]
1181    pub const fn failure_message(self) -> Option<&'static str> {
1182        match self {
1183            Self::FalseWithMessage { message } => Some(message),
1184            Self::True
1185            | Self::False
1186            | Self::Invoke { .. }
1187            | Self::LookaheadTextEquals { .. }
1188            | Self::LookaheadNotEquals { .. }
1189            | Self::TokenPairAdjacent
1190            | Self::ContextChildRuleTextNotEquals { .. }
1191            | Self::LocalIntEquals { .. }
1192            | Self::LocalIntLessOrEqual { .. }
1193            | Self::MemberModuloEquals { .. }
1194            | Self::MemberEquals { .. } => None,
1195        }
1196    }
1197}
1198
1199fn local_arg_comparison(ir: &mut SemIr, op: CmpOp, value: i64) -> ExprId {
1200    let local = ir.expr(PExpr::LocalArg);
1201    let absent = ir.expr(PExpr::IsNull(local));
1202    let expected = ir.expr(PExpr::Int(value));
1203    let comparison = ir.expr(PExpr::Cmp(op, local, expected));
1204    ir.expr(PExpr::Or([absent, comparison].into()))
1205}
1206
1207/// Policy for semantic predicate coordinates that have no runtime
1208/// implementation.
1209///
1210/// ANTLR grammars may embed target-language predicates that the metadata
1211/// generator could not translate into a [`ParserPredicate`] table entry. When
1212/// recognition reaches such a coordinate the runtime cannot know the grammar
1213/// author's intent, so the caller chooses how to proceed.
1214///
1215/// The default is [`Self::AssumeTrue`], matching the historical behavior of
1216/// this runtime. That default is deprecated and will change to [`Self::Error`]
1217/// in a future minor release; grammars relying on unconditional predicates
1218/// should opt in explicitly.
1219#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1220pub enum UnknownSemanticPolicy {
1221    /// Treat the predicate as passing, as if it were absent from the grammar.
1222    #[default]
1223    AssumeTrue,
1224    /// Treat the predicate as failing, removing the guarded alternative.
1225    AssumeFalse,
1226    /// Fail the parse with [`AntlrError::Unsupported`] naming every unknown
1227    /// coordinate that recognition evaluated.
1228    Error,
1229}
1230
1231/// Resolves a predicate coordinate that neither a translated table entry nor a
1232/// user hook could answer, applying the active [`UnknownSemanticPolicy`].
1233///
1234/// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded in `hits`
1235/// so the parse entry can surface every unresolved coordinate afterwards. Both
1236/// the legacy [`ParserPredicate`] path and the [`semir::PExpr::Hook`] path
1237/// funnel through here so a missing implementation is never silently coerced
1238/// to a boolean (design goal G1: never silently mis-parse).
1239fn apply_unknown_predicate_policy(
1240    policy: UnknownSemanticPolicy,
1241    rule_index: usize,
1242    pred_index: usize,
1243    hits: &mut Vec<(usize, usize)>,
1244) -> bool {
1245    match policy {
1246        UnknownSemanticPolicy::AssumeTrue => true,
1247        UnknownSemanticPolicy::AssumeFalse => false,
1248        UnknownSemanticPolicy::Error => {
1249            let coordinate = (rule_index, pred_index);
1250            if !hits.contains(&coordinate) {
1251                hits.push(coordinate);
1252            }
1253            false
1254        }
1255    }
1256}
1257
1258/// Interval-set of expected token types, displayable through a vocabulary —
1259/// the shape ANTLR's `getExpectedTokens().toString(vocabulary)` exposes to
1260/// generated test actions.
1261#[derive(Clone, Debug, Eq, PartialEq)]
1262pub struct ExpectedTokenSet {
1263    symbols: BTreeSet<i32>,
1264}
1265
1266impl ExpectedTokenSet {
1267    /// Formats the set using ANTLR token display names, e.g. `{'a', 'b'}`.
1268    #[must_use]
1269    pub fn to_token_string(&self, vocabulary: &Vocabulary) -> String {
1270        expected_symbols_display(&self.symbols, vocabulary)
1271    }
1272}
1273
1274/// Marker error strategy matching ANTLR's `BailErrorStrategy`.
1275///
1276/// The first syntax error aborts the parse instead of recovering. Generated
1277/// recognizers accept it through `set_error_handler(BailErrorStrategy::new())`.
1278#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1279pub struct BailErrorStrategy;
1280
1281impl BailErrorStrategy {
1282    #[must_use]
1283    pub const fn new() -> Self {
1284        Self
1285    }
1286}
1287
1288/// Prediction strategy requested by generated parser harnesses.
1289#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1290pub enum PredictionMode {
1291    /// Prefer the clean full-context outcome when alternatives reach the same
1292    /// input position.
1293    Ll,
1294    /// Preserve SLL's first-viable alternative bias at a decision, even when a
1295    /// later full-context alternative could avoid recovery.
1296    Sll,
1297    /// Full LL prediction with exact ambiguity detection for diagnostic runs.
1298    LlExactAmbigDetection,
1299}
1300
1301/// Integer argument metadata for a generated parser rule invocation.
1302///
1303/// ANTLR's serialized ATN does not retain Rust-target rule argument values, so
1304/// the generator records the rule-transition source state and the value that
1305/// should be visible to semantic predicates inside the callee.
1306#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1307pub struct ParserRuleArg {
1308    /// ATN state containing the rule transition that receives this argument.
1309    pub source_state: usize,
1310    /// Callee rule index for the transition.
1311    pub rule_index: usize,
1312    /// Literal fallback value to expose in the callee.
1313    pub value: i64,
1314    /// Whether the callee should inherit the caller's current integer argument.
1315    pub inherit_local: bool,
1316}
1317
1318/// Integer member mutation attached to an ATN action transition.
1319#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1320pub struct ParserMemberAction {
1321    /// ATN state containing the action transition.
1322    pub source_state: usize,
1323    /// Generator-assigned integer member id.
1324    pub member: usize,
1325    /// Delta applied when the action is reached on one speculative path.
1326    pub delta: i64,
1327}
1328
1329/// Integer return-value assignment attached to an ATN action transition.
1330///
1331/// Generated parsers use this metadata when target actions assign a simple
1332/// return field such as `$y=1000;`. The interpreter applies it while selecting
1333/// the recognized path so the finished parse tree can answer later
1334/// `$label.y` action templates.
1335#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1336pub struct ParserReturnAction {
1337    /// ATN state containing the action transition.
1338    pub source_state: usize,
1339    /// Rule index recorded by the serialized action transition.
1340    pub rule_index: usize,
1341    /// Return-field name as it appears in the grammar.
1342    pub name: &'static str,
1343    /// Literal integer value assigned by the action.
1344    pub value: i64,
1345}
1346
1347impl ParserMemberAction {
1348    /// Lowers this speculative member mutation into a `SemIR` action.
1349    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
1350        let delta = ir.expr(PExpr::Int(self.delta));
1351        ParserSemanticAction {
1352            source_state: self.source_state,
1353            rule_index: usize::MAX,
1354            stmt: ir.stmt(AStmt::AddMember(self.member, delta)),
1355            speculative: true,
1356        }
1357    }
1358}
1359
1360impl ParserReturnAction {
1361    /// Lowers this committed return-value assignment into a `SemIR` action.
1362    pub fn lower_into_semir(self, ir: &mut SemIr) -> ParserSemanticAction {
1363        let name = ir.intern(self.name);
1364        let value = ir.expr(PExpr::Int(self.value));
1365        ParserSemanticAction {
1366            source_state: self.source_state,
1367            rule_index: self.rule_index,
1368            stmt: ir.stmt(AStmt::SetReturn(name, value)),
1369            speculative: false,
1370        }
1371    }
1372}
1373
1374/// Parser predicate coordinate lowered into [`SemIr`].
1375#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1376pub struct ParserSemanticPredicate {
1377    /// Serialized rule index that owns this predicate.
1378    pub rule_index: usize,
1379    /// Predicate index inside the owning rule.
1380    pub pred_index: usize,
1381    /// Root expression in the associated [`ParserSemantics::ir`] arena.
1382    pub expr: ExprId,
1383    /// ANTLR `<fail='...'>` message for predicates that intentionally fail.
1384    pub failure_message: Option<&'static str>,
1385}
1386
1387/// Parser action coordinate lowered into [`SemIr`].
1388#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1389pub struct ParserSemanticAction {
1390    /// ATN state containing the action transition.
1391    pub source_state: usize,
1392    /// Serialized rule index recorded by the action transition.
1393    pub rule_index: usize,
1394    /// Root statement in the associated [`ParserSemantics::ir`] arena.
1395    pub stmt: StmtId,
1396    /// Whether this action may run on speculative recognition paths.
1397    pub speculative: bool,
1398}
1399
1400/// Data-driven semantic tables emitted by generated parsers.
1401///
1402/// This is the runtime representation for issue #9's `SemIR` path. Existing
1403/// `ParserPredicate`, `ParserMemberAction`, and `ParserReturnAction` tables
1404/// remain accepted as deprecated adapters for generated code produced before
1405/// this table existed.
1406#[derive(Clone, Debug, Default, Eq, PartialEq)]
1407pub struct ParserSemantics {
1408    pub ir: SemIr,
1409    pub predicates: Vec<ParserSemanticPredicate>,
1410    pub actions: Vec<ParserSemanticAction>,
1411}
1412
1413/// Optional generated-runtime metadata for metadata-driven parser execution.
1414#[derive(Clone, Copy, Debug, Default)]
1415pub struct ParserRuntimeOptions<'a> {
1416    /// Rule indexes whose `@init` actions should run at rule entry or be
1417    /// returned for legacy replay when no semantic hook handles them.
1418    pub init_action_rules: &'a [usize],
1419    /// Stable parser-action indexes keyed by authored ATN source state.
1420    ///
1421    /// A non-empty table selects committed interpreted execution: mapped
1422    /// actions run at their grammar position instead of being replayed after
1423    /// the complete rule has been recognized.
1424    pub action_indices: &'a [(usize, usize)],
1425    /// Whether generated parse-tree contexts should retain alternative numbers.
1426    pub track_alt_numbers: bool,
1427    /// Whether generated typed contexts should retain private dispatch alternatives.
1428    ///
1429    /// Unlike `track_alt_numbers`, this metadata does not affect the public
1430    /// alternative number or parse-tree rendering.
1431    #[doc(hidden)]
1432    pub track_context_alt_numbers: bool,
1433    /// Semantic predicate table keyed by serialized `(rule_index, pred_index)`.
1434    pub predicates: &'a [(usize, usize, ParserPredicate)],
1435    /// `SemIR` predicate/action table emitted by newer generated parsers.
1436    pub semantics: Option<&'a ParserSemantics>,
1437    /// Rule-call integer argument table keyed by ATN source state.
1438    pub rule_args: &'a [ParserRuleArg],
1439    /// Integer member mutations keyed by ATN action source state.
1440    pub member_actions: &'a [ParserMemberAction],
1441    /// Integer return assignments keyed by ATN action source state.
1442    pub return_actions: &'a [ParserReturnAction],
1443    /// How to evaluate semantic predicate coordinates absent from
1444    /// `predicates`.
1445    pub unknown_predicate_policy: UnknownSemanticPolicy,
1446}
1447
1448pub trait Parser: Recognizer {
1449    /// Reports whether generated parser rules should build parse-tree nodes
1450    /// while recognizing input.
1451    fn build_parse_trees(&self) -> bool;
1452
1453    /// Enables or disables parse-tree construction for subsequent rule calls.
1454    fn set_build_parse_trees(&mut self, build: bool);
1455
1456    /// Returns the number of parser syntax errors recorded by committed parse
1457    /// paths so far.
1458    fn number_of_syntax_errors(&self) -> usize {
1459        0
1460    }
1461
1462    /// Reports whether prediction diagnostic-listener messages are emitted
1463    /// during parser ATN recognition.
1464    fn report_diagnostic_errors(&self) -> bool {
1465        false
1466    }
1467
1468    /// Enables or disables ANTLR-style prediction diagnostics for subsequent
1469    /// rule calls.
1470    fn set_report_diagnostic_errors(&mut self, _report: bool) {}
1471
1472    /// Reports the prediction strategy used when selecting among alternatives.
1473    fn prediction_mode(&self) -> PredictionMode {
1474        PredictionMode::Ll
1475    }
1476
1477    /// Sets the prediction strategy for subsequent rule calls.
1478    fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
1479
1480    /// Maximum rule-nesting depth accepted before the parse aborts, or `None`
1481    /// for unlimited (the default).
1482    fn max_rule_depth(&self) -> Option<usize> {
1483        None
1484    }
1485
1486    /// Bounds the rule-nesting depth for subsequent rule calls.
1487    ///
1488    /// Deeply nested input is parsed safely regardless (rule recursion grows
1489    /// onto a segmented stack), but each nesting level still costs CPU and
1490    /// tree memory. Callers parsing untrusted input can cap that work: when
1491    /// the limit is exceeded the parse stops with a positioned syntax error
1492    /// instead of consuming unbounded resources. The measure counts rule
1493    /// frames plus left-recursive operator expansions, matching what an
1494    /// upstream-ANTLR rule-entry listener observes.
1495    ///
1496    /// The cap is enforced by generated recursive-descent rule bodies. When
1497    /// one is set, generated dispatch routes ATN-preferred rules through
1498    /// their generated bodies too, trading that fast path for enforcement.
1499    /// Rules the generator emitted no body for (interpreter-only fallback)
1500    /// do not check the cap.
1501    fn set_max_rule_depth(&mut self, _depth: Option<usize>) {}
1502
1503    /// Registers a listener for committed rule enter/exit events during
1504    /// recognition (ANTLR's `addParseListener`). See [`ParseListener`] for
1505    /// the delivery contract. The default implementation drops the listener;
1506    /// [`BaseParser`] and generated parsers deliver events.
1507    fn add_parse_listener(&mut self, _listener: Box<dyn ParseListener>) {}
1508
1509    /// Removes every registered parse listener and returns them, dropping
1510    /// any sticky abort a removed listener had requested.
1511    fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
1512        Vec::new()
1513    }
1514}
1515
1516#[derive(Debug)]
1517struct LeftRecursiveCallerOverlap {
1518    atn_key: SharedAtnCacheKey,
1519    state_number: usize,
1520    symbol: i32,
1521    context_version: usize,
1522    overlaps: bool,
1523}
1524
1525const LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE: usize = 16;
1526
1527#[derive(Debug)]
1528pub struct BaseParser<S, H = NoSemanticHooks> {
1529    input: CommonTokenStream<S>,
1530    tree: ParseTreeStorage,
1531    data: RecognizerData,
1532    semantic_hooks: H,
1533    decision_override_generation: usize,
1534    build_parse_trees: bool,
1535    syntax_errors: usize,
1536    report_diagnostic_errors: bool,
1537    prediction_mode: PredictionMode,
1538    prediction_diagnostics: Vec<ParserDiagnostic>,
1539    reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
1540    generated_parser_diagnostics: Vec<ParserDiagnostic>,
1541    generated_sync_expected: Option<TokenBitSet>,
1542    generated_recovery_error_index: Option<usize>,
1543    generated_recovery_error_states: BTreeSet<isize>,
1544    int_members: MemberEnv,
1545    rule_context_stack: Vec<RuleContextFrame>,
1546    rule_context_version: usize,
1547    left_recursive_caller_overlap_cache:
1548        [Option<LeftRecursiveCallerOverlap>; LEFT_RECURSIVE_CALLER_OVERLAP_CACHE_SIZE],
1549    pending_invoking_states: Vec<isize>,
1550    precedence_stack: Vec<i32>,
1551    /// Predicate side effects are observable in a few target-template tests;
1552    /// speculative recognition may revisit the same coordinate, so replay it
1553    /// once per parser instance.
1554    invoked_predicates: Vec<(usize, usize)>,
1555    /// Bail error strategy: the first syntax error aborts the parse instead of
1556    /// recovering (ANTLR's `BailErrorStrategy`). Generated recognizers set it
1557    /// through `set_error_handler(BailErrorStrategy::new())`.
1558    bail_on_error: bool,
1559    /// Parse listeners receiving committed rule enter/exit events during
1560    /// recognition (ANTLR's `addParseListener`). Empty in the default
1561    /// configuration, and every dispatch site is gated on emptiness so the
1562    /// unused feature costs one predictable branch per rule boundary.
1563    parse_listeners: Vec<ParseListenerSlot>,
1564    /// Sticky abort requested by a parse listener's `enter_every_rule`.
1565    /// Mirrors `rule_depth_error`: rule-level recovery absorbs the error like
1566    /// any rule failure, so the flag stays set until the top-level entry
1567    /// drains it and fails the parse.
1568    parse_listener_abort: Option<AntlrError>,
1569    /// Optional cap on rule-nesting depth for adversarial-input hardening.
1570    /// `None` (default) parses unbounded nesting; `Some(n)` aborts the parse
1571    /// with a positioned syntax error once `n` rule frames are exceeded.
1572    max_rule_depth: Option<usize>,
1573    /// Sticky depth-cap violation. Rule-level recovery would otherwise absorb
1574    /// the error and keep parsing; once set, every subsequent rule entry fails
1575    /// immediately and the top-level entry returns this error even when
1576    /// recovery produced a tree.
1577    rule_depth_error: Option<AntlrError>,
1578    /// Left-recursive expansions currently deepening the parse tree. Each
1579    /// operator iteration wraps the previous context one level deeper without
1580    /// pushing a rule frame, so the depth cap must count these separately —
1581    /// upstream ANTLR fires a rule-entry listener event for exactly this case
1582    /// (`Parser.pushNewRecursionContext` → `triggerEnterRuleEvent`).
1583    recursion_expansions: usize,
1584    /// Per-invocation snapshots of [`Self::recursion_expansions`], pushed by
1585    /// `enter_recursion_rule` and restored by `unroll_recursion_context`, so a
1586    /// finished left-recursive rule releases the depth its expansions added.
1587    recursion_expansion_marks: Vec<usize>,
1588    /// How to evaluate predicate coordinates missing from the active
1589    /// predicate table. Set from [`ParserRuntimeOptions`] at each parse entry.
1590    unknown_predicate_policy: UnknownSemanticPolicy,
1591    /// Unknown predicate coordinates evaluated by the current parse, recorded
1592    /// so [`UnknownSemanticPolicy::Error`] can report them after recognition.
1593    unknown_predicate_hits: Vec<(usize, usize)>,
1594    /// Committed parser action coordinates offered to [`SemanticHooks::action`]
1595    /// that no hook handled, recorded so a generated `hook`/error-disposed
1596    /// action fails loud instead of being silently dropped. Keyed by
1597    /// `(rule_index, source_state)`.
1598    unhandled_action_hits: Vec<(usize, usize)>,
1599    /// Per-parse rule FIRST-set cache keyed by rule start state. This keeps
1600    /// hot rule-transition checks to a vector lookup after the first visit
1601    /// while the thread-local shared ATN cache still owns the cross-parse
1602    /// computed value.
1603    rule_first_set_cache: Vec<Option<Rc<FirstSet>>>,
1604    /// Per-state expected-symbol cache. `state_expected_symbols` walks every
1605    /// epsilon-reachable consuming transition and shows up as a hot loop in
1606    /// `next_recovery_context` and recovery diagnostics on long inputs.
1607    /// Keying on `state_number` and sharing the result through `Rc` removes
1608    /// repeated DFS plus per-call `BTreeSet` allocations.
1609    state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
1610    /// Same expected-symbol cache as a bitset for generated parser sync.
1611    /// Successful parses only need `contains` and union; keeping that path out
1612    /// of `BTreeSet` avoids tree allocation for every nullable loop/optional
1613    /// check and defers deterministic formatting to diagnostics.
1614    state_expected_token_cache: FxHashMap<usize, Rc<TokenBitSet>>,
1615    /// Per-state cache for whether a return state can finish its owning rule
1616    /// without consuming more input. Generated-parser sync uses this to walk
1617    /// parent prediction contexts for nullable exits without paying repeated
1618    /// epsilon-closure searches on every loop or optional decision.
1619    rule_stop_reach_cache: Vec<Option<bool>>,
1620    /// Per-parser interner for `recovery_symbols` sets. Speculative recursion
1621    /// threads the same epsilon-recovery context through hundreds of follow
1622    /// states; sharing `Rc<BTreeSet<i32>>` instances lets clones reduce to a
1623    /// reference bump and lets the memo key hash by pointer.
1624    recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
1625    /// Per-decision-state look-1 cache. Built lazily so grammars that rarely
1626    /// touch a given decision state still pay no upfront cost; once cached,
1627    /// the recognizer prunes alternatives whose look-1 cannot accept the
1628    /// current lookahead, letting common SLL decisions reduce to a single
1629    /// transition walk instead of a full speculative fan-out.
1630    decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
1631    /// Caches the LL(1) alt selection per `(state, lookahead_token)`.
1632    /// Each multi-trans visit asks "given this decision state and this
1633    /// lookahead token, which alt do I commit to?" Hitting this cache
1634    /// turns the question into a hashmap probe instead of re-scanning
1635    /// the decision's per-transition FIRST sets every visit.
1636    ll1_decision_cache: FxHashMap<(usize, i32), Option<usize>>,
1637    /// Predicate results shared by the fast recognizer's clean and recovery
1638    /// attempts. The eligible fast path keeps every runtime-provided input
1639    /// fixed, and custom predicate hooks are required to be replay-safe.
1640    fast_predicate_cache: FxHashMap<(usize, usize, usize), bool>,
1641    /// Cache for whether an ATN state can reach itself without consuming
1642    /// input. Only those states need the recursive recognizer's
1643    /// `(state, token-index)` cycle guard. The companion ATN key lets this
1644    /// grammar-static cache survive parser resets without reusing state
1645    /// coordinates after the parser is driven against a different ATN.
1646    empty_cycle_cache: Vec<Option<bool>>,
1647    empty_cycle_cache_atn: Option<SharedAtnCacheKey>,
1648    /// Probe state for deciding whether clean-pass memo entries are worth
1649    /// storing for the current parse.
1650    clean_memo_mode: CleanMemoMode,
1651    clean_memo_probe_seen: FxHashSet<FastRecognizeKey>,
1652    clean_memo_probe_samples: usize,
1653    clean_memo_probe_repeats: usize,
1654    clean_memo_sparse_samples: usize,
1655    /// Reusable cycle and memo storage for one top-level fast recognition.
1656    fast_recognize_scratch: FastRecognizeTopScratch,
1657    /// Reusable direct-index/hash storage for clean speculative endpoints.
1658    fast_outcome_dedup: FastOutcomeDedupScratch,
1659    /// Empty recovery-symbols singleton used as the default at rule entry and
1660    /// after token consumption.
1661    empty_recovery_symbols: Rc<BTreeSet<i32>>,
1662    /// Whether the fast recognizer's FIRST-set prefilter is enabled. The
1663    /// prefilter trims speculative rule calls whose called rule cannot
1664    /// match the current lookahead, but it also bypasses single-token
1665    /// insertion / deletion recovery that ANTLR runs at the rule's first
1666    /// consuming transition. `parse_atn_rule` flips this off and retries
1667    /// when the first pass produces no clean outcome so the runtime can
1668    /// repair inputs the reference parser would have repaired.
1669    fast_first_set_prefilter: bool,
1670    /// Whether the fast recognizer should explore parser error-recovery paths.
1671    /// Public rule parsing starts with this disabled for the common valid-input
1672    /// path and enables it only for the retry that needs ANTLR-style repairs.
1673    fast_recovery_enabled: bool,
1674    /// Whether the fast recognizer should record terminal-token nodes while
1675    /// speculating. Clean valid-input parsing can reconstruct terminals from
1676    /// selected rule spans after recognition, avoiding many speculative
1677    /// nodes that are thrown away with losing paths.
1678    fast_token_nodes_enabled: bool,
1679    /// Whether fast recognition should retain private/public rule alternatives
1680    /// in deferred tree metadata.
1681    fast_track_alt_numbers: bool,
1682    /// Parser-owned append-only storage for speculative recognition output.
1683    /// Each public interpreted-rule entry clears lengths while retaining
1684    /// bounded backing capacities for parser reuse.
1685    recognition_arena: RecognitionArena,
1686    last_recognition_arena_root: NodeSeqId,
1687    last_recognition_arena_diagnostics: DiagnosticSeqId,
1688}
1689
1690/// Rollback marker for speculative generated parser paths.
1691#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1692pub struct GeneratedDiagnosticsCheckpoint {
1693    diagnostics_len: usize,
1694    syntax_errors: usize,
1695    tree: ParseTreeCheckpoint,
1696}
1697
1698/// Storage and reachability counters for the most recent interpreted-rule
1699/// recognition arena.
1700#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1701pub struct RecognitionArenaStats {
1702    pub total_nodes: usize,
1703    pub live_nodes: usize,
1704    pub dead_nodes: usize,
1705    pub node_capacity: usize,
1706    pub total_links: usize,
1707    pub live_links: usize,
1708    pub dead_links: usize,
1709    pub link_capacity: usize,
1710    pub total_extras: usize,
1711    pub live_extras: usize,
1712    pub dead_extras: usize,
1713    pub extra_capacity: usize,
1714}
1715
1716#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1717struct RuleContextFrame {
1718    rule_index: usize,
1719    invoking_state: isize,
1720}
1721
1722#[derive(Clone, Debug, Eq, PartialEq)]
1723struct RecognizeOutcome {
1724    index: usize,
1725    consumed_eof: bool,
1726    alt_number: usize,
1727    member_values: MemberEnv,
1728    return_values: BTreeMap<String, i64>,
1729    diagnostics: DiagnosticSeqId,
1730    decisions: Vec<usize>,
1731    actions: Vec<ParserAction>,
1732    nodes: NodeSeqId,
1733}
1734
1735#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1736struct FastRecognizeOutcome {
1737    index: usize,
1738    consumed_eof: bool,
1739    diagnostics: DiagnosticSeqId,
1740    deferred_nodes: FastDeferredNodeId,
1741    /// Head of the speculative parse-tree fragment in the parser-owned arena.
1742    /// Copying an outcome copies this compact ID; prepending appends one
1743    /// `SeqLink` without allocating an individual node or list tail.
1744    nodes: NodeSeqId,
1745}
1746
1747#[derive(Debug, Default)]
1748struct FastRecognizeTopScratch {
1749    visiting: FxHashSet<FastRecognizeKey>,
1750    memo: FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
1751}
1752
1753impl FastRecognizeTopScratch {
1754    fn prepare(&mut self, memo_capacity: usize) {
1755        self.visiting.clear();
1756        self.visiting.reserve(FAST_RECOGNIZE_VISITING_CAPACITY);
1757        self.memo.clear();
1758        self.memo.reserve(memo_capacity);
1759    }
1760
1761    fn release_oversized_memo(&mut self) {
1762        self.memo.clear();
1763        if self.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY {
1764            self.memo = FxHashMap::default();
1765        }
1766    }
1767}
1768
1769fn fast_recognize_memo_capacity(buffered_tokens: usize) -> usize {
1770    buffered_tokens.saturating_mul(8).clamp(
1771        FAST_RECOGNIZE_MIN_MEMO_CAPACITY,
1772        FAST_RECOGNIZE_MAX_MEMO_CAPACITY,
1773    )
1774}
1775
1776#[derive(Debug, Default)]
1777struct FastOutcomeDedupScratch {
1778    dense_words: Vec<u64>,
1779    touched_dense_words: Vec<u32>,
1780    sparse_keys: FxHashSet<(usize, bool)>,
1781}
1782
1783/// Handle into the parser-owned deferred tree rope.
1784///
1785/// The sentinel keeps outcomes and repetition paths compact without an
1786/// `Option` discriminant or per-node reference counting.
1787#[repr(transparent)]
1788#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1789struct FastDeferredNodeId(u32);
1790
1791impl FastDeferredNodeId {
1792    const EMPTY: Self = Self(u32::MAX);
1793
1794    const fn is_empty(self) -> bool {
1795        self.0 == Self::EMPTY.0
1796    }
1797}
1798
1799impl Default for FastDeferredNodeId {
1800    fn default() -> Self {
1801        Self::EMPTY
1802    }
1803}
1804
1805#[repr(transparent)]
1806#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1807struct FastDeferredRuleId(u32);
1808
1809/// One immutable deferred-tree rope record in `RecognitionArena`.
1810#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1811enum FastDeferredNode {
1812    Fragment(NodeSeqId),
1813    Rule(FastDeferredRuleId),
1814    Alternative(u32),
1815    LeftRecursiveBoundary {
1816        rule_index: u32,
1817    },
1818    Concat {
1819        prefix: FastDeferredNodeId,
1820        suffix: FastDeferredNodeId,
1821    },
1822}
1823
1824#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1825struct FastDeferredRule {
1826    rule_index: u32,
1827    invoking_state: i32,
1828    start_index: u32,
1829    stop_index: Option<u32>,
1830    deferred_children: FastDeferredNodeId,
1831    children: NodeSeqId,
1832}
1833
1834#[repr(transparent)]
1835#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1836struct RecognizedNodeId(u32);
1837
1838#[repr(transparent)]
1839#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1840struct NodeSeqId(u32);
1841
1842impl NodeSeqId {
1843    const EMPTY: Self = Self(u32::MAX);
1844
1845    const fn is_empty(self) -> bool {
1846        self.0 == Self::EMPTY.0
1847    }
1848}
1849
1850impl Default for NodeSeqId {
1851    fn default() -> Self {
1852        Self::EMPTY
1853    }
1854}
1855
1856#[repr(transparent)]
1857#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1858struct DiagnosticSeqId(u32);
1859
1860impl DiagnosticSeqId {
1861    const EMPTY: Self = Self(u32::MAX);
1862
1863    const fn is_empty(self) -> bool {
1864        self.0 == Self::EMPTY.0
1865    }
1866}
1867
1868impl Default for DiagnosticSeqId {
1869    fn default() -> Self {
1870        Self::EMPTY
1871    }
1872}
1873
1874#[repr(transparent)]
1875#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1876struct RecognitionExtraId(u32);
1877
1878#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1879struct SeqLink {
1880    head: RecognizedNodeId,
1881    tail: NodeSeqId,
1882}
1883
1884#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1885struct DiagnosticLink {
1886    head: RecognitionExtraId,
1887    tail: DiagnosticSeqId,
1888}
1889
1890struct ArenaRuleSpec {
1891    rule_index: usize,
1892    invoking_state: isize,
1893    alt_number: usize,
1894    start_index: usize,
1895    stop_index: Option<usize>,
1896    return_values: BTreeMap<String, i64>,
1897    children: NodeSeqId,
1898}
1899
1900/// Compact speculative node record. Common records contain only IDs and
1901/// scalars; missing-token text and generated return values live in `extras`.
1902#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
1903enum ArenaRecognizedNode {
1904    Token {
1905        token: TokenId,
1906    },
1907    ErrorToken {
1908        token: TokenId,
1909    },
1910    MissingToken {
1911        extra: RecognitionExtraId,
1912    },
1913    Rule {
1914        rule_index: u32,
1915        invoking_state: i32,
1916        alt_number: u32,
1917        start_index: u32,
1918        stop_index: Option<u32>,
1919        return_values: Option<RecognitionExtraId>,
1920        children: NodeSeqId,
1921    },
1922    /// Marker emitted at a precedence-rule loop entry where ANTLR would call
1923    /// `pushNewRecursionContext`. Folded into a wrapper rule node before the
1924    /// public rule entry hands the tree to the caller.
1925    LeftRecursiveBoundary {
1926        rule_index: u32,
1927        alt_number: u32,
1928    },
1929}
1930
1931#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
1932enum RecognitionExtra {
1933    MissingToken {
1934        token_type: i32,
1935        at_index: u32,
1936        text: String,
1937    },
1938    ReturnValues(BTreeMap<String, i64>),
1939    Diagnostic(ParserDiagnostic),
1940}
1941
1942#[derive(Debug, Default)]
1943struct RecognitionArena {
1944    nodes: Vec<ArenaRecognizedNode>,
1945    seq_links: Vec<SeqLink>,
1946    diagnostic_links: Vec<DiagnosticLink>,
1947    extras: Vec<RecognitionExtra>,
1948    deferred_nodes: Vec<FastDeferredNode>,
1949    deferred_rules: Vec<FastDeferredRule>,
1950}
1951
1952// Preserve normal parser reuse while preventing one pathological parse from
1953// pinning an arbitrarily large arena for the parser's remaining lifetime.
1954const MAX_RETAINED_RECOGNITION_NODES: usize = 131_072;
1955const MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS: usize = 262_144;
1956const MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS: usize = 65_536;
1957const MAX_RETAINED_RECOGNITION_EXTRAS: usize = 32_768;
1958const MAX_RETAINED_FAST_DEFERRED_NODES: usize = 262_144;
1959const MAX_RETAINED_FAST_DEFERRED_RULES: usize = 131_072;
1960
1961impl RecognitionArena {
1962    fn reset(&mut self) {
1963        reset_arena_vec(&mut self.nodes, MAX_RETAINED_RECOGNITION_NODES);
1964        reset_arena_vec(&mut self.seq_links, MAX_RETAINED_RECOGNITION_SEQUENCE_LINKS);
1965        reset_arena_vec(
1966            &mut self.diagnostic_links,
1967            MAX_RETAINED_RECOGNITION_DIAGNOSTIC_LINKS,
1968        );
1969        reset_arena_vec(&mut self.extras, MAX_RETAINED_RECOGNITION_EXTRAS);
1970        reset_arena_vec(&mut self.deferred_nodes, MAX_RETAINED_FAST_DEFERRED_NODES);
1971        reset_arena_vec(&mut self.deferred_rules, MAX_RETAINED_FAST_DEFERRED_RULES);
1972    }
1973
1974    fn push_node(&mut self, node: ArenaRecognizedNode) -> RecognizedNodeId {
1975        let id = RecognizedNodeId(
1976            u32::try_from(self.nodes.len()).expect("recognition node arena fits in u32"),
1977        );
1978        self.nodes.push(node);
1979        id
1980    }
1981
1982    fn push_extra(&mut self, extra: RecognitionExtra) -> RecognitionExtraId {
1983        let id = RecognitionExtraId(
1984            u32::try_from(self.extras.len()).expect("recognition extra arena fits in u32"),
1985        );
1986        self.extras.push(extra);
1987        id
1988    }
1989
1990    fn prepend(&mut self, tail: NodeSeqId, head: RecognizedNodeId) -> NodeSeqId {
1991        let id = NodeSeqId(
1992            u32::try_from(self.seq_links.len()).expect("node sequence arena fits in u32"),
1993        );
1994        self.seq_links.push(SeqLink { head, tail });
1995        id
1996    }
1997
1998    fn push_deferred_node(&mut self, node: FastDeferredNode) -> FastDeferredNodeId {
1999        let id = FastDeferredNodeId(
2000            u32::try_from(self.deferred_nodes.len()).expect("deferred node arena fits in u32"),
2001        );
2002        self.deferred_nodes.push(node);
2003        id
2004    }
2005
2006    fn push_deferred_rule(&mut self, rule: FastDeferredRule) -> FastDeferredRuleId {
2007        let id = FastDeferredRuleId(
2008            u32::try_from(self.deferred_rules.len()).expect("deferred rule arena fits in u32"),
2009        );
2010        self.deferred_rules.push(rule);
2011        id
2012    }
2013
2014    fn deferred_fragment(&mut self, nodes: NodeSeqId) -> FastDeferredNodeId {
2015        if nodes.is_empty() {
2016            FastDeferredNodeId::EMPTY
2017        } else {
2018            self.push_deferred_node(FastDeferredNode::Fragment(nodes))
2019        }
2020    }
2021
2022    fn deferred_rule_node(&mut self, rule: FastDeferredRule) -> FastDeferredNodeId {
2023        let rule = self.push_deferred_rule(rule);
2024        self.push_deferred_node(FastDeferredNode::Rule(rule))
2025    }
2026
2027    fn deferred_alternative(&mut self, alt_number: usize) -> FastDeferredNodeId {
2028        self.push_deferred_node(FastDeferredNode::Alternative(
2029            u32::try_from(alt_number).expect("alternative number fits in u32"),
2030        ))
2031    }
2032
2033    fn deferred_left_recursive_boundary(&mut self, rule_index: usize) -> FastDeferredNodeId {
2034        self.push_deferred_node(FastDeferredNode::LeftRecursiveBoundary {
2035            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
2036        })
2037    }
2038
2039    fn concat_deferred_nodes(
2040        &mut self,
2041        prefix: FastDeferredNodeId,
2042        suffix: FastDeferredNodeId,
2043    ) -> FastDeferredNodeId {
2044        if prefix.is_empty() {
2045            return suffix;
2046        }
2047        if suffix.is_empty() {
2048            return prefix;
2049        }
2050        self.push_deferred_node(FastDeferredNode::Concat { prefix, suffix })
2051    }
2052
2053    fn deferred_node(&self, id: FastDeferredNodeId) -> FastDeferredNode {
2054        self.deferred_nodes[id.0 as usize]
2055    }
2056
2057    fn deferred_rule(&self, id: FastDeferredRuleId) -> FastDeferredRule {
2058        self.deferred_rules[id.0 as usize]
2059    }
2060
2061    fn prepend_diagnostic(
2062        &mut self,
2063        tail: DiagnosticSeqId,
2064        diagnostic: ParserDiagnostic,
2065    ) -> DiagnosticSeqId {
2066        let head = self.push_extra(RecognitionExtra::Diagnostic(diagnostic));
2067        self.prepend_diagnostic_id(tail, head)
2068    }
2069
2070    fn prepend_diagnostic_id(
2071        &mut self,
2072        tail: DiagnosticSeqId,
2073        head: RecognitionExtraId,
2074    ) -> DiagnosticSeqId {
2075        let id = DiagnosticSeqId(
2076            u32::try_from(self.diagnostic_links.len())
2077                .expect("diagnostic sequence arena fits in u32"),
2078        );
2079        self.diagnostic_links.push(DiagnosticLink { head, tail });
2080        id
2081    }
2082
2083    fn concat_diagnostics(
2084        &mut self,
2085        prefix: DiagnosticSeqId,
2086        mut suffix: DiagnosticSeqId,
2087    ) -> DiagnosticSeqId {
2088        if prefix.is_empty() {
2089            return suffix;
2090        }
2091        if suffix.is_empty() {
2092            return prefix;
2093        }
2094        let mut reversed = DiagnosticSeqId::EMPTY;
2095        let mut cursor = prefix;
2096        while let Some(link) = self.diagnostic_link(cursor) {
2097            reversed = self.prepend_diagnostic_id(reversed, link.head);
2098            cursor = link.tail;
2099        }
2100        while let Some(link) = self.diagnostic_link(reversed) {
2101            suffix = self.prepend_diagnostic_id(suffix, link.head);
2102            reversed = link.tail;
2103        }
2104        suffix
2105    }
2106
2107    #[cfg(test)]
2108    fn diagnostic_sequence(
2109        &mut self,
2110        diagnostics: impl IntoIterator<Item = ParserDiagnostic>,
2111    ) -> DiagnosticSeqId {
2112        let diagnostics = diagnostics.into_iter().collect::<Vec<_>>();
2113        let mut sequence = DiagnosticSeqId::EMPTY;
2114        for diagnostic in diagnostics.into_iter().rev() {
2115            sequence = self.prepend_diagnostic(sequence, diagnostic);
2116        }
2117        sequence
2118    }
2119
2120    fn node(&self, id: RecognizedNodeId) -> ArenaRecognizedNode {
2121        self.nodes[id.0 as usize]
2122    }
2123
2124    fn set_boundary_alt_number(&mut self, id: RecognizedNodeId, alt_number: u32) {
2125        let ArenaRecognizedNode::LeftRecursiveBoundary {
2126            alt_number: stored, ..
2127        } = &mut self.nodes[id.0 as usize]
2128        else {
2129            unreachable!("deferred boundary must materialize as a boundary node");
2130        };
2131        *stored = alt_number;
2132    }
2133
2134    fn extra(&self, id: RecognitionExtraId) -> &RecognitionExtra {
2135        &self.extras[id.0 as usize]
2136    }
2137
2138    fn link(&self, id: NodeSeqId) -> Option<SeqLink> {
2139        (!id.is_empty()).then(|| self.seq_links[id.0 as usize])
2140    }
2141
2142    fn diagnostic_link(&self, id: DiagnosticSeqId) -> Option<DiagnosticLink> {
2143        (!id.is_empty()).then(|| self.diagnostic_links[id.0 as usize])
2144    }
2145
2146    const fn iter(&self, sequence: NodeSeqId) -> NodeSeqIter<'_> {
2147        NodeSeqIter {
2148            arena: self,
2149            cursor: sequence,
2150        }
2151    }
2152
2153    const fn diagnostics(&self, sequence: DiagnosticSeqId) -> DiagnosticSeqIter<'_> {
2154        DiagnosticSeqIter {
2155            arena: self,
2156            cursor: sequence,
2157        }
2158    }
2159
2160    fn diagnostics_len(&self, sequence: DiagnosticSeqId) -> usize {
2161        self.diagnostics(sequence).count()
2162    }
2163
2164    fn diagnostics_recovery_rank(&self, sequence: DiagnosticSeqId) -> usize {
2165        self.diagnostics(sequence)
2166            .filter(|diagnostic| {
2167                diagnostic.message.starts_with("mismatched input ")
2168                    && !diagnostic.message.starts_with("mismatched input '<EOF>' ")
2169            })
2170            .count()
2171    }
2172
2173    fn compare_diagnostics(&self, left: DiagnosticSeqId, right: DiagnosticSeqId) -> Ordering {
2174        self.diagnostics(left).cmp(self.diagnostics(right))
2175    }
2176
2177    fn sequence_len(&self, sequence: NodeSeqId) -> usize {
2178        self.iter(sequence).count()
2179    }
2180
2181    fn sequence_has_left_recursive_boundary(&self, sequence: NodeSeqId) -> bool {
2182        self.iter(sequence).any(|node| match self.node(node) {
2183            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
2184            ArenaRecognizedNode::Rule { children, .. } => {
2185                self.sequence_has_left_recursive_boundary(children)
2186            }
2187            ArenaRecognizedNode::Token { .. }
2188            | ArenaRecognizedNode::ErrorToken { .. }
2189            | ArenaRecognizedNode::MissingToken { .. } => false,
2190        })
2191    }
2192
2193    fn sequence_has_direct_boundary(&self, sequence: NodeSeqId) -> bool {
2194        self.iter(sequence).any(|node| {
2195            matches!(
2196                self.node(node),
2197                ArenaRecognizedNode::LeftRecursiveBoundary { .. }
2198            )
2199        })
2200    }
2201
2202    fn sequence_has_explicit_token(&self, sequence: NodeSeqId) -> bool {
2203        self.iter(sequence).any(|node| {
2204            matches!(
2205                self.node(node),
2206                ArenaRecognizedNode::Token { .. }
2207                    | ArenaRecognizedNode::ErrorToken { .. }
2208                    | ArenaRecognizedNode::MissingToken { .. }
2209            )
2210        })
2211    }
2212
2213    fn node_start_index(&self, node: RecognizedNodeId) -> Option<usize> {
2214        match self.node(node) {
2215            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
2216                Some(token.index())
2217            }
2218            ArenaRecognizedNode::MissingToken { extra } => {
2219                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
2220                    unreachable!("missing-token node must reference missing-token extra");
2221                };
2222                Some(*at_index as usize)
2223            }
2224            ArenaRecognizedNode::Rule { start_index, .. } => Some(start_index as usize),
2225            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
2226        }
2227    }
2228
2229    fn node_stop_index(&self, node: RecognizedNodeId) -> Option<usize> {
2230        match self.node(node) {
2231            ArenaRecognizedNode::Token { token } | ArenaRecognizedNode::ErrorToken { token } => {
2232                Some(token.index())
2233            }
2234            ArenaRecognizedNode::MissingToken { extra } => {
2235                let RecognitionExtra::MissingToken { at_index, .. } = self.extra(extra) else {
2236                    unreachable!("missing-token node must reference missing-token extra");
2237                };
2238                (*at_index as usize).checked_sub(1)
2239            }
2240            ArenaRecognizedNode::Rule { stop_index, .. } => stop_index.map(|index| index as usize),
2241            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => None,
2242        }
2243    }
2244
2245    fn node_span(&self, node: RecognizedNodeId) -> Option<(usize, Option<usize>)> {
2246        let start = self.node_start_index(node)?;
2247        let stop = self.node_stop_index(node);
2248        Some((start, stop))
2249    }
2250
2251    fn sequence_start_index(&self, sequence: NodeSeqId) -> Option<usize> {
2252        self.iter(sequence)
2253            .find_map(|node| self.node_start_index(node))
2254    }
2255
2256    fn sequence_stop_index(&self, sequence: NodeSeqId) -> Option<usize> {
2257        let mut stop = None;
2258        for node in self.iter(sequence) {
2259            if let Some(index) = self.node_stop_index(node) {
2260                stop = Some(index);
2261            }
2262        }
2263        stop
2264    }
2265
2266    fn sequence_needs_stable_tie(&self, sequence: NodeSeqId) -> bool {
2267        self.iter(sequence)
2268            .any(|node| self.node_needs_stable_tie(node))
2269    }
2270
2271    fn node_needs_stable_tie(&self, node: RecognizedNodeId) -> bool {
2272        match self.node(node) {
2273            ArenaRecognizedNode::Token { .. }
2274            | ArenaRecognizedNode::ErrorToken { .. }
2275            | ArenaRecognizedNode::MissingToken { .. } => false,
2276            ArenaRecognizedNode::LeftRecursiveBoundary { .. } => true,
2277            ArenaRecognizedNode::Rule {
2278                rule_index,
2279                children,
2280                ..
2281            } => self.iter(children).any(|child| {
2282                matches!(
2283                    self.node(child),
2284                    ArenaRecognizedNode::Rule {
2285                        rule_index: child_rule,
2286                        ..
2287                    } if child_rule == rule_index
2288                ) || self.node_needs_stable_tie(child)
2289            }),
2290        }
2291    }
2292
2293    fn compare_sequences(&self, mut left: NodeSeqId, mut right: NodeSeqId) -> Ordering {
2294        loop {
2295            match (self.link(left), self.link(right)) {
2296                (Some(left_link), Some(right_link)) => {
2297                    let order = self.compare_nodes(left_link.head, right_link.head);
2298                    if order != Ordering::Equal {
2299                        return order;
2300                    }
2301                    left = left_link.tail;
2302                    right = right_link.tail;
2303                }
2304                (None, None) => return Ordering::Equal,
2305                (None, Some(_)) => return Ordering::Less,
2306                (Some(_), None) => return Ordering::Greater,
2307            }
2308        }
2309    }
2310
2311    fn compare_nodes(&self, left: RecognizedNodeId, right: RecognizedNodeId) -> Ordering {
2312        let left = self.node(left);
2313        let right = self.node(right);
2314        match (left, right) {
2315            (
2316                ArenaRecognizedNode::Token { token: left },
2317                ArenaRecognizedNode::Token { token: right },
2318            )
2319            | (
2320                ArenaRecognizedNode::ErrorToken { token: left },
2321                ArenaRecognizedNode::ErrorToken { token: right },
2322            ) => left.cmp(&right),
2323            (
2324                ArenaRecognizedNode::MissingToken { extra: left },
2325                ArenaRecognizedNode::MissingToken { extra: right },
2326            ) => self.extra(left).cmp(self.extra(right)),
2327            (
2328                ArenaRecognizedNode::Rule {
2329                    rule_index: left_rule,
2330                    invoking_state: left_invoking,
2331                    alt_number: left_alt,
2332                    start_index: left_start,
2333                    stop_index: left_stop,
2334                    return_values: left_returns,
2335                    children: left_children,
2336                },
2337                ArenaRecognizedNode::Rule {
2338                    rule_index: right_rule,
2339                    invoking_state: right_invoking,
2340                    alt_number: right_alt,
2341                    start_index: right_start,
2342                    stop_index: right_stop,
2343                    return_values: right_returns,
2344                    children: right_children,
2345                },
2346            ) => (left_rule, left_invoking, left_alt, left_start, left_stop)
2347                .cmp(&(
2348                    right_rule,
2349                    right_invoking,
2350                    right_alt,
2351                    right_start,
2352                    right_stop,
2353                ))
2354                .then_with(|| {
2355                    left_returns
2356                        .map(|id| self.extra(id))
2357                        .cmp(&right_returns.map(|id| self.extra(id)))
2358                })
2359                .then_with(|| self.compare_sequences(left_children, right_children)),
2360            (
2361                ArenaRecognizedNode::LeftRecursiveBoundary {
2362                    rule_index: left_rule,
2363                    alt_number: left_alt,
2364                },
2365                ArenaRecognizedNode::LeftRecursiveBoundary {
2366                    rule_index: right_rule,
2367                    alt_number: right_alt,
2368                },
2369            ) => (left_rule, left_alt).cmp(&(right_rule, right_alt)),
2370            (left, right) => recognition_node_kind(&left).cmp(&recognition_node_kind(&right)),
2371        }
2372    }
2373
2374    fn reverse_sequence(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
2375        let mut reversed = NodeSeqId::EMPTY;
2376        while let Some(link) = self.link(sequence) {
2377            reversed = self.prepend(reversed, link.head);
2378            sequence = link.tail;
2379        }
2380        reversed
2381    }
2382
2383    fn fold_left_recursive_boundaries(&mut self, mut sequence: NodeSeqId) -> NodeSeqId {
2384        if !self.sequence_has_direct_boundary(sequence) {
2385            return sequence;
2386        }
2387        let mut reversed = NodeSeqId::EMPTY;
2388        while let Some(link) = self.link(sequence) {
2389            match self.node(link.head) {
2390                ArenaRecognizedNode::LeftRecursiveBoundary {
2391                    rule_index,
2392                    alt_number,
2393                } => {
2394                    if !reversed.is_empty() {
2395                        let children = self.reverse_sequence(reversed);
2396                        let start_index = self.sequence_start_index(children).unwrap_or_default();
2397                        let stop_index = self.sequence_stop_index(children);
2398                        let rule = self.push_node(ArenaRecognizedNode::Rule {
2399                            rule_index,
2400                            invoking_state: -1,
2401                            alt_number,
2402                            start_index: u32::try_from(start_index)
2403                                .expect("left-recursive start index fits in u32"),
2404                            stop_index: stop_index.map(|index| {
2405                                u32::try_from(index).expect("left-recursive stop index fits in u32")
2406                            }),
2407                            return_values: None,
2408                            children,
2409                        });
2410                        reversed = self.prepend(NodeSeqId::EMPTY, rule);
2411                    }
2412                }
2413                _ => {
2414                    reversed = self.prepend(reversed, link.head);
2415                }
2416            }
2417            sequence = link.tail;
2418        }
2419        self.reverse_sequence(reversed)
2420    }
2421
2422    fn stats(&self, root: NodeSeqId, diagnostics: DiagnosticSeqId) -> RecognitionArenaStats {
2423        let mut live_nodes = vec![false; self.nodes.len()];
2424        let mut live_links = vec![false; self.seq_links.len()];
2425        let mut live_diagnostic_links = vec![false; self.diagnostic_links.len()];
2426        let mut live_extras = vec![false; self.extras.len()];
2427        let mut pending = vec![root];
2428        while let Some(mut sequence) = pending.pop() {
2429            while let Some(link) = self.link(sequence) {
2430                let link_index = sequence.0 as usize;
2431                if live_links[link_index] {
2432                    break;
2433                }
2434                live_links[link_index] = true;
2435                let node_index = link.head.0 as usize;
2436                if !live_nodes[node_index] {
2437                    live_nodes[node_index] = true;
2438                    match self.node(link.head) {
2439                        ArenaRecognizedNode::MissingToken { extra } => {
2440                            live_extras[extra.0 as usize] = true;
2441                        }
2442                        ArenaRecognizedNode::Rule {
2443                            return_values,
2444                            children,
2445                            ..
2446                        } => {
2447                            if let Some(extra) = return_values {
2448                                live_extras[extra.0 as usize] = true;
2449                            }
2450                            pending.push(children);
2451                        }
2452                        ArenaRecognizedNode::Token { .. }
2453                        | ArenaRecognizedNode::ErrorToken { .. }
2454                        | ArenaRecognizedNode::LeftRecursiveBoundary { .. } => {}
2455                    }
2456                }
2457                sequence = link.tail;
2458            }
2459        }
2460        let mut diagnostics = diagnostics;
2461        while let Some(link) = self.diagnostic_link(diagnostics) {
2462            let link_index = diagnostics.0 as usize;
2463            if live_diagnostic_links[link_index] {
2464                break;
2465            }
2466            live_diagnostic_links[link_index] = true;
2467            live_extras[link.head.0 as usize] = true;
2468            diagnostics = link.tail;
2469        }
2470        let live_node_count = live_nodes.into_iter().filter(|live| *live).count();
2471        let live_link_count = live_links.into_iter().filter(|live| *live).count()
2472            + live_diagnostic_links
2473                .into_iter()
2474                .filter(|live| *live)
2475                .count();
2476        let live_extra_count = live_extras.into_iter().filter(|live| *live).count();
2477        let total_links = self.seq_links.len() + self.diagnostic_links.len();
2478        RecognitionArenaStats {
2479            total_nodes: self.nodes.len(),
2480            live_nodes: live_node_count,
2481            dead_nodes: self.nodes.len().saturating_sub(live_node_count),
2482            node_capacity: self.nodes.capacity(),
2483            total_links,
2484            live_links: live_link_count,
2485            dead_links: total_links.saturating_sub(live_link_count),
2486            link_capacity: self.seq_links.capacity() + self.diagnostic_links.capacity(),
2487            total_extras: self.extras.len(),
2488            live_extras: live_extra_count,
2489            dead_extras: self.extras.len().saturating_sub(live_extra_count),
2490            extra_capacity: self.extras.capacity(),
2491        }
2492    }
2493}
2494
2495fn reset_arena_vec<T>(storage: &mut Vec<T>, max_retained_capacity: usize) {
2496    if storage.capacity() > max_retained_capacity {
2497        *storage = Vec::new();
2498    } else {
2499        storage.clear();
2500    }
2501}
2502
2503const fn recognition_node_kind(node: &ArenaRecognizedNode) -> u8 {
2504    match node {
2505        ArenaRecognizedNode::Token { .. } => 0,
2506        ArenaRecognizedNode::ErrorToken { .. } => 1,
2507        ArenaRecognizedNode::MissingToken { .. } => 2,
2508        ArenaRecognizedNode::Rule { .. } => 3,
2509        ArenaRecognizedNode::LeftRecursiveBoundary { .. } => 4,
2510    }
2511}
2512
2513struct NodeSeqIter<'a> {
2514    arena: &'a RecognitionArena,
2515    cursor: NodeSeqId,
2516}
2517
2518impl Iterator for NodeSeqIter<'_> {
2519    type Item = RecognizedNodeId;
2520
2521    fn next(&mut self) -> Option<Self::Item> {
2522        let link = self.arena.link(self.cursor)?;
2523        self.cursor = link.tail;
2524        Some(link.head)
2525    }
2526}
2527
2528struct DiagnosticSeqIter<'a> {
2529    arena: &'a RecognitionArena,
2530    cursor: DiagnosticSeqId,
2531}
2532
2533impl<'a> Iterator for DiagnosticSeqIter<'a> {
2534    type Item = &'a ParserDiagnostic;
2535
2536    fn next(&mut self) -> Option<Self::Item> {
2537        let link = self.arena.diagnostic_link(self.cursor)?;
2538        self.cursor = link.tail;
2539        let RecognitionExtra::Diagnostic(diagnostic) = self.arena.extra(link.head) else {
2540            unreachable!("diagnostic link must reference diagnostic extra");
2541        };
2542        Some(diagnostic)
2543    }
2544}
2545
2546#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
2547struct ParserDiagnostic {
2548    line: usize,
2549    column: usize,
2550    message: String,
2551    /// Token the diagnostic is anchored to, resolved to a view when the
2552    /// diagnostic is dispatched to error listeners. `None` when no token
2553    /// exists (synthetic positions, lexer-originated messages).
2554    offending: Option<TokenId>,
2555}
2556
2557#[derive(Clone, Debug, Default, Eq, PartialEq)]
2558struct ExpectedTokens {
2559    index: Option<usize>,
2560    symbols: BTreeSet<i32>,
2561    no_viable: Option<NoViableAlternative>,
2562}
2563
2564#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2565struct NoViableAlternative {
2566    start_index: usize,
2567    error_index: usize,
2568}
2569
2570impl ExpectedTokens {
2571    /// Records the expected symbols for the farthest token index reached by any
2572    /// failed ATN path.
2573    fn record_transition(
2574        &mut self,
2575        index: usize,
2576        transition: ParserTransition<'_>,
2577        max_token_type: i32,
2578    ) {
2579        let symbols = transition_expected_symbols(transition, max_token_type);
2580        match self.index {
2581            Some(current) if index < current => {}
2582            Some(current) if index == current => self.symbols.extend(symbols),
2583            _ => {
2584                self.index = Some(index);
2585                self.symbols = symbols;
2586            }
2587        }
2588    }
2589
2590    /// Records an ambiguous decision that failed after consuming a shared
2591    /// prefix, which ANTLR reports as `no viable alternative`.
2592    const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
2593        match self.no_viable {
2594            Some(current) if error_index < current.error_index => {}
2595            _ => {
2596                self.no_viable = Some(NoViableAlternative {
2597                    start_index,
2598                    error_index,
2599                });
2600            }
2601        }
2602    }
2603}
2604
2605/// Compact token-type set for parser-internal FIRST/lookahead caches.
2606///
2607/// Public diagnostics still use `BTreeSet<i32>` for deterministic formatting,
2608/// but the hot recognizer path mostly needs `contains` and set union over
2609/// small token ids. A bitset avoids tree traversal and per-symbol allocation
2610/// while keeping conversion to `BTreeSet` at recovery/reporting boundaries.
2611#[derive(Clone, Debug, Default, Eq, PartialEq)]
2612struct TokenBitSet {
2613    words: Vec<u64>,
2614}
2615
2616impl TokenBitSet {
2617    fn insert(&mut self, symbol: i32) {
2618        let Some(slot) = token_bit_slot(symbol) else {
2619            return;
2620        };
2621        let word = slot / u64::BITS as usize;
2622        if word >= self.words.len() {
2623            self.words.resize(word + 1, 0);
2624        }
2625        self.words[word] |= 1_u64 << (slot % u64::BITS as usize);
2626    }
2627
2628    fn extend_range(&mut self, start: i32, stop: i32) {
2629        let (start, stop) = if start <= stop {
2630            (start, stop)
2631        } else {
2632            (stop, start)
2633        };
2634        if start <= TOKEN_EOF && stop >= TOKEN_EOF {
2635            self.insert(TOKEN_EOF);
2636        }
2637        let positive_start = start.max(1);
2638        if positive_start > stop {
2639            return;
2640        }
2641        let Some(start_slot) = token_bit_slot(positive_start) else {
2642            return;
2643        };
2644        let Some(stop_slot) = token_bit_slot(stop) else {
2645            return;
2646        };
2647        self.extend_slot_range(start_slot, stop_slot);
2648    }
2649
2650    fn extend_slot_range(&mut self, start_slot: usize, stop_slot: usize) {
2651        if start_slot > stop_slot {
2652            return;
2653        }
2654        let start_word = start_slot / u64::BITS as usize;
2655        let stop_word = stop_slot / u64::BITS as usize;
2656        if stop_word >= self.words.len() {
2657            self.words.resize(stop_word + 1, 0);
2658        }
2659        let start_offset = start_slot % u64::BITS as usize;
2660        let stop_offset = stop_slot % u64::BITS as usize;
2661        if start_word == stop_word {
2662            self.words[start_word] |=
2663                (!0_u64 << start_offset) & (!0_u64 >> (u64::BITS as usize - 1 - stop_offset));
2664            return;
2665        }
2666        self.words[start_word] |= !0_u64 << start_offset;
2667        for word in &mut self.words[(start_word + 1)..stop_word] {
2668            *word = !0_u64;
2669        }
2670        self.words[stop_word] |= !0_u64 >> (u64::BITS as usize - 1 - stop_offset);
2671    }
2672
2673    fn extend_iter(&mut self, symbols: impl IntoIterator<Item = i32>) {
2674        for symbol in symbols {
2675            self.insert(symbol);
2676        }
2677    }
2678
2679    fn extend_from(&mut self, other: &Self) {
2680        if other.words.len() > self.words.len() {
2681            self.words.resize(other.words.len(), 0);
2682        }
2683        for (left, right) in self.words.iter_mut().zip(&other.words) {
2684            *left |= *right;
2685        }
2686    }
2687
2688    fn contains(&self, symbol: i32) -> bool {
2689        let Some(slot) = token_bit_slot(symbol) else {
2690            return false;
2691        };
2692        let word = slot / u64::BITS as usize;
2693        self.words
2694            .get(word)
2695            .is_some_and(|bits| bits & (1_u64 << (slot % u64::BITS as usize)) != 0)
2696    }
2697
2698    fn is_empty(&self) -> bool {
2699        self.words.iter().all(|word| *word == 0)
2700    }
2701
2702    fn symbols(&self) -> impl Iterator<Item = i32> + '_ {
2703        self.words
2704            .iter()
2705            .copied()
2706            .enumerate()
2707            .flat_map(|(word_index, mut bits)| {
2708                std::iter::from_fn(move || {
2709                    while bits != 0 {
2710                        let bit = bits.trailing_zeros() as usize;
2711                        bits &= bits - 1;
2712                        if let Some(symbol) =
2713                            token_bit_symbol(word_index * u64::BITS as usize + bit)
2714                        {
2715                            return Some(symbol);
2716                        }
2717                    }
2718                    None
2719                })
2720            })
2721    }
2722
2723    fn extend_btree_set(&self, target: &mut BTreeSet<i32>) {
2724        target.extend(self.symbols());
2725    }
2726
2727    fn to_btree_set(&self) -> BTreeSet<i32> {
2728        let mut out = BTreeSet::new();
2729        self.extend_btree_set(&mut out);
2730        out
2731    }
2732}
2733
2734fn token_bit_slot(symbol: i32) -> Option<usize> {
2735    if symbol == TOKEN_EOF {
2736        Some(0)
2737    } else if symbol > 0 {
2738        usize::try_from(symbol).ok()
2739    } else {
2740        None
2741    }
2742}
2743
2744fn token_bit_symbol(slot: usize) -> Option<i32> {
2745    if slot == 0 {
2746        Some(TOKEN_EOF)
2747    } else {
2748        i32::try_from(slot).ok()
2749    }
2750}
2751
2752/// Converts one consuming transition into the token types that would satisfy it
2753/// for diagnostic reporting.
2754fn transition_expected_symbols(
2755    transition: ParserTransition<'_>,
2756    max_token_type: i32,
2757) -> BTreeSet<i32> {
2758    let mut symbols = BTreeSet::new();
2759    match &transition.data() {
2760        Transition::Atom { label, .. } => {
2761            symbols.insert(*label);
2762        }
2763        Transition::Range { start, stop, .. } => {
2764            symbols.extend(*start..=*stop);
2765        }
2766        Transition::Set { set, .. } => {
2767            for (start, stop) in set.ranges() {
2768                symbols.extend(start..=stop);
2769            }
2770        }
2771        Transition::NotSet { set, .. } => {
2772            symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2773        }
2774        Transition::Wildcard { .. } => {
2775            symbols.extend(1..=max_token_type);
2776        }
2777        Transition::Epsilon { .. }
2778        | Transition::Rule { .. }
2779        | Transition::Predicate { .. }
2780        | Transition::Action { .. }
2781        | Transition::Precedence { .. } => {}
2782    }
2783    symbols
2784}
2785
2786fn transition_expected_token_set(
2787    transition: ParserTransition<'_>,
2788    max_token_type: i32,
2789) -> TokenBitSet {
2790    let mut symbols = TokenBitSet::default();
2791    match &transition.data() {
2792        Transition::Atom { label, .. } => {
2793            symbols.insert(*label);
2794        }
2795        Transition::Range { start, stop, .. } => {
2796            symbols.extend_range(*start, *stop);
2797        }
2798        Transition::Set { set, .. } => {
2799            for (start, stop) in set.ranges() {
2800                symbols.extend_range(start, stop);
2801            }
2802        }
2803        Transition::NotSet { set, .. } => {
2804            symbols.extend_iter((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
2805        }
2806        Transition::Wildcard { .. } => {
2807            symbols.extend_range(1, max_token_type);
2808        }
2809        Transition::Epsilon { .. }
2810        | Transition::Rule { .. }
2811        | Transition::Predicate { .. }
2812        | Transition::Action { .. }
2813        | Transition::Precedence { .. } => {}
2814    }
2815    symbols
2816}
2817
2818/// Returns the consuming-token expectations reachable from an ATN state through
2819/// epsilon transitions. Recovery diagnostics need this closure so alternatives
2820/// and loop exits report the same expectation set ANTLR users see.
2821fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
2822    let mut symbols = BTreeSet::new();
2823    let mut stack = vec![state_number];
2824    let mut visited = BTreeSet::new();
2825    while let Some(current) = stack.pop() {
2826        if !visited.insert(current) {
2827            continue;
2828        }
2829        let Some(state) = atn.state(current) else {
2830            continue;
2831        };
2832        for transition in &state.transitions() {
2833            let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
2834            if transition_symbols.is_empty() {
2835                if transition.is_epsilon() {
2836                    stack.push(transition.target());
2837                }
2838            } else {
2839                symbols.extend(transition_symbols);
2840            }
2841        }
2842    }
2843    symbols
2844}
2845
2846fn state_expected_token_set(atn: &Atn, state_number: usize) -> TokenBitSet {
2847    let mut symbols = TokenBitSet::default();
2848    let mut stack = vec![state_number];
2849    let mut visited = BTreeSet::new();
2850    while let Some(current) = stack.pop() {
2851        if !visited.insert(current) {
2852            continue;
2853        }
2854        let Some(state) = atn.state(current) else {
2855            continue;
2856        };
2857        for transition in &state.transitions() {
2858            let transition_symbols =
2859                transition_expected_token_set(transition, atn.max_token_type());
2860            if transition_symbols.is_empty() {
2861                if transition.is_epsilon() {
2862                    stack.push(transition.target());
2863                }
2864            } else {
2865                symbols.extend_from(&transition_symbols);
2866            }
2867        }
2868    }
2869    symbols
2870}
2871
2872fn state_can_reach_rule_stop(atn: &Atn, state_number: usize) -> bool {
2873    let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
2874        return false;
2875    };
2876    let Some(stop_state) = atn.rule_to_stop_state().get(rule_index) else {
2877        return false;
2878    };
2879    epsilon_reaches_state(atn, state_number, stop_state)
2880}
2881
2882fn epsilon_reaches_state(atn: &Atn, start: usize, target: usize) -> bool {
2883    let mut stack = vec![start];
2884    let mut visited = BTreeSet::new();
2885    while let Some(current) = stack.pop() {
2886        if current == target {
2887            return true;
2888        }
2889        if !visited.insert(current) {
2890            continue;
2891        }
2892        let Some(state) = atn.state(current) else {
2893            continue;
2894        };
2895        stack.extend(
2896            state
2897                .transitions()
2898                .iter()
2899                .filter(|transition| transition.is_epsilon())
2900                .map(ParserTransition::target),
2901        );
2902    }
2903    false
2904}
2905
2906/// FIRST set for a rule entry plus whether the rule is nullable.
2907///
2908/// Walks epsilon, predicate, action, and rule-call transitions until it finds
2909/// a consuming transition or reaches the rule's stop state. Used by the fast
2910/// recognizer to skip rule alternatives whose first-consumed token cannot
2911/// possibly match the current lookahead.
2912#[derive(Clone, Debug, Default, Eq, PartialEq)]
2913struct FirstSet {
2914    symbols: TokenBitSet,
2915    nullable: bool,
2916}
2917
2918/// Per-parser cache of FIRST sets computed during recognition. The fast path
2919/// consults this on every speculative `Transition::Rule` encounter, so the
2920/// computation must amortize across all of those calls — the FIRST set is a
2921/// pure function of the ATN, not of the input position. Cached entries are
2922/// shared via `Rc` so the recognizer never deep-copies the underlying
2923/// `BTreeSet<i32>`.
2924type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
2925
2926// Thread-local FIRST-set caches keyed by the ATN pointer. The FIRST set
2927// and decision-lookahead entries are purely functions of the grammar's
2928// ATN, so caching across parses lets repeated parsing of the same grammar
2929// (the common case for a CLI tool or language server) avoid redoing the
2930// closure work. Generated parsers hand us a `&'static Atn` whose address
2931// is stable, which is what we hash on.
2932type DecisionLookaheadCache = FxHashMap<usize, Rc<DecisionLookahead>>;
2933
2934#[derive(Debug, Default)]
2935struct LeftRecursiveOperatorLookahead {
2936    /// Operator alts whose token-prefix is fully matched by this one symbol
2937    /// (then only epsilons/actions remain before the recursive RHS call).
2938    /// Safe for one-token loop-enter fast path.
2939    single_token: TokenBitSet,
2940    /// Operator alts that start with this symbol but still require more tokens
2941    /// before the operand. Must not force enter from one-token lookahead when a
2942    /// shorter operator shares the prefix; `StarLoopEntry` adaptive prediction
2943    /// has to weigh the exit alt as well.
2944    multi_token_prefix: TokenBitSet,
2945    predicate_dependent: TokenBitSet,
2946}
2947
2948#[derive(Default)]
2949struct SharedAtnCache {
2950    first_set: FirstSetCache,
2951    decision_lookahead: DecisionLookaheadCache,
2952    left_recursive_operator_lookahead: FxHashMap<(usize, i32), Rc<LeftRecursiveOperatorLookahead>>,
2953    state_before_stop_lookahead: FxHashMap<(usize, usize), Rc<StateBeforeStopLookahead>>,
2954    state_expected_tokens: FxHashMap<usize, Rc<TokenBitSet>>,
2955    rule_stop_reach: FxHashMap<usize, bool>,
2956    observable_action_transitions: Option<bool>,
2957    predicate_transitions: Option<bool>,
2958}
2959
2960thread_local! {
2961    static SHARED_ATN_CACHES: RefCell<FxHashMap<SharedAtnCacheKey, SharedAtnCache>> =
2962        RefCell::new(FxHashMap::default());
2963}
2964
2965/// Compound key for `SHARED_ATN_CACHES`.
2966///
2967/// Generated parsers feed us a `&'static Atn` from a `OnceLock<Atn>`, so the
2968/// pointer identifies one grammar for the program's lifetime. For the
2969/// non-`'static` case (a dropped `Atn` whose allocation is later reused),
2970/// the secondary fields below catch the pointer collision: a new grammar
2971/// would need to match all of `(states ptr, states len, max_token_type)` to
2972/// be mistaken for the dropped one. That combination changing under us
2973/// without a rebuild is implausible enough to treat as a bug; bundling them
2974/// into the key is otherwise a few extra bytes per lookup.
2975#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2976struct SharedAtnCacheKey {
2977    atn: usize,
2978    states: usize,
2979    state_count: usize,
2980    max_token_type: i32,
2981}
2982
2983impl SharedAtnCacheKey {
2984    fn for_atn(atn: &Atn) -> Self {
2985        let (states, state_count) = atn.storage_identity();
2986        Self {
2987            atn: std::ptr::from_ref::<Atn>(atn) as usize,
2988            states,
2989            state_count,
2990            max_token_type: atn.max_token_type(),
2991        }
2992    }
2993}
2994
2995fn with_shared_first_set_cache<R>(atn: &Atn, f: impl FnOnce(&mut FirstSetCache) -> R) -> R {
2996    SHARED_ATN_CACHES.with(|cell| {
2997        let key = SharedAtnCacheKey::for_atn(atn);
2998        let mut map = cell.borrow_mut();
2999        let cache = map.entry(key).or_default();
3000        f(&mut cache.first_set)
3001    })
3002}
3003
3004fn with_shared_atn_caches<R>(atn: &Atn, f: impl FnOnce(&mut SharedAtnCache) -> R) -> R {
3005    SHARED_ATN_CACHES.with(|cell| {
3006        let key = SharedAtnCacheKey::for_atn(atn);
3007        let mut map = cell.borrow_mut();
3008        let cache = map.entry(key).or_default();
3009        f(cache)
3010    })
3011}
3012
3013/// Per-decision-state cached look-1 sets for each outgoing transition.
3014///
3015/// At a multi-alternative state, the recognizer would otherwise speculatively
3016/// walk every alternative even when only one can possibly accept the current
3017/// lookahead. Caching the look-1 set per transition lets us prune the
3018/// non-viable transitions before recursing — the same SLL prediction trick
3019/// the reference ANTLR runtime uses, just expressed as a `(state, lookahead)`
3020/// filter rather than a full DFA.
3021#[derive(Debug, Default)]
3022struct DecisionLookahead {
3023    transitions: Vec<TransitionLookSet>,
3024}
3025
3026/// Look-1 information for one outgoing transition.
3027///
3028/// `nullable` mirrors `FirstSet::nullable` and is true when the transition
3029/// can reach the rule stop without consuming a token (e.g. an empty alt).
3030/// Nullable transitions cannot be pruned: they may still be the right path
3031/// when the lookahead consumes nothing further inside the current rule.
3032#[derive(Clone, Debug, Default)]
3033struct TransitionLookSet {
3034    symbols: TokenBitSet,
3035    nullable: bool,
3036}
3037
3038/// Mutable bookkeeping shared across one FIRST-set computation. Bundling the
3039/// rarely-touched fields keeps the recursive helpers below the function-arity
3040/// lint and lets every nested call thread the same cache and cycle guards.
3041struct FirstSetCtx<'a> {
3042    cache: &'a mut FirstSetCache,
3043    in_progress: BTreeSet<(usize, usize)>,
3044    hit_cycle: bool,
3045}
3046
3047/// Returns the FIRST set for the (rule entry, rule stop) pair, populating the
3048/// shared cache and tolerating recursive nullable rule chains. Mutually
3049/// recursive rules cannot stack-overflow because callers in flight are tracked
3050/// in `ctx.in_progress`; revisits return without recursing, and the partial
3051/// result is cached only when no cycle was detected during its computation.
3052///
3053/// On a cache hit the returned `Rc` is shared with the recognizer so subsequent
3054/// rule-call probes only pay a reference bump.
3055fn rule_first_set(
3056    atn: &Atn,
3057    target: usize,
3058    rule_stop_state: usize,
3059    cache: &mut FirstSetCache,
3060) -> Rc<FirstSet> {
3061    if let Some(cached) = cache.get(&(target, rule_stop_state)) {
3062        return Rc::clone(cached);
3063    }
3064    let mut ctx = FirstSetCtx {
3065        cache,
3066        in_progress: BTreeSet::new(),
3067        hit_cycle: false,
3068    };
3069    rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
3070}
3071
3072fn rule_first_set_cached(
3073    atn: &Atn,
3074    target: usize,
3075    rule_stop_state: usize,
3076    ctx: &mut FirstSetCtx<'_>,
3077) -> Rc<FirstSet> {
3078    let key = (target, rule_stop_state);
3079    if let Some(cached) = ctx.cache.get(&key) {
3080        return Rc::clone(cached);
3081    }
3082    if !ctx.in_progress.insert(key) {
3083        // Cycle: a caller above is already computing this entry. Return an
3084        // empty FIRST set; that caller's traversal supplies the contributions
3085        // from the rule's other alternatives.
3086        return Rc::new(FirstSet::default());
3087    }
3088    let saved_hit_cycle = ctx.hit_cycle;
3089    ctx.hit_cycle = false;
3090    let mut first = FirstSet::default();
3091    let mut visited = BTreeSet::new();
3092    rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
3093    ctx.in_progress.remove(&key);
3094    let entry = Rc::new(first);
3095    if !ctx.hit_cycle {
3096        ctx.cache.insert(key, Rc::clone(&entry));
3097    }
3098    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
3099    entry
3100}
3101
3102/// Returns the look-1 set for traversing `transition` while still inside the
3103/// current `rule_stop_state`. Used by the multi-alternative prefilter, which
3104/// prunes transitions whose look-1 cannot accept the current lookahead.
3105fn transition_first_set(
3106    atn: &Atn,
3107    transition: ParserTransition<'_>,
3108    rule_stop_state: usize,
3109    cache: &mut FirstSetCache,
3110) -> TransitionLookSet {
3111    match &transition.data() {
3112        Transition::Atom { label, .. } => {
3113            let mut symbols = TokenBitSet::default();
3114            symbols.insert(*label);
3115            TransitionLookSet {
3116                symbols,
3117                nullable: false,
3118            }
3119        }
3120        Transition::Range { start, stop, .. } => {
3121            let mut symbols = TokenBitSet::default();
3122            symbols.extend_range(*start, *stop);
3123            TransitionLookSet {
3124                symbols,
3125                nullable: false,
3126            }
3127        }
3128        Transition::Set { set, .. } => {
3129            let mut symbols = TokenBitSet::default();
3130            for (start, stop) in set.ranges() {
3131                symbols.extend_range(start, stop);
3132            }
3133            TransitionLookSet {
3134                symbols,
3135                nullable: false,
3136            }
3137        }
3138        Transition::NotSet { set, .. } => {
3139            let max = atn.max_token_type();
3140            let mut symbols = TokenBitSet::default();
3141            symbols.extend_iter((1..=max).filter(|symbol| !set.contains(*symbol)));
3142            TransitionLookSet {
3143                symbols,
3144                nullable: false,
3145            }
3146        }
3147        Transition::Wildcard { .. } => {
3148            let mut symbols = TokenBitSet::default();
3149            symbols.extend_range(1, atn.max_token_type());
3150            TransitionLookSet {
3151                symbols,
3152                nullable: false,
3153            }
3154        }
3155        Transition::Epsilon { target }
3156        | Transition::Action { target, .. }
3157        | Transition::Predicate { target, .. }
3158        | Transition::Precedence { target, .. } => {
3159            // Walk the closure starting at `target` until a consuming transition
3160            // is reached or the rule stop state is hit.
3161            let first = rule_first_set(atn, *target, rule_stop_state, cache);
3162            TransitionLookSet {
3163                symbols: first.symbols.clone(),
3164                nullable: first.nullable,
3165            }
3166        }
3167        Transition::Rule {
3168            target,
3169            rule_index,
3170            follow_state,
3171            ..
3172        } => {
3173            let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3174                return TransitionLookSet::default();
3175            };
3176            let child = rule_first_set(atn, *target, child_stop, cache);
3177            let mut symbols = child.symbols.clone();
3178            let nullable = if child.nullable {
3179                let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
3180                symbols.extend_from(&follow.symbols);
3181                follow.nullable
3182            } else {
3183                false
3184            };
3185            TransitionLookSet { symbols, nullable }
3186        }
3187    }
3188}
3189
3190/// Reports whether `transition` can be pruned at a multi-alt state because
3191/// its cached look-1 cannot accept the current lookahead.
3192///
3193/// Pruning runs only for non-consuming transitions (Epsilon/Action/Predicate/
3194/// Rule/Precedence) so consuming transitions still reach the
3195/// `matches`+recovery path that surfaces single-token deletion / insertion
3196/// repairs and ANTLR-compatible expected-token sets. When a non-consuming
3197/// transition is pruned, its FIRST set is folded into `expected` so failed
3198/// parses produce the same `mismatched input ... expecting ...` diagnostic
3199/// the no-prefilter baseline would emit.
3200/// Returns the unique alt index (0-based) when `symbol` falls into exactly
3201/// one transition's FIRST set and no transition is nullable. Used as an
3202/// LL(1) commit point: when prediction is unambiguous from the lookahead
3203/// alone, the recursive recognizer can skip every other alt without paying
3204/// for the per-transition filter probe.
3205///
3206/// `None` signals the caller to fall back to per-transition lookahead
3207/// filtering. Returning `Some` for an alt whose transition cannot actually
3208/// match would prune the only viable parse path; this is why we require
3209/// strict disjointness *and* no nullable transitions in the decision.
3210fn ll1_unique_alt(entry: &DecisionLookahead, symbol: i32) -> Option<usize> {
3211    let mut chosen: Option<usize> = None;
3212    for (index, transition) in entry.transitions.iter().enumerate() {
3213        if transition.nullable {
3214            return None;
3215        }
3216        if transition.symbols.contains(symbol) {
3217            if chosen.is_some() {
3218                return None;
3219            }
3220            chosen = Some(index);
3221        }
3222    }
3223    chosen
3224}
3225
3226/// Returns the unique greedy alt index (0-based) selected by the current
3227/// lookahead.
3228///
3229/// The shortcut is intentionally conservative around nullable exits. If the
3230/// current symbol can start a consuming alternative and an empty alternative is
3231/// also present, one-token lookahead is not enough to know whether the symbol
3232/// belongs to the current construct or to its caller's follow set. `None`
3233/// signals the caller to fall back to adaptive prediction.
3234fn ll1_greedy_alt(entry: &DecisionLookahead, symbol: i32, non_greedy: bool) -> Option<usize> {
3235    let mut matching_non_nullable_alt = None;
3236    let mut nullable_alt = None;
3237    for (index, transition) in entry.transitions.iter().enumerate() {
3238        if transition.nullable {
3239            if nullable_alt.is_some() {
3240                return None;
3241            }
3242            nullable_alt = Some(index);
3243        }
3244        if transition.symbols.contains(symbol) {
3245            if transition.nullable {
3246                continue;
3247            }
3248            if matching_non_nullable_alt.is_some() {
3249                return None;
3250            }
3251            matching_non_nullable_alt = Some(index);
3252        }
3253    }
3254    if matching_non_nullable_alt.is_some() && nullable_alt.is_some() {
3255        return None;
3256    }
3257    if non_greedy {
3258        nullable_alt.or(matching_non_nullable_alt)
3259    } else {
3260        matching_non_nullable_alt.or(nullable_alt)
3261    }
3262}
3263
3264fn should_skip_via_lookahead(
3265    transition_kind: ParserTransitionKind,
3266    transition_index: usize,
3267    lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
3268    index: usize,
3269    record_expected: bool,
3270    expected: &mut ExpectedTokens,
3271) -> bool {
3272    let prune_non_consuming = matches!(
3273        transition_kind,
3274        ParserTransitionKind::Epsilon
3275            | ParserTransitionKind::Action
3276            | ParserTransitionKind::Predicate
3277            | ParserTransitionKind::Rule
3278            | ParserTransitionKind::Precedence
3279    );
3280    if !prune_non_consuming {
3281        return false;
3282    }
3283    let Some((symbol, entry)) = lookahead_filter else {
3284        return false;
3285    };
3286    let Some(set) = entry.transitions.get(transition_index) else {
3287        return false;
3288    };
3289    if set.symbols.contains(*symbol) || set.nullable {
3290        return false;
3291    }
3292    if record_expected && !set.symbols.is_empty() {
3293        record_pruned_transition_expected(set, index, expected);
3294    }
3295    true
3296}
3297
3298fn should_skip_rule_via_first_set(
3299    first: &FirstSet,
3300    symbol: i32,
3301    record_expected: bool,
3302    index: usize,
3303    expected: &mut ExpectedTokens,
3304) -> bool {
3305    if first.nullable || first.symbols.contains(symbol) {
3306        return false;
3307    }
3308    if record_expected && !first.symbols.is_empty() {
3309        record_token_bit_expected(&first.symbols, index, expected);
3310    }
3311    true
3312}
3313
3314fn record_token_bit_expected(symbols: &TokenBitSet, index: usize, expected: &mut ExpectedTokens) {
3315    match expected.index {
3316        Some(current) if index < current => {}
3317        Some(current) if index == current => {
3318            symbols.extend_btree_set(&mut expected.symbols);
3319        }
3320        _ => {
3321            expected.index = Some(index);
3322            expected.symbols = symbols.to_btree_set();
3323        }
3324    }
3325}
3326
3327/// Folds a pruned transition's FIRST set into the farthest-expected accumulator.
3328fn record_pruned_transition_expected(
3329    set: &TransitionLookSet,
3330    index: usize,
3331    expected: &mut ExpectedTokens,
3332) {
3333    match expected.index {
3334        Some(current) if index < current => {}
3335        Some(current) if index == current => {
3336            set.symbols.extend_btree_set(&mut expected.symbols);
3337        }
3338        _ => {
3339            expected.index = Some(index);
3340            expected.symbols = set.symbols.to_btree_set();
3341        }
3342    }
3343}
3344
3345fn rule_first_set_inner(
3346    atn: &Atn,
3347    state_number: usize,
3348    rule_stop_state: usize,
3349    ctx: &mut FirstSetCtx<'_>,
3350    visited: &mut BTreeSet<usize>,
3351    first: &mut FirstSet,
3352) {
3353    if !visited.insert(state_number) {
3354        return;
3355    }
3356    if state_number == rule_stop_state {
3357        first.nullable = true;
3358        return;
3359    }
3360    let Some(state) = atn.state(state_number) else {
3361        return;
3362    };
3363    for transition in &state.transitions() {
3364        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
3365        if !transition_symbols.is_empty() {
3366            first.symbols.extend_iter(transition_symbols);
3367            continue;
3368        }
3369        match &transition.data() {
3370            Transition::Epsilon { target }
3371            | Transition::Action { target, .. }
3372            | Transition::Predicate { target, .. }
3373            | Transition::Precedence { target, .. } => {
3374                rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
3375            }
3376            Transition::Rule {
3377                target,
3378                rule_index,
3379                follow_state,
3380                ..
3381            } => {
3382                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3383                    continue;
3384                };
3385                let child_key = (*target, child_stop);
3386                if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
3387                    ctx.hit_cycle = true;
3388                }
3389                let child = rule_first_set_cached(atn, *target, child_stop, ctx);
3390                first.symbols.extend_from(&child.symbols);
3391                if child.nullable {
3392                    rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
3393                }
3394            }
3395            Transition::Atom { .. }
3396            | Transition::Range { .. }
3397            | Transition::Set { .. }
3398            | Transition::NotSet { .. }
3399            | Transition::Wildcard { .. } => {}
3400        }
3401    }
3402}
3403
3404/// Returns token types that can resume parsing from `state_number` after a
3405/// failed child rule, following rule calls as well as epsilon transitions.
3406fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
3407    let mut symbols = BTreeSet::new();
3408    state_sync_symbols_inner(
3409        atn,
3410        state_number,
3411        stop_state,
3412        &mut BTreeSet::new(),
3413        &mut symbols,
3414    );
3415    symbols
3416}
3417
3418/// Walks epsilon-like continuations from a parent follow state until it finds
3419/// consuming tokens that can anchor recovery, or EOF if the parent rule can end.
3420fn state_sync_symbols_inner(
3421    atn: &Atn,
3422    state_number: usize,
3423    stop_state: usize,
3424    visited: &mut BTreeSet<usize>,
3425    symbols: &mut BTreeSet<i32>,
3426) {
3427    if !visited.insert(state_number) {
3428        return;
3429    }
3430    if state_number == stop_state {
3431        symbols.insert(TOKEN_EOF);
3432        return;
3433    }
3434    let Some(state) = atn.state(state_number) else {
3435        return;
3436    };
3437    for transition in &state.transitions() {
3438        let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
3439        if transition_symbols.is_empty() {
3440            match &transition.data() {
3441                Transition::Rule { target, .. }
3442                | Transition::Epsilon { target }
3443                | Transition::Action { target, .. }
3444                | Transition::Predicate { target, .. }
3445                | Transition::Precedence { target, .. } => {
3446                    state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
3447                }
3448                Transition::Atom { .. }
3449                | Transition::Range { .. }
3450                | Transition::Set { .. }
3451                | Transition::NotSet { .. }
3452                | Transition::Wildcard { .. } => {}
3453            }
3454        } else {
3455            symbols.extend(transition_symbols);
3456        }
3457    }
3458}
3459
3460#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3461struct OperatorSymbolReachability {
3462    /// One token completes an unconditional operator token-prefix.
3463    single_token: bool,
3464    /// An unconditional operator path requires more tokens before its operand.
3465    multi_token: bool,
3466    /// At least one matching operator path depends on a semantic predicate.
3467    predicate_dependent: bool,
3468}
3469
3470impl OperatorSymbolReachability {
3471    const ADAPTIVE_FALLBACK: Self = Self {
3472        single_token: false,
3473        multi_token: false,
3474        predicate_dependent: true,
3475    };
3476
3477    const fn single_token(predicate_dependent: bool) -> Self {
3478        if predicate_dependent {
3479            Self {
3480                single_token: false,
3481                multi_token: false,
3482                predicate_dependent: true,
3483            }
3484        } else {
3485            Self {
3486                single_token: true,
3487                multi_token: false,
3488                predicate_dependent: false,
3489            }
3490        }
3491    }
3492
3493    const fn multi_token(predicate_dependent: bool) -> Self {
3494        if predicate_dependent {
3495            Self {
3496                single_token: false,
3497                multi_token: false,
3498                predicate_dependent: true,
3499            }
3500        } else {
3501            Self {
3502                single_token: false,
3503                multi_token: true,
3504                predicate_dependent: false,
3505            }
3506        }
3507    }
3508
3509    const fn union(self, other: Self) -> Self {
3510        Self {
3511            single_token: self.single_token || other.single_token,
3512            multi_token: self.multi_token || other.multi_token,
3513            predicate_dependent: self.predicate_dependent || other.predicate_dependent,
3514        }
3515    }
3516}
3517
3518#[derive(Clone, Copy)]
3519struct OperatorReachabilityRequest {
3520    symbol: i32,
3521    precedence: i32,
3522    predicate_dependent: bool,
3523    operator_rule_index: usize,
3524}
3525
3526#[derive(Clone, Copy, Debug)]
3527struct OperatorRuleContinuation {
3528    stop_state: usize,
3529    follow_state: usize,
3530    return_precedence: i32,
3531}
3532
3533struct NullablePrecedenceCtx {
3534    cache: FxHashMap<(usize, usize, i32, bool), bool>,
3535    in_progress: BTreeSet<(usize, usize, i32, bool)>,
3536    hit_cycle: bool,
3537}
3538
3539fn state_is_nullable_with_precedence(
3540    atn: &Atn,
3541    state_number: usize,
3542    stop_state_number: usize,
3543    precedence: i32,
3544    allow_predicates: bool,
3545    ctx: &mut NullablePrecedenceCtx,
3546) -> bool {
3547    let saved_hit_cycle = ctx.hit_cycle;
3548    ctx.hit_cycle = false;
3549    let nullable = state_is_nullable_with_precedence_cached(
3550        atn,
3551        state_number,
3552        stop_state_number,
3553        precedence,
3554        allow_predicates,
3555        ctx,
3556    );
3557    ctx.hit_cycle = saved_hit_cycle;
3558    nullable
3559}
3560
3561fn state_is_nullable_with_precedence_cached(
3562    atn: &Atn,
3563    state_number: usize,
3564    stop_state_number: usize,
3565    precedence: i32,
3566    allow_predicates: bool,
3567    ctx: &mut NullablePrecedenceCtx,
3568) -> bool {
3569    if state_number == stop_state_number {
3570        return true;
3571    }
3572    let key = (
3573        state_number,
3574        stop_state_number,
3575        precedence,
3576        allow_predicates,
3577    );
3578    if let Some(cached) = ctx.cache.get(&key) {
3579        return *cached;
3580    }
3581    if !ctx.in_progress.insert(key) {
3582        ctx.hit_cycle = true;
3583        return false;
3584    }
3585    let saved_hit_cycle = ctx.hit_cycle;
3586    ctx.hit_cycle = false;
3587    let nullable = atn.state(state_number).is_some_and(|state| {
3588        state
3589            .transitions()
3590            .iter()
3591            .any(|transition| match &transition.data() {
3592                Transition::Rule {
3593                    target,
3594                    rule_index,
3595                    follow_state,
3596                    precedence: rule_precedence,
3597                } => {
3598                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3599                        return false;
3600                    };
3601                    state_is_nullable_with_precedence_cached(
3602                        atn,
3603                        *target,
3604                        child_stop,
3605                        *rule_precedence,
3606                        allow_predicates,
3607                        ctx,
3608                    ) && state_is_nullable_with_precedence_cached(
3609                        atn,
3610                        *follow_state,
3611                        stop_state_number,
3612                        precedence,
3613                        allow_predicates,
3614                        ctx,
3615                    )
3616                }
3617                Transition::Epsilon { target } | Transition::Action { target, .. } => {
3618                    state_is_nullable_with_precedence_cached(
3619                        atn,
3620                        *target,
3621                        stop_state_number,
3622                        precedence,
3623                        allow_predicates,
3624                        ctx,
3625                    )
3626                }
3627                Transition::Predicate { target, .. } if allow_predicates => {
3628                    state_is_nullable_with_precedence_cached(
3629                        atn,
3630                        *target,
3631                        stop_state_number,
3632                        precedence,
3633                        allow_predicates,
3634                        ctx,
3635                    )
3636                }
3637                Transition::Precedence {
3638                    target,
3639                    precedence: transition_precedence,
3640                } if *transition_precedence >= precedence => {
3641                    state_is_nullable_with_precedence_cached(
3642                        atn,
3643                        *target,
3644                        stop_state_number,
3645                        precedence,
3646                        allow_predicates,
3647                        ctx,
3648                    )
3649                }
3650                Transition::Atom { .. }
3651                | Transition::Range { .. }
3652                | Transition::Set { .. }
3653                | Transition::NotSet { .. }
3654                | Transition::Wildcard { .. }
3655                | Transition::Predicate { .. }
3656                | Transition::Precedence { .. } => false,
3657            })
3658    });
3659    ctx.in_progress.remove(&key);
3660    if !ctx.hit_cycle {
3661        ctx.cache.insert(key, nullable);
3662    }
3663    ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
3664    nullable
3665}
3666
3667/// Classifies what remains after the operator's first token is matched.
3668fn state_operator_token_prefix_reachability(
3669    atn: &Atn,
3670    state_number: usize,
3671    request: OperatorReachabilityRequest,
3672    continuations: &[OperatorRuleContinuation],
3673    visited: &mut BTreeSet<(usize, i32, bool)>,
3674) -> OperatorSymbolReachability {
3675    let key = (
3676        state_number,
3677        request.precedence,
3678        request.predicate_dependent,
3679    );
3680    if !visited.insert(key) {
3681        // Recursive helper rules can grow the return stack without consuming
3682        // input. Delegate cycles to adaptive prediction instead of forcing a
3683        // potentially incomplete one-token answer.
3684        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3685    }
3686    if let Some((continuation, remaining)) = continuations.split_last()
3687        && state_number == continuation.stop_state
3688    {
3689        let result = state_operator_token_prefix_reachability(
3690            atn,
3691            continuation.follow_state,
3692            OperatorReachabilityRequest {
3693                precedence: continuation.return_precedence,
3694                ..request
3695            },
3696            remaining,
3697            visited,
3698        );
3699        visited.remove(&key);
3700        return result;
3701    }
3702    let Some(state) = atn.state(state_number) else {
3703        visited.remove(&key);
3704        return OperatorSymbolReachability::default();
3705    };
3706    let completes_operator = match state.kind() {
3707        AtnStateKind::RuleStop => continuations.is_empty(),
3708        AtnStateKind::StarLoopBack
3709        | AtnStateKind::StarLoopEntry
3710        | AtnStateKind::PlusLoopBack
3711        | AtnStateKind::LoopEnd => state.rule_index() == Some(request.operator_rule_index),
3712        _ => false,
3713    };
3714    if completes_operator {
3715        visited.remove(&key);
3716        return OperatorSymbolReachability::single_token(request.predicate_dependent);
3717    }
3718    let mut reachability = OperatorSymbolReachability::default();
3719    for transition in &state.transitions() {
3720        let transition_reachability = match &transition.data() {
3721            Transition::Rule { rule_index, .. } if *rule_index == request.operator_rule_index => {
3722                OperatorSymbolReachability::single_token(request.predicate_dependent)
3723            }
3724            Transition::Rule {
3725                target,
3726                rule_index,
3727                follow_state,
3728                precedence: rule_precedence,
3729            } => {
3730                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3731                    continue;
3732                };
3733                let mut nested = continuations.to_vec();
3734                nested.push(OperatorRuleContinuation {
3735                    stop_state: child_stop,
3736                    follow_state: *follow_state,
3737                    return_precedence: request.precedence,
3738                });
3739                state_operator_token_prefix_reachability(
3740                    atn,
3741                    *target,
3742                    OperatorReachabilityRequest {
3743                        precedence: *rule_precedence,
3744                        ..request
3745                    },
3746                    &nested,
3747                    visited,
3748                )
3749            }
3750            Transition::Epsilon { target } | Transition::Action { target, .. } => {
3751                state_operator_token_prefix_reachability(
3752                    atn,
3753                    *target,
3754                    request,
3755                    continuations,
3756                    visited,
3757                )
3758            }
3759            Transition::Precedence {
3760                target,
3761                precedence: transition_precedence,
3762            } => {
3763                if *transition_precedence < request.precedence {
3764                    OperatorSymbolReachability::default()
3765                } else {
3766                    state_operator_token_prefix_reachability(
3767                        atn,
3768                        *target,
3769                        request,
3770                        continuations,
3771                        visited,
3772                    )
3773                }
3774            }
3775            Transition::Predicate { target, .. } => state_operator_token_prefix_reachability(
3776                atn,
3777                *target,
3778                OperatorReachabilityRequest {
3779                    predicate_dependent: true,
3780                    ..request
3781                },
3782                continuations,
3783                visited,
3784            ),
3785            Transition::Atom { .. }
3786            | Transition::Range { .. }
3787            | Transition::Set { .. }
3788            | Transition::NotSet { .. }
3789            | Transition::Wildcard { .. } => {
3790                OperatorSymbolReachability::multi_token(request.predicate_dependent)
3791            }
3792        };
3793        reachability = reachability.union(transition_reachability);
3794    }
3795    visited.remove(&key);
3796    reachability
3797}
3798
3799fn state_can_reach_symbol_with_precedence(
3800    atn: &Atn,
3801    state_number: usize,
3802    request: OperatorReachabilityRequest,
3803    nullable_ctx: &mut NullablePrecedenceCtx,
3804    continuations: &mut Vec<OperatorRuleContinuation>,
3805    visited: &mut BTreeSet<(usize, i32, bool)>,
3806) -> OperatorSymbolReachability {
3807    let key = (
3808        state_number,
3809        request.precedence,
3810        request.predicate_dependent,
3811    );
3812    if !visited.insert(key) {
3813        return OperatorSymbolReachability::ADAPTIVE_FALLBACK;
3814    }
3815    let Some(state) = atn.state(state_number) else {
3816        visited.remove(&key);
3817        return OperatorSymbolReachability::default();
3818    };
3819    let mut reachability = OperatorSymbolReachability::default();
3820    for transition in &state.transitions() {
3821        if transition.matches(request.symbol, 1, atn.max_token_type()) {
3822            reachability = reachability.union(state_operator_token_prefix_reachability(
3823                atn,
3824                transition.target(),
3825                request,
3826                continuations,
3827                &mut BTreeSet::new(),
3828            ));
3829            continue;
3830        }
3831        let transition_reachability = match &transition.data() {
3832            Transition::Rule {
3833                target,
3834                rule_index,
3835                follow_state,
3836                precedence: rule_precedence,
3837            } => {
3838                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
3839                    continue;
3840                };
3841                continuations.push(OperatorRuleContinuation {
3842                    stop_state: child_stop,
3843                    follow_state: *follow_state,
3844                    return_precedence: request.precedence,
3845                });
3846                let mut result = state_can_reach_symbol_with_precedence(
3847                    atn,
3848                    *target,
3849                    OperatorReachabilityRequest {
3850                        precedence: *rule_precedence,
3851                        ..request
3852                    },
3853                    nullable_ctx,
3854                    continuations,
3855                    visited,
3856                );
3857                continuations.pop();
3858                if state_is_nullable_with_precedence(
3859                    atn,
3860                    *target,
3861                    child_stop,
3862                    *rule_precedence,
3863                    true,
3864                    nullable_ctx,
3865                ) {
3866                    let child_predicate_dependent = request.predicate_dependent
3867                        || !state_is_nullable_with_precedence(
3868                            atn,
3869                            *target,
3870                            child_stop,
3871                            *rule_precedence,
3872                            false,
3873                            nullable_ctx,
3874                        );
3875                    result = result.union(state_can_reach_symbol_with_precedence(
3876                        atn,
3877                        *follow_state,
3878                        OperatorReachabilityRequest {
3879                            predicate_dependent: child_predicate_dependent,
3880                            ..request
3881                        },
3882                        nullable_ctx,
3883                        continuations,
3884                        visited,
3885                    ));
3886                }
3887                result
3888            }
3889            Transition::Epsilon { target }
3890            | Transition::Action { target, .. }
3891            | Transition::Precedence { target, .. } => {
3892                if matches!(
3893                    &transition.data(),
3894                    Transition::Precedence {
3895                        precedence: transition_precedence,
3896                        ..
3897                    } if *transition_precedence < request.precedence
3898                ) {
3899                    continue;
3900                }
3901                state_can_reach_symbol_with_precedence(
3902                    atn,
3903                    *target,
3904                    request,
3905                    nullable_ctx,
3906                    continuations,
3907                    visited,
3908                )
3909            }
3910            Transition::Predicate { target, .. } => state_can_reach_symbol_with_precedence(
3911                atn,
3912                *target,
3913                OperatorReachabilityRequest {
3914                    predicate_dependent: true,
3915                    ..request
3916                },
3917                nullable_ctx,
3918                continuations,
3919                visited,
3920            ),
3921            Transition::Atom { .. }
3922            | Transition::Range { .. }
3923            | Transition::Set { .. }
3924            | Transition::NotSet { .. }
3925            | Transition::Wildcard { .. } => OperatorSymbolReachability::default(),
3926        };
3927        reachability = reachability.union(transition_reachability);
3928    }
3929    visited.remove(&key);
3930    reachability
3931}
3932
3933fn left_recursive_operator_lookahead(
3934    atn: &Atn,
3935    state_number: usize,
3936    precedence: i32,
3937) -> LeftRecursiveOperatorLookahead {
3938    let Some(state) = atn.state(state_number) else {
3939        return LeftRecursiveOperatorLookahead::default();
3940    };
3941    let Some(operator_rule_index) = state.rule_index() else {
3942        return LeftRecursiveOperatorLookahead::default();
3943    };
3944    let mut lookahead = LeftRecursiveOperatorLookahead::default();
3945    let mut nullable_ctx = NullablePrecedenceCtx {
3946        cache: FxHashMap::default(),
3947        in_progress: BTreeSet::new(),
3948        hit_cycle: false,
3949    };
3950    for transition in &state.transitions() {
3951        let target = transition.target();
3952        if atn
3953            .state(target)
3954            .is_some_and(|state| state.kind() == AtnStateKind::LoopEnd)
3955        {
3956            continue;
3957        }
3958        for symbol in 1..=atn.max_token_type() {
3959            let reachability = state_can_reach_symbol_with_precedence(
3960                atn,
3961                target,
3962                OperatorReachabilityRequest {
3963                    symbol,
3964                    precedence,
3965                    predicate_dependent: false,
3966                    operator_rule_index,
3967                },
3968                &mut nullable_ctx,
3969                &mut Vec::new(),
3970                &mut BTreeSet::new(),
3971            );
3972            if reachability.single_token {
3973                lookahead.single_token.insert(symbol);
3974            }
3975            if reachability.multi_token {
3976                lookahead.multi_token_prefix.insert(symbol);
3977            }
3978            if reachability.predicate_dependent {
3979                lookahead.predicate_dependent.insert(symbol);
3980            }
3981        }
3982    }
3983    lookahead
3984}
3985
3986#[derive(Debug, Default)]
3987struct StateBeforeStopLookahead {
3988    symbols: TokenBitSet,
3989    reaches_context_boundary: bool,
3990}
3991
3992fn state_before_stop_lookahead(
3993    atn: &Atn,
3994    state_number: usize,
3995    stop_state_number: usize,
3996) -> Rc<StateBeforeStopLookahead> {
3997    with_shared_atn_caches(atn, |cache| {
3998        let key = (state_number, stop_state_number);
3999        if let Some(cached) = cache.state_before_stop_lookahead.get(&key) {
4000            return Rc::clone(cached);
4001        }
4002        let mut lookahead = StateBeforeStopLookahead::default();
4003        state_before_stop_lookahead_inner(
4004            atn,
4005            state_number,
4006            stop_state_number,
4007            &mut BTreeSet::new(),
4008            &mut cache.first_set,
4009            &mut lookahead,
4010        );
4011        let lookahead = Rc::new(lookahead);
4012        cache
4013            .state_before_stop_lookahead
4014            .insert(key, Rc::clone(&lookahead));
4015        lookahead
4016    })
4017}
4018
4019fn state_before_stop_lookahead_inner(
4020    atn: &Atn,
4021    state_number: usize,
4022    stop_state_number: usize,
4023    visited: &mut BTreeSet<usize>,
4024    first_set_cache: &mut FirstSetCache,
4025    lookahead: &mut StateBeforeStopLookahead,
4026) {
4027    if state_number == stop_state_number {
4028        lookahead.reaches_context_boundary = true;
4029        return;
4030    }
4031    if !visited.insert(state_number) {
4032        return;
4033    }
4034    let Some(state) = atn.state(state_number) else {
4035        return;
4036    };
4037    if state.kind() == AtnStateKind::RuleStop {
4038        lookahead.reaches_context_boundary = true;
4039        return;
4040    }
4041    for transition in &state.transitions() {
4042        match &transition.data() {
4043            Transition::Epsilon { target }
4044            | Transition::Action { target, .. }
4045            | Transition::Predicate { target, .. }
4046            | Transition::Precedence { target, .. } => {
4047                state_before_stop_lookahead_inner(
4048                    atn,
4049                    *target,
4050                    stop_state_number,
4051                    visited,
4052                    first_set_cache,
4053                    lookahead,
4054                );
4055            }
4056            Transition::Rule {
4057                target,
4058                rule_index,
4059                follow_state,
4060                ..
4061            } => {
4062                let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
4063                    continue;
4064                };
4065                let child = rule_first_set(atn, *target, child_stop, first_set_cache);
4066                lookahead.symbols.extend_from(&child.symbols);
4067                if child.nullable {
4068                    state_before_stop_lookahead_inner(
4069                        atn,
4070                        *follow_state,
4071                        stop_state_number,
4072                        visited,
4073                        first_set_cache,
4074                        lookahead,
4075                    );
4076                }
4077            }
4078            Transition::Atom { .. }
4079            | Transition::Range { .. }
4080            | Transition::Set { .. }
4081            | Transition::NotSet { .. }
4082            | Transition::Wildcard { .. } => {
4083                lookahead.symbols.extend_iter(transition_expected_symbols(
4084                    transition,
4085                    atn.max_token_type(),
4086                ));
4087            }
4088        }
4089    }
4090}
4091
4092fn caller_context_can_match_symbol_before_state(
4093    atn: &Atn,
4094    return_states: impl DoubleEndedIterator<Item = usize>,
4095    stop_state_number: usize,
4096    symbol: i32,
4097) -> bool {
4098    for return_state in return_states.rev() {
4099        let lookahead = state_before_stop_lookahead(atn, return_state, stop_state_number);
4100        if lookahead.symbols.contains(symbol) {
4101            return true;
4102        }
4103        if !lookahead.reaches_context_boundary {
4104            return false;
4105        }
4106    }
4107    false
4108}
4109
4110/// Carries recovery expectations and their restart state through epsilon-only
4111/// paths. ANTLR can report and repair at the decision state even when the
4112/// failed consuming transition is nested under block or loop epsilon edges.
4113fn next_recovery_context(
4114    atn: &Atn,
4115    state: AtnState<'_>,
4116    inherited: &BTreeSet<i32>,
4117    inherited_state: Option<usize>,
4118) -> (BTreeSet<i32>, Option<usize>) {
4119    let state_symbols = state_expected_symbols(atn, state.state_number());
4120    if state.transitions().len() > 1 && !state_symbols.is_empty() {
4121        let mut symbols = state_symbols;
4122        symbols.extend(inherited.iter().copied());
4123        return (symbols, Some(state.state_number()));
4124    }
4125    (inherited.clone(), inherited_state)
4126}
4127
4128fn recovery_expected_symbols(
4129    atn: &Atn,
4130    state_number: usize,
4131    inherited: &BTreeSet<i32>,
4132) -> BTreeSet<i32> {
4133    let mut symbols = state_expected_symbols(atn, state_number);
4134    symbols.extend(inherited.iter().copied());
4135    symbols
4136}
4137
4138/// Fast-recognizer variant of [`next_recovery_context`] that reuses the
4139/// parser's cached state-expected-symbols sets and the inherited `Rc`
4140/// without copying when the state cannot widen recovery.
4141fn fast_next_recovery_context<S, H>(
4142    parser: &mut BaseParser<S, H>,
4143    atn: &Atn,
4144    state: AtnState<'_>,
4145    inherited: &Rc<BTreeSet<i32>>,
4146    inherited_state: Option<usize>,
4147) -> (Rc<BTreeSet<i32>>, Option<usize>)
4148where
4149    S: TokenSource,
4150    H: SemanticHooks,
4151{
4152    if state.transitions().len() <= 1 {
4153        return (Rc::clone(inherited), inherited_state);
4154    }
4155    let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number());
4156    if state_symbols.is_empty() {
4157        return (Rc::clone(inherited), inherited_state);
4158    }
4159    if inherited.is_empty() {
4160        return (state_symbols, Some(state.state_number()));
4161    }
4162    if Rc::ptr_eq(&state_symbols, inherited) {
4163        return (state_symbols, Some(state.state_number()));
4164    }
4165    let mut combined = (*state_symbols).clone();
4166    combined.extend(inherited.iter().copied());
4167    (
4168        parser.intern_recovery_symbols(combined),
4169        Some(state.state_number()),
4170    )
4171}
4172
4173/// Fast-recognizer variant of [`recovery_expected_symbols`] that reuses the
4174/// cached state-expected-symbols and avoids cloning when no widening is
4175/// needed.
4176fn fast_recovery_expected_symbols<S, H>(
4177    parser: &mut BaseParser<S, H>,
4178    atn: &Atn,
4179    state_number: usize,
4180    inherited: &Rc<BTreeSet<i32>>,
4181) -> Rc<BTreeSet<i32>>
4182where
4183    S: TokenSource,
4184    H: SemanticHooks,
4185{
4186    let cached = parser.cached_state_expected_symbols(atn, state_number);
4187    if inherited.is_empty() {
4188        return cached;
4189    }
4190    if cached.is_empty() {
4191        return Rc::clone(inherited);
4192    }
4193    if Rc::ptr_eq(&cached, inherited) {
4194        return cached;
4195    }
4196    let mut combined = (*cached).clone();
4197    combined.extend(inherited.iter().copied());
4198    parser.intern_recovery_symbols(combined)
4199}
4200
4201struct ParserTableSemCtx<'a> {
4202    member_values: &'a mut MemberEnv,
4203    return_values: &'a mut BTreeMap<String, i64>,
4204}
4205
4206impl semir::PredContext for ParserTableSemCtx<'_> {
4207    type TokenText<'a>
4208        = &'a str
4209    where
4210        Self: 'a;
4211
4212    fn la(&mut self, _offset: isize) -> i64 {
4213        i64::from(TOKEN_EOF)
4214    }
4215
4216    fn token_text(&mut self, _offset: isize) -> Option<Self::TokenText<'_>> {
4217        None
4218    }
4219
4220    fn token_index_adjacent(&mut self) -> bool {
4221        false
4222    }
4223
4224    fn ctx_rule_text(&self, _rule_index: usize) -> Option<String> {
4225        None
4226    }
4227
4228    fn member(&self, member: usize) -> Option<i64> {
4229        Some(self.member_values.scalar(member).unwrap_or_default())
4230    }
4231
4232    fn member_top(&self, member: usize) -> Option<i64> {
4233        self.member_values.stack_top(member)
4234    }
4235
4236    fn member_len(&self, member: usize) -> usize {
4237        self.member_values.stack_len(member)
4238    }
4239
4240    fn local_arg(&self) -> Option<i64> {
4241        None
4242    }
4243
4244    fn column(&self) -> Option<i64> {
4245        None
4246    }
4247
4248    fn token_start_column(&self) -> Option<i64> {
4249        None
4250    }
4251
4252    fn token_text_so_far(&self) -> Option<String> {
4253        None
4254    }
4255
4256    fn hook(&mut self, _hook: HookId) -> bool {
4257        false
4258    }
4259}
4260
4261impl semir::ActContext for ParserTableSemCtx<'_> {
4262    fn set_member(&mut self, member: usize, value: i64) {
4263        self.member_values.set_scalar(member, value);
4264    }
4265
4266    fn push_member(&mut self, member: usize, value: i64) {
4267        self.member_values.push_stack(member, value);
4268    }
4269
4270    fn pop_member(&mut self, member: usize) -> Option<i64> {
4271        self.member_values.pop_stack(member)
4272    }
4273
4274    fn set_return(&mut self, name: &str, value: i64) {
4275        self.return_values.insert(name.to_owned(), value);
4276    }
4277
4278    fn action_hook(&mut self, _hook: HookId) {}
4279}
4280
4281/// Applies generated integer-member side effects to one speculative path.
4282fn apply_member_actions(
4283    source_state: usize,
4284    actions: &[ParserMemberAction],
4285    semantics: Option<&ParserSemantics>,
4286    values: &mut MemberEnv,
4287) {
4288    for action in actions
4289        .iter()
4290        .filter(|action| action.source_state == source_state)
4291    {
4292        values.add_scalar(action.member, action.delta);
4293    }
4294    let Some(semantics) = semantics else {
4295        return;
4296    };
4297    let mut return_values = BTreeMap::new();
4298    let mut ctx = ParserTableSemCtx {
4299        member_values: values,
4300        return_values: &mut return_values,
4301    };
4302    for action in semantics
4303        .actions
4304        .iter()
4305        .filter(|action| action.source_state == source_state && action.speculative)
4306    {
4307        semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
4308    }
4309}
4310
4311/// Returns the speculative member state after replaying one ATN action state.
4312fn member_values_after_action(
4313    source_state: usize,
4314    actions: &[ParserMemberAction],
4315    semantics: Option<&ParserSemantics>,
4316    values: &MemberEnv,
4317) -> MemberEnv {
4318    let mut values = values.clone();
4319    apply_member_actions(source_state, actions, semantics, &mut values);
4320    values
4321}
4322
4323/// Returns the speculative rule-return state after replaying one ATN action.
4324fn return_values_after_action(
4325    source_state: usize,
4326    rule_index: usize,
4327    actions: &[ParserReturnAction],
4328    semantics: Option<&ParserSemantics>,
4329    values: &BTreeMap<String, i64>,
4330) -> BTreeMap<String, i64> {
4331    let mut values = values.clone();
4332    for action in actions
4333        .iter()
4334        .filter(|action| action.source_state == source_state && action.rule_index == rule_index)
4335    {
4336        values.insert(action.name.to_owned(), action.value);
4337    }
4338    if let Some(semantics) = semantics {
4339        let mut member_values = MemberEnv::new();
4340        let mut ctx = ParserTableSemCtx {
4341            member_values: &mut member_values,
4342            return_values: &mut values,
4343        };
4344        for action in semantics.actions.iter().filter(|action| {
4345            action.source_state == source_state
4346                && action.rule_index == rule_index
4347                && !action.speculative
4348        }) {
4349            semir::exec_stmt(&semantics.ir, action.stmt, &mut ctx);
4350        }
4351    }
4352    values
4353}
4354
4355/// Resolves the integer argument visible to a child rule invocation.
4356fn rule_local_int_arg(
4357    rule_args: &[ParserRuleArg],
4358    source_state: usize,
4359    rule_index: usize,
4360    local_int_arg: Option<(usize, i64)>,
4361) -> Option<(usize, i64)> {
4362    rule_args
4363        .iter()
4364        .find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
4365        .map(|arg| {
4366            let value = if arg.inherit_local {
4367                local_int_arg.map_or(arg.value, |(_, value)| value)
4368            } else {
4369                arg.value
4370            };
4371            (rule_index, value)
4372        })
4373}
4374
4375/// Builds the terminal recognition outcome for a path that reached its stop
4376/// state.
4377fn stop_outcome(
4378    index: usize,
4379    consumed_eof: bool,
4380    rule_alt_number: usize,
4381    member_values: MemberEnv,
4382    return_values: BTreeMap<String, i64>,
4383) -> Vec<RecognizeOutcome> {
4384    vec![RecognizeOutcome {
4385        index,
4386        consumed_eof,
4387        alt_number: rule_alt_number,
4388        member_values,
4389        return_values,
4390        diagnostics: DiagnosticSeqId::EMPTY,
4391        decisions: Vec::new(),
4392        actions: Vec::new(),
4393        nodes: NodeSeqId::EMPTY,
4394    }]
4395}
4396
4397fn atn_has_observable_action_transitions(atn: &Atn) -> bool {
4398    with_shared_atn_caches(atn, |cache| {
4399        *cache.observable_action_transitions.get_or_insert_with(|| {
4400            atn.states().any(|state| {
4401                state.transitions().iter().any(|transition| {
4402                    matches!(
4403                        &transition.data(),
4404                        Transition::Action {
4405                            action_index: Some(_),
4406                            ..
4407                        }
4408                    )
4409                })
4410            })
4411        })
4412    })
4413}
4414
4415fn atn_has_predicate_transitions(atn: &Atn) -> bool {
4416    with_shared_atn_caches(atn, |cache| {
4417        *cache.predicate_transitions.get_or_insert_with(|| {
4418            atn.states().any(|state| {
4419                state
4420                    .transitions()
4421                    .iter()
4422                    .any(|transition| matches!(&transition.data(), Transition::Predicate { .. }))
4423            })
4424        })
4425    })
4426}
4427
4428/// Reports whether predicates are the only observable semantics the fast
4429/// recognizer must preserve. Without path-local actions, arguments, or return
4430/// state, repeated evaluation at one coordinate and input index receives the
4431/// same runtime context.
4432fn can_use_fast_predicate_recognizer(atn: &Atn, options: &ParserRuntimeOptions<'_>) -> bool {
4433    options.init_action_rules.is_empty()
4434        && options.action_indices.is_empty()
4435        && !options.track_alt_numbers
4436        && options
4437            .predicates
4438            .iter()
4439            .all(|(_, _, predicate)| predicate.failure_message().is_none())
4440        && options.semantics.is_none_or(|semantics| {
4441            semantics.actions.is_empty()
4442                && semantics
4443                    .predicates
4444                    .iter()
4445                    .all(|predicate| predicate.failure_message.is_none())
4446        })
4447        && options.rule_args.is_empty()
4448        && options.member_actions.is_empty()
4449        && options.return_actions.is_empty()
4450        && !atn_has_observable_action_transitions(atn)
4451}
4452
4453#[derive(Clone, Debug, Eq, PartialEq)]
4454struct RecognizeRequest<'a> {
4455    state_number: usize,
4456    stop_state: usize,
4457    index: usize,
4458    rule_start_index: usize,
4459    decision_start_index: Option<usize>,
4460    init_action_rules: &'a BTreeSet<usize>,
4461    predicates: &'a [(usize, usize, ParserPredicate)],
4462    semantics: Option<&'a ParserSemantics>,
4463    rule_args: &'a [ParserRuleArg],
4464    member_actions: &'a [ParserMemberAction],
4465    return_actions: &'a [ParserReturnAction],
4466    local_int_arg: Option<(usize, i64)>,
4467    member_values: MemberEnv,
4468    return_values: BTreeMap<String, i64>,
4469    rule_alt_number: usize,
4470    track_alt_numbers: bool,
4471    consumed_eof: bool,
4472    committed_decision: bool,
4473    /// Current left-recursive precedence threshold, matching ANTLR's
4474    /// `precpred(_ctx, k)` check for generated precedence rules.
4475    precedence: i32,
4476    depth: usize,
4477    recovery_symbols: BTreeSet<i32>,
4478    recovery_state: Option<usize>,
4479}
4480
4481#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
4482struct RecognizeKey {
4483    state_number: usize,
4484    stop_state: usize,
4485    index: usize,
4486    rule_start_index: usize,
4487    decision_start_index: Option<usize>,
4488    local_int_arg: Option<(usize, i64)>,
4489    member_values: MemberEnv,
4490    return_values: BTreeMap<String, i64>,
4491    rule_alt_number: usize,
4492    track_alt_numbers: bool,
4493    consumed_eof: bool,
4494    committed_decision: bool,
4495    precedence: i32,
4496    recovery_symbols: BTreeSet<i32>,
4497    recovery_state: Option<usize>,
4498}
4499
4500#[derive(Clone, Debug, Eq, PartialEq)]
4501struct EpsilonActionStep {
4502    source_state: usize,
4503    target: usize,
4504    action_rule_index: Option<usize>,
4505    action_index: Option<usize>,
4506    left_recursive_boundary: Option<usize>,
4507    decision: Option<usize>,
4508    decision_start_index: Option<usize>,
4509    alt_number: usize,
4510    recovery_symbols: BTreeSet<i32>,
4511    recovery_state: Option<usize>,
4512}
4513
4514struct RecognizeScratch<'a> {
4515    visiting: &'a mut BTreeSet<RecognizeKey>,
4516    memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4517    expected: &'a mut ExpectedTokens,
4518}
4519
4520#[derive(Clone, Debug, Eq, PartialEq)]
4521struct FastRecognizeRequest {
4522    state_number: usize,
4523    stop_state: usize,
4524    index: usize,
4525    rule_start_index: usize,
4526    decision_start_index: Option<usize>,
4527    precedence: i32,
4528    depth: usize,
4529    recovery_symbols: Rc<BTreeSet<i32>>,
4530    recovery_state: Option<usize>,
4531}
4532
4533#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4534struct FastRecognizeTopRequest {
4535    start_state: usize,
4536    stop_state: usize,
4537    start_index: usize,
4538    precedence: i32,
4539    caller_follow_state: Option<usize>,
4540}
4541
4542#[derive(Clone, Copy, Debug)]
4543struct FastPredicateContext<'a> {
4544    predicates: &'a [(usize, usize, ParserPredicate)],
4545    semantics: Option<&'a ParserSemantics>,
4546    member_values: &'a MemberEnv,
4547}
4548
4549#[derive(Clone, Copy, Debug, Default)]
4550struct AltNumberTracking {
4551    public: bool,
4552    context: bool,
4553}
4554
4555impl AltNumberTracking {
4556    const fn any(self) -> bool {
4557        self.public || self.context
4558    }
4559}
4560
4561struct FastRecognizeScratch<'a, 'b> {
4562    predicate_context: Option<FastPredicateContext<'a>>,
4563    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4564    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4565    expected: &'b mut ExpectedTokens,
4566    native_depth: usize,
4567}
4568
4569#[derive(Clone, Copy, Debug)]
4570struct FastRepetitionShape {
4571    enter_target: usize,
4572    exit_target: usize,
4573    body_stop_state: usize,
4574    enter_transition_index: usize,
4575    exit_transition_index: usize,
4576}
4577
4578#[derive(Clone, Copy, Debug)]
4579struct FastRepetitionPath {
4580    index: usize,
4581    deferred_nodes: FastDeferredNodeId,
4582    diagnostics: DiagnosticSeqId,
4583    consumed_eof: bool,
4584}
4585
4586enum FastRepetitionWork {
4587    Enter(FastRepetitionPath),
4588    Exit(FastRepetitionPath),
4589}
4590
4591/// Dense entered/exited coordinate sets for one repetition walk.
4592///
4593/// The start coordinate stays inline so short loops avoid a heap allocation;
4594/// later token indexes use one byte each instead of two hash-table entries.
4595struct FastRepetitionCoordinates {
4596    base_index: usize,
4597    base_state: u8,
4598    later_states: Vec<u8>,
4599}
4600
4601impl FastRepetitionCoordinates {
4602    const ENTERED: u8 = 0;
4603    const EXITED: u8 = 2;
4604
4605    const fn new(base_index: usize) -> Self {
4606        Self {
4607            base_index,
4608            base_state: 0,
4609            later_states: Vec::new(),
4610        }
4611    }
4612
4613    fn insert_entered(&mut self, path: FastRepetitionPath) -> bool {
4614        self.insert(path.index, path.consumed_eof, Self::ENTERED)
4615    }
4616
4617    fn insert_exited(&mut self, path: FastRepetitionPath) -> bool {
4618        self.insert(path.index, path.consumed_eof, Self::EXITED)
4619    }
4620
4621    fn insert(&mut self, index: usize, consumed_eof: bool, base_bit: u8) -> bool {
4622        let Some(offset) = index.checked_sub(self.base_index) else {
4623            return false;
4624        };
4625        let state = if offset == 0 {
4626            &mut self.base_state
4627        } else {
4628            if self.later_states.len() < offset {
4629                self.later_states.resize(offset, 0);
4630            }
4631            &mut self.later_states[offset - 1]
4632        };
4633        let bit = 1 << (base_bit + u8::from(consumed_eof));
4634        let is_new = *state & bit == 0;
4635        *state |= bit;
4636        is_new
4637    }
4638}
4639
4640fn fast_repetition_shape(atn: &Atn, state: AtnState<'_>) -> Option<FastRepetitionShape> {
4641    if state.precedence_rule_decision()
4642        || !matches!(
4643            state.kind(),
4644            AtnStateKind::StarLoopEntry | AtnStateKind::PlusLoopBack
4645        )
4646        || state.transitions().len() != 2
4647    {
4648        return None;
4649    }
4650    let mut enter = None;
4651    let mut exit = None;
4652    for (index, transition) in state.transitions().iter().enumerate() {
4653        if transition.kind() != ParserTransitionKind::Epsilon {
4654            return None;
4655        }
4656        let target = transition.target();
4657        if atn
4658            .state(target)
4659            .is_some_and(|target_state| target_state.kind() == AtnStateKind::LoopEnd)
4660        {
4661            if exit.replace((index, target)).is_some() {
4662                return None;
4663            }
4664        } else if enter.replace((index, target)).is_some() {
4665            return None;
4666        }
4667    }
4668    let (enter_transition_index, enter_target) = enter?;
4669    let (exit_transition_index, exit_target) = exit?;
4670    let body_stop_state = if state.kind() == AtnStateKind::StarLoopEntry {
4671        atn.state(exit_target)?.loop_back_state()?
4672    } else {
4673        state.state_number()
4674    };
4675    Some(FastRepetitionShape {
4676        enter_target,
4677        exit_target,
4678        body_stop_state,
4679        enter_transition_index,
4680        exit_transition_index,
4681    })
4682}
4683
4684fn push_fast_repetition_work(
4685    work: &mut Vec<FastRepetitionWork>,
4686    shape: FastRepetitionShape,
4687    path: FastRepetitionPath,
4688    lookahead: Option<&DecisionLookahead>,
4689    symbol: i32,
4690) {
4691    // Match the normal recognizer's FIRST-set pruning before queueing work.
4692    // Ambiguous body paths still share the coordinate bitmap below.
4693    let transition_is_viable = |transition_index: usize| {
4694        let Some(entry) = lookahead else {
4695            return true;
4696        };
4697        let Some(transition) = entry.transitions.get(transition_index) else {
4698            return true;
4699        };
4700        transition.nullable || transition.symbols.contains(symbol)
4701    };
4702    let enter_is_viable = transition_is_viable(shape.enter_transition_index);
4703    let exit_is_viable = transition_is_viable(shape.exit_transition_index);
4704    if shape.enter_transition_index < shape.exit_transition_index {
4705        if exit_is_viable {
4706            work.push(FastRepetitionWork::Exit(path));
4707        }
4708        if enter_is_viable {
4709            work.push(FastRepetitionWork::Enter(path));
4710        }
4711    } else {
4712        if enter_is_viable {
4713            work.push(FastRepetitionWork::Enter(path));
4714        }
4715        if exit_is_viable {
4716            work.push(FastRepetitionWork::Exit(path));
4717        }
4718    }
4719}
4720
4721/// Memo key for the fast recognizer. `recovery_symbols` must come from
4722/// `intern_recovery_symbols` or `empty_recovery_symbols` before it reaches this
4723/// key, so equal sets share one allocation and the key can store that
4724/// allocation's address instead of cloning an `Rc` and walking the full
4725/// `BTreeSet`. Bypassing the interner would turn content-equal recovery sets
4726/// into distinct cache coordinates.
4727#[derive(Clone, Debug)]
4728struct FastRecognizeKey {
4729    state_number: usize,
4730    stop_state: usize,
4731    index: usize,
4732    rule_start_index: usize,
4733    decision_start_index: Option<usize>,
4734    precedence: i32,
4735    recovery_symbols_id: usize,
4736    recovery_state: Option<usize>,
4737}
4738
4739impl PartialEq for FastRecognizeKey {
4740    fn eq(&self, other: &Self) -> bool {
4741        if self.state_number != other.state_number
4742            || self.stop_state != other.stop_state
4743            || self.index != other.index
4744            || self.rule_start_index != other.rule_start_index
4745            || self.decision_start_index != other.decision_start_index
4746            || self.precedence != other.precedence
4747            || self.recovery_state != other.recovery_state
4748            || self.recovery_symbols_id != other.recovery_symbols_id
4749        {
4750            return false;
4751        }
4752        true
4753    }
4754}
4755
4756impl Eq for FastRecognizeKey {}
4757
4758impl Hash for FastRecognizeKey {
4759    fn hash<H: Hasher>(&self, hasher: &mut H) {
4760        self.state_number.hash(hasher);
4761        self.stop_state.hash(hasher);
4762        self.index.hash(hasher);
4763        self.rule_start_index.hash(hasher);
4764        self.decision_start_index.hash(hasher);
4765        self.precedence.hash(hasher);
4766        self.recovery_state.hash(hasher);
4767        self.recovery_symbols_id.hash(hasher);
4768    }
4769}
4770
4771struct FastRecoveryRequest<'a, 'b> {
4772    atn: &'a Atn,
4773    transition: ParserTransition<'a>,
4774    expected_symbols: Rc<BTreeSet<i32>>,
4775    target: usize,
4776    request: FastRecognizeRequest,
4777    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4778    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4779    expected: &'b mut ExpectedTokens,
4780}
4781
4782struct FastCurrentTokenDeletionRequest<'a, 'b> {
4783    atn: &'a Atn,
4784    expected_symbols: Rc<BTreeSet<i32>>,
4785    request: FastRecognizeRequest,
4786    visiting: &'b mut FxHashSet<FastRecognizeKey>,
4787    memo: &'b mut FxHashMap<FastRecognizeKey, Rc<[FastRecognizeOutcome]>>,
4788    expected: &'b mut ExpectedTokens,
4789}
4790
4791#[derive(Clone, Copy)]
4792struct FastChildRuleFailureRecoveryRequest<'a> {
4793    atn: &'a Atn,
4794    rule_index: usize,
4795    start_index: usize,
4796    follow_state: usize,
4797    stop_state: usize,
4798    expected: &'a ExpectedTokens,
4799}
4800
4801struct RecoveryRequest<'a, 'b> {
4802    atn: &'a Atn,
4803    transition: ParserTransition<'a>,
4804    expected_symbols: BTreeSet<i32>,
4805    target: usize,
4806    request: RecognizeRequest<'a>,
4807    visiting: &'b mut BTreeSet<RecognizeKey>,
4808    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4809    expected: &'b mut ExpectedTokens,
4810}
4811
4812struct CurrentTokenDeletionRequest<'a, 'b> {
4813    atn: &'a Atn,
4814    expected_symbols: BTreeSet<i32>,
4815    request: RecognizeRequest<'a>,
4816    visiting: &'b mut BTreeSet<RecognizeKey>,
4817    memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
4818    expected: &'b mut ExpectedTokens,
4819}
4820
4821/// Carries the state needed after the normal token-recovery strategies fail
4822/// for a consuming transition.
4823struct ConsumingFailureFallback<'a> {
4824    atn: &'a Atn,
4825    target: usize,
4826    request: RecognizeRequest<'a>,
4827    symbol: i32,
4828    expected_symbols: BTreeSet<i32>,
4829    decision_start_index: Option<usize>,
4830    decision: Option<usize>,
4831}
4832
4833/// Captures the parent-rule context needed when a called rule fails before it
4834/// can produce a normal outcome.
4835struct ChildRuleFailureRecovery<'a> {
4836    atn: &'a Atn,
4837    rule_index: usize,
4838    start_index: usize,
4839    follow_state: usize,
4840    stop_state: usize,
4841    member_values: MemberEnv,
4842    expected: &'a ExpectedTokens,
4843}
4844
4845/// Bundles the context needed to evaluate one semantic predicate transition.
4846#[derive(Clone, Copy, Debug)]
4847struct PredicateEval<'a> {
4848    index: usize,
4849    rule_index: usize,
4850    pred_index: usize,
4851    predicates: &'a [(usize, usize, ParserPredicate)],
4852    semantics: Option<&'a ParserSemantics>,
4853    context: Option<&'a ParserRuleContext>,
4854    local_int_arg: Option<(usize, i64)>,
4855    member_values: &'a MemberEnv,
4856}
4857
4858#[derive(Clone, Copy, Debug)]
4859struct ParserSemanticHookRequest<'a> {
4860    index: usize,
4861    rule_index: usize,
4862    pred_index: usize,
4863    context: Option<&'a ParserRuleContext>,
4864    local_int_arg: Option<(usize, i64)>,
4865    member_values: &'a MemberEnv,
4866}
4867
4868/// Predicate-evaluation context over the recognizer's speculative state.
4869///
4870/// This sits in the prediction hot loop, so everything is borrowed: member
4871/// state read-only from the current speculative path and the rule name
4872/// straight from recognizer metadata. Predicates are pure by construction
4873/// ([`semir::PExpr`] has no mutating node); statement execution uses
4874/// [`ParserTableSemCtx`] (speculative member/return replay) and
4875/// [`BaseParser::parser_action_hook`] (committed action hooks) instead.
4876struct ParserSemIrCtx<'a, S, H>
4877where
4878    S: TokenSource,
4879    H: SemanticHooks,
4880{
4881    input: &'a mut CommonTokenStream<S>,
4882    tree_storage: &'a ParseTreeStorage,
4883    semantic_hooks: &'a mut H,
4884    rule_index: usize,
4885    coordinate_index: usize,
4886    rule_name: Option<&'a str>,
4887    context: Option<&'a ParserRuleContext>,
4888    local_int_arg: Option<(usize, i64)>,
4889    member_values: &'a MemberEnv,
4890    invoked_predicates: &'a mut Vec<(usize, usize)>,
4891    /// Policy applied when a [`semir::PExpr::Hook`] node's user hook declines
4892    /// (`None`); keeps the fail-loud fallback chain identical to the legacy
4893    /// table path instead of coercing the miss to `false`.
4894    unknown_predicate_policy: UnknownSemanticPolicy,
4895    unknown_predicate_hits: &'a mut Vec<(usize, usize)>,
4896}
4897
4898impl<S, H> semir::PredContext for ParserSemIrCtx<'_, S, H>
4899where
4900    S: TokenSource,
4901    H: SemanticHooks,
4902{
4903    type TokenText<'a>
4904        = TokenView<'a>
4905    where
4906        Self: 'a;
4907
4908    fn la(&mut self, offset: isize) -> i64 {
4909        i64::from(self.input.la(offset))
4910    }
4911
4912    fn token_text(&mut self, offset: isize) -> Option<Self::TokenText<'_>> {
4913        self.input.lt(offset)
4914    }
4915
4916    fn token_index_adjacent(&mut self) -> bool {
4917        let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
4918            return false;
4919        };
4920        let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
4921            return false;
4922        };
4923        first + 1 == second
4924    }
4925
4926    fn ctx_rule_text(&self, rule_index: usize) -> Option<String> {
4927        self.context.and_then(|context| {
4928            context
4929                .child_rules(self.tree_storage, self.input.token_store(), rule_index)
4930                .next()
4931                .map(crate::tree::RuleNodeView::text)
4932        })
4933    }
4934
4935    fn member(&self, member: usize) -> Option<i64> {
4936        Some(self.member_values.scalar(member).unwrap_or_default())
4937    }
4938
4939    fn member_top(&self, member: usize) -> Option<i64> {
4940        self.member_values.stack_top(member)
4941    }
4942
4943    fn member_len(&self, member: usize) -> usize {
4944        self.member_values.stack_len(member)
4945    }
4946
4947    fn local_arg(&self) -> Option<i64> {
4948        self.local_int_arg.map(|(_, value)| value)
4949    }
4950
4951    fn column(&self) -> Option<i64> {
4952        None
4953    }
4954
4955    fn token_start_column(&self) -> Option<i64> {
4956        None
4957    }
4958
4959    fn token_text_so_far(&self) -> Option<String> {
4960        None
4961    }
4962
4963    fn hook(&mut self, _hook: HookId) -> bool {
4964        let mut ctx = ParserSemCtx {
4965            input: &mut *self.input,
4966            tree_storage: self.tree_storage,
4967            rule_index: self.rule_index,
4968            coordinate_index: self.coordinate_index,
4969            rule_name: self.rule_name.map(str::to_owned),
4970            context: self.context,
4971            tree: None,
4972            local_int_arg: self.local_int_arg,
4973            member_values: self.member_values,
4974            action: None,
4975        };
4976        match self
4977            .semantic_hooks
4978            .sempred(&mut ctx, self.rule_index, self.coordinate_index)
4979        {
4980            Some(result) => result,
4981            // No hook answered this coordinate: fall through to the configured
4982            // policy instead of silently rejecting the alternative, matching the
4983            // legacy table path's dispatch chain (hook → policy).
4984            None => apply_unknown_predicate_policy(
4985                self.unknown_predicate_policy,
4986                self.rule_index,
4987                self.coordinate_index,
4988                self.unknown_predicate_hits,
4989            ),
4990        }
4991    }
4992
4993    fn trace_bool(&mut self, value: bool) -> bool {
4994        let key = (self.rule_index, self.coordinate_index);
4995        if !self.invoked_predicates.contains(&key) {
4996            self.invoked_predicates.push(key);
4997            use std::io::Write as _;
4998            let mut stdout = std::io::stdout().lock();
4999            let _ = writeln!(stdout, "eval={value}");
5000        }
5001        value
5002    }
5003}
5004
5005/// Captures predicate-failure recovery metadata for fail-option predicates.
5006struct PredicateFailureRecovery<'a> {
5007    rule_index: usize,
5008    index: usize,
5009    message: &'a str,
5010    member_values: MemberEnv,
5011    return_values: BTreeMap<String, i64>,
5012    rule_alt_number: usize,
5013}
5014
5015#[derive(Debug)]
5016enum DirectAdaptiveParseControl {
5017    Fallback(DirectAdaptiveFallback),
5018}
5019
5020#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5021enum DirectAdaptiveFallback {
5022    Action,
5023    InvalidAlt,
5024    LeftRecursiveBoundary,
5025    MissingAtn,
5026    NoTransition,
5027    Predicate,
5028    Prediction,
5029    Precedence,
5030    RuleStop,
5031    SemanticContext,
5032    StepLimit,
5033    TokenMismatch,
5034    UnknownDecision,
5035}
5036
5037type DirectAdaptiveParseResult<T> = Result<T, DirectAdaptiveParseControl>;
5038
5039struct DirectAdaptiveParser<'atn, 'sim, S, H = NoSemanticHooks>
5040where
5041    S: TokenSource,
5042    H: SemanticHooks,
5043{
5044    parser: &'sim mut BaseParser<S, H>,
5045    atn: &'atn Atn,
5046    simulator: &'sim mut ParserAtnSimulator<'atn>,
5047    decision_by_state: Vec<Option<usize>>,
5048    steps: usize,
5049}
5050
5051struct CommittedAtnParser<'atn, 'sim, 'options, S, H = NoSemanticHooks>
5052where
5053    S: TokenSource,
5054    H: SemanticHooks,
5055{
5056    parser: &'sim mut BaseParser<S, H>,
5057    atn: &'atn Atn,
5058    simulator: ParserAtnSimulator<'atn>,
5059    options: ParserRuntimeOptions<'options>,
5060    decision_by_state: Vec<Option<usize>>,
5061    action_index_by_state: FxHashMap<usize, usize>,
5062    deferred_actions: Vec<ParserAction>,
5063}
5064
5065struct CommittedRuleOutcome {
5066    tree: ParseTree,
5067    consumed_eof: bool,
5068}
5069
5070struct CommittedDecisionContext<'a> {
5071    precedence: i32,
5072    local_int_arg: Option<(usize, i64)>,
5073    context: &'a mut ParserRuleContext,
5074    entered_loops: &'a mut BTreeSet<usize>,
5075}
5076
5077/// Outcome of a generated token / set / not-set match that may recover.
5078///
5079/// Generated parsers append `children` to the current rule context. `consumed_eof`
5080/// reports whether the match actually consumed a real EOF terminal — it is true
5081/// only on a successful match (or single-token deletion that lands on EOF), and
5082/// always false on single-token insertion, which synthesizes a missing token and
5083/// consumes nothing. Generated code feeds this into `finish_rule`'s
5084/// `consumed_eof`, so the rule stop token is recorded as EOF only when EOF was
5085/// truly matched, matching ANTLR's `matchedEOF` semantics.
5086#[derive(Clone, Debug, Eq, PartialEq)]
5087pub struct GeneratedMatch {
5088    children: GeneratedMatchChildren,
5089    consumed_eof: bool,
5090}
5091
5092#[derive(Clone, Copy)]
5093enum GeneratedExpectedSymbols<'a> {
5094    Tree(&'a BTreeSet<i32>),
5095    TokenSet(ParserIntervalSet<'a>),
5096    TokenSetComplement {
5097        set: ParserIntervalSet<'a>,
5098        min_vocabulary: i32,
5099        max_vocabulary: i32,
5100    },
5101}
5102
5103impl GeneratedExpectedSymbols<'_> {
5104    fn is_empty(self) -> bool {
5105        match self {
5106            Self::Tree(symbols) => symbols.is_empty(),
5107            Self::TokenSet(set) => set.is_empty(),
5108            Self::TokenSetComplement {
5109                set,
5110                min_vocabulary,
5111                max_vocabulary,
5112            } => (min_vocabulary..=max_vocabulary).all(|symbol| set.contains(symbol)),
5113        }
5114    }
5115
5116    fn first(self) -> Option<i32> {
5117        match self {
5118            Self::Tree(symbols) => symbols.iter().next().copied(),
5119            Self::TokenSet(set) => set.ranges().next().map(|(start, _)| start),
5120            Self::TokenSetComplement {
5121                set,
5122                min_vocabulary,
5123                max_vocabulary,
5124            } => (min_vocabulary..=max_vocabulary).find(|symbol| !set.contains(*symbol)),
5125        }
5126    }
5127
5128    fn display(self, vocabulary: &Vocabulary) -> String {
5129        match self {
5130            Self::Tree(symbols) => expected_symbols_display(symbols, vocabulary),
5131            Self::TokenSet(set) => expected_symbols_display_iter(
5132                set.ranges().flat_map(|(start, stop)| start..=stop),
5133                vocabulary,
5134            ),
5135            Self::TokenSetComplement {
5136                set,
5137                min_vocabulary,
5138                max_vocabulary,
5139            } => expected_symbols_display_iter(
5140                (min_vocabulary..=max_vocabulary).filter(|symbol| !set.contains(*symbol)),
5141                vocabulary,
5142            ),
5143        }
5144    }
5145}
5146
5147#[derive(Clone, Debug, Eq, PartialEq)]
5148enum GeneratedMatchChildren {
5149    One(ParseTree),
5150    Many(Vec<ParseTree>),
5151}
5152
5153struct GeneratedMatchChildrenIntoIter {
5154    one: Option<ParseTree>,
5155    many: Option<std::vec::IntoIter<ParseTree>>,
5156}
5157
5158impl Iterator for GeneratedMatchChildrenIntoIter {
5159    type Item = ParseTree;
5160
5161    fn next(&mut self) -> Option<Self::Item> {
5162        self.one
5163            .take()
5164            .or_else(|| self.many.as_mut().and_then(Iterator::next))
5165    }
5166}
5167
5168impl GeneratedMatch {
5169    /// Parse-tree children produced by the match (the matched terminal, an
5170    /// error node plus deleted-then-matched terminal, or a single missing-token
5171    /// error node).
5172    #[must_use]
5173    pub fn children(&self) -> &[ParseTree] {
5174        match &self.children {
5175            GeneratedMatchChildren::One(child) => std::slice::from_ref(child),
5176            GeneratedMatchChildren::Many(children) => children,
5177        }
5178    }
5179
5180    /// Consumes the result, returning the children for appending to the rule
5181    /// context.
5182    #[must_use]
5183    pub fn into_children(self) -> Vec<ParseTree> {
5184        match self.children {
5185            GeneratedMatchChildren::One(child) => vec![child],
5186            GeneratedMatchChildren::Many(children) => children,
5187        }
5188    }
5189
5190    /// Consumes the match without allocating for the common single-child case.
5191    pub fn into_child_iter(self) -> impl Iterator<Item = ParseTree> {
5192        match self.children {
5193            GeneratedMatchChildren::One(child) => GeneratedMatchChildrenIntoIter {
5194                one: Some(child),
5195                many: None,
5196            },
5197            GeneratedMatchChildren::Many(children) => GeneratedMatchChildrenIntoIter {
5198                one: None,
5199                many: Some(children.into_iter()),
5200            },
5201        }
5202    }
5203
5204    /// Whether a real EOF terminal was consumed by this match.
5205    #[must_use]
5206    pub const fn consumed_eof(&self) -> bool {
5207        self.consumed_eof
5208    }
5209}
5210
5211impl<S> BaseParser<S, NoSemanticHooks>
5212where
5213    S: TokenSource,
5214{
5215    /// Creates a parser base over a buffered token stream and recognizer
5216    /// metadata.
5217    pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
5218        Self::with_semantic_hooks(input, data, NoSemanticHooks)
5219    }
5220}
5221
5222impl<S, H> BaseParser<S, H>
5223where
5224    S: TokenSource,
5225    H: SemanticHooks,
5226{
5227    /// Creates a parser base with caller-owned semantic hooks.
5228    pub fn with_semantic_hooks(
5229        input: CommonTokenStream<S>,
5230        data: RecognizerData,
5231        semantic_hooks: H,
5232    ) -> Self {
5233        Self {
5234            input,
5235            tree: ParseTreeStorage::new(),
5236            data,
5237            semantic_hooks,
5238            decision_override_generation: 0,
5239            build_parse_trees: true,
5240            syntax_errors: 0,
5241            report_diagnostic_errors: false,
5242            prediction_mode: PredictionMode::Ll,
5243            prediction_diagnostics: Vec::new(),
5244            reported_prediction_diagnostics: BTreeSet::new(),
5245            generated_parser_diagnostics: Vec::new(),
5246            generated_sync_expected: None,
5247            generated_recovery_error_index: None,
5248            generated_recovery_error_states: BTreeSet::new(),
5249            int_members: MemberEnv::new(),
5250            rule_context_stack: Vec::new(),
5251            rule_context_version: 0,
5252            left_recursive_caller_overlap_cache: std::array::from_fn(|_| None),
5253            pending_invoking_states: Vec::new(),
5254            precedence_stack: vec![0],
5255            invoked_predicates: Vec::new(),
5256            bail_on_error: false,
5257            parse_listeners: Vec::new(),
5258            parse_listener_abort: None,
5259            max_rule_depth: None,
5260            rule_depth_error: None,
5261            recursion_expansions: 0,
5262            recursion_expansion_marks: Vec::new(),
5263            unknown_predicate_policy: UnknownSemanticPolicy::default(),
5264            unknown_predicate_hits: Vec::new(),
5265            unhandled_action_hits: Vec::new(),
5266            rule_first_set_cache: Vec::new(),
5267            state_expected_cache: FxHashMap::default(),
5268            state_expected_token_cache: FxHashMap::default(),
5269            rule_stop_reach_cache: Vec::new(),
5270            recovery_symbols_intern: FxHashMap::default(),
5271            decision_lookahead_cache: FxHashMap::default(),
5272            ll1_decision_cache: FxHashMap::default(),
5273            fast_predicate_cache: FxHashMap::default(),
5274            empty_cycle_cache: Vec::new(),
5275            empty_cycle_cache_atn: None,
5276            clean_memo_mode: CleanMemoMode::Probe,
5277            clean_memo_probe_seen: FxHashSet::default(),
5278            clean_memo_probe_samples: 0,
5279            clean_memo_probe_repeats: 0,
5280            clean_memo_sparse_samples: 0,
5281            fast_recognize_scratch: FastRecognizeTopScratch::default(),
5282            fast_outcome_dedup: FastOutcomeDedupScratch::default(),
5283            empty_recovery_symbols: Rc::new(BTreeSet::new()),
5284            fast_first_set_prefilter: true,
5285            fast_recovery_enabled: true,
5286            fast_token_nodes_enabled: true,
5287            fast_track_alt_numbers: false,
5288            recognition_arena: RecognitionArena::default(),
5289            last_recognition_arena_root: NodeSeqId::EMPTY,
5290            last_recognition_arena_diagnostics: DiagnosticSeqId::EMPTY,
5291        }
5292    }
5293
5294    pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
5295        &mut self.input
5296    }
5297
5298    /// Fully resets parser-owned state and rewinds the current token stream.
5299    ///
5300    /// Parser configuration, semantic hooks, learned DFA tables, and
5301    /// grammar-owned member values are retained.
5302    pub fn reset(&mut self) {
5303        self.input.seek(0);
5304        self.tree.reset();
5305        self.data.set_state(-1);
5306        self.syntax_errors = 0;
5307        self.prediction_diagnostics.clear();
5308        self.reported_prediction_diagnostics.clear();
5309        self.generated_parser_diagnostics.clear();
5310        self.generated_sync_expected = None;
5311        self.reset_generated_recovery_state();
5312        self.rule_context_stack.clear();
5313        self.advance_rule_context_version();
5314        self.left_recursive_caller_overlap_cache = std::array::from_fn(|_| None);
5315        self.pending_invoking_states.clear();
5316        self.precedence_stack.clear();
5317        self.precedence_stack.push(0);
5318        self.invoked_predicates.clear();
5319        self.decision_override_generation = 0;
5320        self.unknown_predicate_hits.clear();
5321        self.unhandled_action_hits.clear();
5322        self.parse_listener_abort = None;
5323        self.rule_depth_error = None;
5324        self.recursion_expansions = 0;
5325        self.recursion_expansion_marks.clear();
5326        self.reset_per_parse_caches();
5327        self.fast_first_set_prefilter = true;
5328        self.fast_recovery_enabled = true;
5329        self.fast_token_nodes_enabled = self.build_parse_trees;
5330        self.fast_track_alt_numbers = false;
5331        self.reset_recognition_arena();
5332    }
5333
5334    /// Replaces the buffered token stream and fully resets this parser.
5335    pub fn set_token_stream(&mut self, input: CommonTokenStream<S>) {
5336        self.input = input;
5337        self.reset();
5338    }
5339
5340    /// Installs the policy for predicate coordinates that no translated table
5341    /// entry or user hook resolves.
5342    ///
5343    /// The interpreter fallback sets this per parse from [`ParserRuntimeOptions`],
5344    /// but generated recursive-descent rules evaluate predicates directly
5345    /// (`parser_semantic_ir_predicate_matches_with_context_and_local`) without
5346    /// going through those options. Generated parser constructors call this so
5347    /// the generated-direct path honors `--sem-unknown` too, instead of leaving
5348    /// the field at its `AssumeTrue` default and silently accepting an
5349    /// unimplemented hook predicate.
5350    pub const fn set_unknown_predicate_policy(&mut self, policy: UnknownSemanticPolicy) {
5351        self.unknown_predicate_policy = policy;
5352    }
5353
5354    /// Reports any unknown predicate coordinate the generated-direct path
5355    /// recorded under [`UnknownSemanticPolicy::Error`], as an
5356    /// [`AntlrError::Unsupported`]. Generated parser entry points call this
5357    /// after a rule completes so the fail-loud policy surfaces on the
5358    /// generated path the same way the interpreter entry surfaces it.
5359    #[must_use]
5360    pub fn take_unknown_semantic_error(&mut self) -> Option<AntlrError> {
5361        let error = self.unknown_semantic_error();
5362        self.unknown_predicate_hits.clear();
5363        self.unhandled_action_hits.clear();
5364        error
5365    }
5366
5367    /// Drops any fail-loud semantic coordinates recorded by a previous parse.
5368    ///
5369    /// Generated parsers call this at the true top-level entry so a parser
5370    /// reused after a fail-loud (or recovered) parse starts clean, without
5371    /// clearing hits mid-parse where a generated parent still needs a child's
5372    /// recorded coordinate to survive to the top-level boundary.
5373    pub fn reset_unknown_semantic_hits(&mut self) {
5374        self.unknown_predicate_hits.clear();
5375        self.unhandled_action_hits.clear();
5376    }
5377
5378    /// Returns the token stream owned by this parser.
5379    #[must_use]
5380    pub const fn token_stream(&self) -> &CommonTokenStream<S> {
5381        &self.input
5382    }
5383
5384    /// Returns the token stream for source replacement or in-place re-feeding.
5385    #[must_use]
5386    pub const fn token_stream_mut(&mut self) -> &mut CommonTokenStream<S> {
5387        &mut self.input
5388    }
5389
5390    /// Returns the canonical token store referenced by parse trees.
5391    #[must_use]
5392    pub const fn token_store(&self) -> &TokenStore {
5393        self.input.token_store()
5394    }
5395
5396    /// Returns the flat CST storage populated by completed rules.
5397    #[must_use]
5398    pub const fn parse_tree_storage(&self) -> &ParseTreeStorage {
5399        &self.tree
5400    }
5401
5402    /// Resolves a compact parse-tree ID into a borrowing node view.
5403    #[must_use]
5404    pub fn node(&self, id: NodeId) -> Node<'_> {
5405        self.tree
5406            .node(self.input.token_store(), id)
5407            .expect("parser-produced node ID should remain valid")
5408    }
5409
5410    /// Consumes this parser and returns its token stream.
5411    #[must_use]
5412    pub fn into_token_stream(self) -> CommonTokenStream<S> {
5413        self.input
5414    }
5415
5416    /// Consumes this parser and returns its canonical token store.
5417    #[must_use]
5418    pub fn into_token_store(self) -> TokenStore {
5419        self.input.into_token_store()
5420    }
5421
5422    /// Consumes the parser and pairs its token store and flat CST with `root`.
5423    #[must_use]
5424    pub fn into_parsed_file(self, root: NodeId) -> ParsedFile {
5425        ParsedFile::new(self.input.into_token_store(), self.tree, root)
5426    }
5427
5428    /// Returns the number of parser syntax errors recorded by committed parse
5429    /// paths so far.
5430    pub const fn number_of_syntax_errors(&self) -> usize {
5431        self.syntax_errors
5432    }
5433
5434    /// Computes reachability and retained-capacity counters for the most recent
5435    /// interpreted-rule recognition arena.
5436    ///
5437    /// The reachability scan is linear in the arena size and is deferred until
5438    /// this instrumentation method is called.
5439    #[must_use]
5440    pub fn recognition_arena_stats(&self) -> RecognitionArenaStats {
5441        self.recognition_arena.stats(
5442            self.last_recognition_arena_root,
5443            self.last_recognition_arena_diagnostics,
5444        )
5445    }
5446
5447    /// Records a syntax error that generated parser code returns as fatal before
5448    /// it can recover into the current rule context.
5449    pub const fn record_generated_syntax_error(&mut self) {
5450        self.record_syntax_errors(1);
5451    }
5452
5453    const fn record_syntax_errors(&mut self, count: usize) {
5454        self.syntax_errors = self.syntax_errors.saturating_add(count);
5455    }
5456
5457    /// Returns whether no interpreted rule context or generated invocation is active.
5458    const fn is_top_level_entry(&self) -> bool {
5459        self.rule_context_stack.is_empty() && self.pending_invoking_states.is_empty()
5460    }
5461
5462    /// Emits diagnostics buffered by the token stream while generated parser
5463    /// code was fetching lexer tokens directly.
5464    pub fn report_token_source_errors(&mut self) {
5465        let errors = self.input.drain_source_errors();
5466        self.dispatch_token_source_errors(&errors);
5467    }
5468
5469    /// Captures generated-parser diagnostics and syntax-error count before a
5470    /// speculative generated rule path.
5471    pub const fn generated_diagnostics_checkpoint(&self) -> GeneratedDiagnosticsCheckpoint {
5472        GeneratedDiagnosticsCheckpoint {
5473            diagnostics_len: self.generated_parser_diagnostics.len(),
5474            syntax_errors: self.syntax_errors,
5475            tree: self.tree.checkpoint(),
5476        }
5477    }
5478
5479    /// Restores generated-parser diagnostics after a speculative rule path failed.
5480    pub fn restore_generated_diagnostics(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
5481        self.generated_parser_diagnostics
5482            .truncate(marker.diagnostics_len);
5483        self.syntax_errors = marker.syntax_errors;
5484        self.rollback_generated_tree(marker);
5485    }
5486
5487    /// Rolls back generated tree state while retaining committed diagnostics.
5488    ///
5489    /// Fatal public entries use this after an earlier child recovery: the
5490    /// partial tree is discarded, but ANTLR has already committed the child's
5491    /// diagnostic and syntax-error count.
5492    pub fn rollback_generated_tree(&mut self, marker: GeneratedDiagnosticsCheckpoint) {
5493        self.generated_sync_expected = None;
5494        self.tree.rollback(marker.tree);
5495    }
5496
5497    /// Emits diagnostics recorded by committed generated parser recovery.
5498    pub fn report_generated_parser_diagnostics(&mut self) {
5499        let parser_diagnostics = std::mem::take(&mut self.generated_parser_diagnostics);
5500        let token_errors = self.input.drain_source_errors();
5501        self.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
5502    }
5503
5504    fn syntax_error_event<'a>(
5505        &'a self,
5506        offending: Option<TokenId>,
5507        line: usize,
5508        column: usize,
5509        message: &'a str,
5510        error: Option<&'a AntlrError>,
5511    ) -> SyntaxErrorEvent<'a> {
5512        let offending = offending.and_then(|token| self.token_store().view(token));
5513        SyntaxErrorEvent {
5514            offending,
5515            line,
5516            column,
5517            span: offending.and_then(|token| token.byte_span()),
5518            message,
5519            error,
5520        }
5521    }
5522
5523    /// Emits a fatal parser error after an entry-rule parse commits to returning it.
5524    ///
5525    /// Generated parsers call this only at their public entry boundary. Nested
5526    /// failures remain silent until generated recovery commits and buffers them.
5527    pub fn report_unrecovered_parser_error(&self, error: &AntlrError) {
5528        let AntlrError::ParserError {
5529            line,
5530            column,
5531            message,
5532            offending,
5533        } = error
5534        else {
5535            return;
5536        };
5537        self.notify_error_listeners(self.syntax_error_event(
5538            *offending,
5539            *line,
5540            *column,
5541            message,
5542            Some(error),
5543        ));
5544    }
5545
5546    fn dispatch_parser_diagnostic(&self, diagnostic: &ParserDiagnostic) {
5547        self.notify_error_listeners(self.syntax_error_event(
5548            diagnostic.offending,
5549            diagnostic.line,
5550            diagnostic.column,
5551            &diagnostic.message,
5552            None,
5553        ));
5554    }
5555
5556    fn dispatch_parser_diagnostics<'a>(
5557        &self,
5558        diagnostics: impl IntoIterator<Item = &'a ParserDiagnostic>,
5559    ) {
5560        for diagnostic in diagnostics {
5561            self.dispatch_parser_diagnostic(diagnostic);
5562        }
5563    }
5564
5565    fn dispatch_token_source_error(&self, source_error: &TokenSourceError) {
5566        if self.input.token_source().report_error(source_error) {
5567            return;
5568        }
5569        // Lexer errors have no offending token: the failure is that no token
5570        // could be produced, matching ANTLR's null offendingSymbol.
5571        self.notify_error_listeners(source_error.into());
5572    }
5573
5574    fn dispatch_token_source_errors(&self, errors: &[TokenSourceError]) {
5575        for error in errors {
5576            self.dispatch_token_source_error(error);
5577        }
5578    }
5579
5580    /// Dispatches generated parser and lexer diagnostics in the same
5581    /// source-position order as ANTLR's lazy token stream reports them.
5582    fn dispatch_generated_diagnostics(
5583        &self,
5584        parser_diagnostics: &[ParserDiagnostic],
5585        token_errors: &[TokenSourceError],
5586    ) {
5587        // Parser diagnostics keep their event order: Java's console and
5588        // DiagnosticErrorListener print reports as prediction produces them,
5589        // so reportAttemptingFullContext precedes reportContextSensitivity
5590        // even though the latter's position is earlier. Buffered token-source
5591        // errors interleave by source position and win ties.
5592        let mut token_iter = token_errors.iter().peekable();
5593        for diagnostic in parser_diagnostics {
5594            while let Some(error) = token_iter.peek() {
5595                if (error.line, error.column) <= (diagnostic.line, diagnostic.column) {
5596                    self.dispatch_token_source_error(error);
5597                    token_iter.next();
5598                } else {
5599                    break;
5600                }
5601            }
5602            self.dispatch_parser_diagnostic(diagnostic);
5603        }
5604        for error in token_iter {
5605            self.dispatch_token_source_error(error);
5606        }
5607    }
5608
5609    /// Buffers ANTLR-style ambiguity diagnostics discovered by generated
5610    /// decision code.
5611    pub fn record_generated_ambiguity_diagnostic(
5612        &mut self,
5613        atn: &Atn,
5614        state_number: usize,
5615        start_index: usize,
5616        stop_index: usize,
5617        alts: &[usize],
5618    ) {
5619        if !self.report_diagnostic_errors || alts.len() < 2 {
5620            return;
5621        }
5622        let Some(decision) = atn
5623            .decision_to_state()
5624            .iter()
5625            .position(|candidate| candidate == state_number)
5626        else {
5627            return;
5628        };
5629        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5630            return;
5631        };
5632        let rule_name = self
5633            .rule_names()
5634            .get(rule_index)
5635            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5636        let input = display_input_text(&self.input.text(start_index, stop_index));
5637        let alts = alts
5638            .iter()
5639            .map(usize::to_string)
5640            .collect::<Vec<_>>()
5641            .join(", ");
5642        let key = (decision, start_index, format!("{alts}:{input}"));
5643        if !self.reported_prediction_diagnostics.insert(key) {
5644            return;
5645        }
5646        let start_diagnostic = diagnostic_for_token(
5647            self.token_at(start_index),
5648            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
5649        );
5650        let stop_diagnostic = diagnostic_for_token(
5651            self.token_at(stop_index),
5652            format!(
5653                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
5654            ),
5655        );
5656        self.generated_parser_diagnostics.push(start_diagnostic);
5657        self.generated_parser_diagnostics.push(stop_diagnostic);
5658    }
5659
5660    /// Buffers ANTLR-style diagnostic-listener messages produced by generated
5661    /// parser calls to the adaptive simulator.
5662    pub fn record_generated_prediction_diagnostic(
5663        &mut self,
5664        atn: &Atn,
5665        state_number: usize,
5666        prediction: &ParserAtnPrediction,
5667    ) {
5668        let Some(diagnostic) = &prediction.diagnostic else {
5669            return;
5670        };
5671        if !self.report_diagnostic_errors || diagnostic.conflicting_alts.len() < 2 {
5672            return;
5673        }
5674        let Some(decision) = atn
5675            .decision_to_state()
5676            .iter()
5677            .position(|candidate| candidate == state_number)
5678        else {
5679            return;
5680        };
5681        let Some(rule_index) = atn.state(state_number).and_then(AtnState::rule_index) else {
5682            return;
5683        };
5684        let rule_name = self
5685            .rule_names()
5686            .get(rule_index)
5687            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
5688        let attempt_input = display_input_text(
5689            &self
5690                .input
5691                .text(diagnostic.start_index, diagnostic.sll_stop_index),
5692        );
5693        let result_input = display_input_text(
5694            &self
5695                .input
5696                .text(diagnostic.start_index, diagnostic.ll_stop_index),
5697        );
5698        let alts = diagnostic
5699            .conflicting_alts
5700            .iter()
5701            .map(usize::to_string)
5702            .collect::<Vec<_>>()
5703            .join(", ");
5704        let key = (
5705            decision,
5706            diagnostic.start_index,
5707            format!(
5708                "{:?}:{alts}:{attempt_input}:{result_input}",
5709                diagnostic.kind
5710            ),
5711        );
5712        if !self.reported_prediction_diagnostics.insert(key) {
5713            return;
5714        }
5715        let attempt_diagnostic = diagnostic_for_token(
5716            self.token_at(diagnostic.sll_stop_index),
5717            format!(
5718                "reportAttemptingFullContext d={decision} ({rule_name}), input='{attempt_input}'"
5719            ),
5720        );
5721        self.generated_parser_diagnostics.push(attempt_diagnostic);
5722        let message = match diagnostic.kind {
5723            ParserAtnPredictionDiagnosticKind::Ambiguity => {
5724                // Java's DiagnosticErrorListener is exactOnly by default:
5725                // non-exact ambiguities (default LL mode stopping at the
5726                // first resolvable conflict) report the attempt above but
5727                // suppress the ambiguity line itself.
5728                if !diagnostic.exact {
5729                    return;
5730                }
5731                format!(
5732                    "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{result_input}'"
5733                )
5734            }
5735            ParserAtnPredictionDiagnosticKind::ContextSensitivity => {
5736                format!(
5737                    "reportContextSensitivity d={decision} ({rule_name}), input='{result_input}'"
5738                )
5739            }
5740        };
5741        let result_diagnostic =
5742            diagnostic_for_token(self.token_at(diagnostic.ll_stop_index), message);
5743        self.generated_parser_diagnostics.push(result_diagnostic);
5744    }
5745
5746    pub fn la(&self, offset: isize) -> i32 {
5747        self.input.la_token(offset)
5748    }
5749
5750    pub fn consume(&mut self) {
5751        IntStream::consume(&mut self.input);
5752    }
5753
5754    /// Sets a generated integer member value used by target-template tests.
5755    pub fn set_int_member(&mut self, member: usize, value: i64) {
5756        self.int_members.set_scalar(member, value);
5757    }
5758
5759    /// Reads a generated integer member value.
5760    pub fn int_member(&self, member: usize) -> Option<i64> {
5761        self.int_members.scalar(member)
5762    }
5763
5764    /// Pushes onto a generated stack-valued member slot (issue #206).
5765    pub fn push_stack_member(&mut self, member: usize, value: i64) {
5766        self.int_members.push_stack(member, value);
5767    }
5768
5769    /// Pops a generated stack-valued member slot, returning the removed value.
5770    /// `None` when the stack is empty.
5771    pub fn pop_stack_member(&mut self, member: usize) -> Option<i64> {
5772        self.int_members.pop_stack(member)
5773    }
5774
5775    /// Reads the top of a generated stack-valued member slot; `None` when
5776    /// empty or never pushed.
5777    #[must_use]
5778    pub fn stack_member_top(&self, member: usize) -> Option<i64> {
5779        self.int_members.stack_top(member)
5780    }
5781
5782    /// Depth of a generated stack-valued member slot.
5783    #[must_use]
5784    pub fn stack_member_len(&self, member: usize) -> usize {
5785        self.int_members.stack_len(member)
5786    }
5787
5788    /// Seeds grammar-declared initial member values (issue #206).
5789    ///
5790    /// Generated parsers call this at construction for a grammar whose
5791    /// `@members` declares an initializer (`private int level = 1;`). Without
5792    /// it the slot would start at 0, so a predicate reading it would reject
5793    /// input the source grammar accepts.
5794    pub fn set_initial_members(&mut self, initial: impl IntoIterator<Item = (usize, i64)>) {
5795        self.int_members = MemberEnv::with_initial_scalars(initial);
5796    }
5797
5798    /// Captures generated member state before speculative generated parser
5799    /// execution.
5800    ///
5801    /// The snapshot covers scalar *and* stack slots: restoring only scalars
5802    /// would leave a rolled-back path's pushes behind.
5803    #[must_use]
5804    pub fn int_members_checkpoint(&self) -> MemberEnv {
5805        self.int_members.clone()
5806    }
5807
5808    /// Restores generated member state after generated parser fallback.
5809    pub fn restore_int_members(&mut self, members: MemberEnv) {
5810        self.int_members = members;
5811    }
5812
5813    /// Adds `delta` to a generated integer member and returns the new value.
5814    pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
5815        self.int_members.add_scalar(member, delta)
5816    }
5817
5818    fn token_type_for_id(&self, id: TokenId) -> i32 {
5819        self.input.token_store().token_type(id).unwrap_or(TOKEN_EOF)
5820    }
5821
5822    fn terminal_tree(&mut self, id: TokenId) -> ParseTree {
5823        if self.build_parse_trees {
5824            self.tree.terminal(id)
5825        } else {
5826            NodeId::placeholder()
5827        }
5828    }
5829
5830    fn error_tree(&mut self, id: TokenId) -> ParseTree {
5831        if self.build_parse_trees {
5832            self.tree.error(id)
5833        } else {
5834            NodeId::placeholder()
5835        }
5836    }
5837
5838    const fn set_context_start(&self, context: &mut ParserRuleContext, id: TokenId) {
5839        context.set_start_id(id);
5840    }
5841
5842    const fn set_context_stop(&self, context: &mut ParserRuleContext, id: TokenId) {
5843        context.set_stop_id(id);
5844    }
5845
5846    fn insert_synthetic_token(
5847        &mut self,
5848        token_type: i32,
5849        text: String,
5850        line: usize,
5851        column: usize,
5852    ) -> Result<TokenId, AntlrError> {
5853        self.input
5854            .insert(
5855                TokenSpec::explicit(token_type, text)
5856                    .with_span(usize::MAX, usize::MAX)
5857                    .with_position(line, column),
5858            )
5859            .map_err(|error| AntlrError::Unsupported(error.to_string()))
5860    }
5861
5862    /// Matches and consumes the current token when it has the expected token
5863    /// type.
5864    ///
5865    /// On success the consumed token is wrapped as a terminal parse-tree node.
5866    /// On mismatch the error carries vocabulary display names so diagnostics are
5867    /// stable across literal and symbolic token naming.
5868    pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
5869        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5870            line: 0,
5871            column: 0,
5872            message: "missing current token".to_owned(),
5873            offending: None,
5874        })?;
5875        let current_type = self.token_type_for_id(current);
5876        if current_type == token_type {
5877            self.reset_generated_recovery_state();
5878            self.consume();
5879            Ok(self.terminal_tree(current))
5880        } else {
5881            Err(AntlrError::MismatchedInput {
5882                expected: self.vocabulary().display_name(token_type),
5883                found: self.vocabulary().display_name(current_type),
5884            })
5885        }
5886    }
5887
5888    /// Matches a token from generated recursive-descent code, including ANTLR's
5889    /// single-token insertion recovery when the active rule context can legally
5890    /// continue at the current input symbol.
5891    pub fn match_token_recovering(
5892        &mut self,
5893        token_type: i32,
5894        follow_state: usize,
5895        atn: &Atn,
5896    ) -> Result<GeneratedMatch, AntlrError> {
5897        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5898            line: 0,
5899            column: 0,
5900            message: "missing current token".to_owned(),
5901            offending: None,
5902        })?;
5903        let current_type = self.token_type_for_id(current);
5904        if current_type == token_type {
5905            self.generated_sync_expected = None;
5906            self.reset_generated_recovery_state();
5907            let consumed_eof = current_type == TOKEN_EOF;
5908            self.consume();
5909            return Ok(GeneratedMatch {
5910                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5911                consumed_eof,
5912            });
5913        }
5914        let mut expected_symbols = BTreeSet::new();
5915        expected_symbols.insert(token_type);
5916        self.recover_generated_match(
5917            current,
5918            GeneratedExpectedSymbols::Tree(&expected_symbols),
5919            follow_state,
5920            atn,
5921            |symbol| symbol == token_type,
5922        )
5923    }
5924
5925    pub fn match_set_recovering(
5926        &mut self,
5927        intervals: &[(i32, i32)],
5928        follow_state: usize,
5929        atn: &Atn,
5930    ) -> Result<GeneratedMatch, AntlrError> {
5931        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5932            line: 0,
5933            column: 0,
5934            message: "missing current token".to_owned(),
5935            offending: None,
5936        })?;
5937        let current_type = self.token_type_for_id(current);
5938        if interval_set_contains(intervals, current_type) {
5939            self.generated_sync_expected = None;
5940            self.reset_generated_recovery_state();
5941            let consumed_eof = current_type == TOKEN_EOF;
5942            self.consume();
5943            return Ok(GeneratedMatch {
5944                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5945                consumed_eof,
5946            });
5947        }
5948        let expected_symbols = interval_symbols(intervals);
5949        self.recover_generated_match(
5950            current,
5951            GeneratedExpectedSymbols::Tree(&expected_symbols),
5952            follow_state,
5953            atn,
5954            |symbol| interval_set_contains(intervals, symbol),
5955        )
5956    }
5957
5958    pub fn match_token_set_recovering(
5959        &mut self,
5960        set: ParserIntervalSet<'_>,
5961        follow_state: usize,
5962        atn: &Atn,
5963    ) -> Result<GeneratedMatch, AntlrError> {
5964        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5965            line: 0,
5966            column: 0,
5967            message: "missing current token".to_owned(),
5968            offending: None,
5969        })?;
5970        let current_type = self.token_type_for_id(current);
5971        if set.contains(current_type) {
5972            self.generated_sync_expected = None;
5973            self.reset_generated_recovery_state();
5974            let consumed_eof = current_type == TOKEN_EOF;
5975            self.consume();
5976            return Ok(GeneratedMatch {
5977                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
5978                consumed_eof,
5979            });
5980        }
5981        self.recover_generated_match(
5982            current,
5983            GeneratedExpectedSymbols::TokenSet(set),
5984            follow_state,
5985            atn,
5986            |symbol| set.contains(symbol),
5987        )
5988    }
5989
5990    pub fn match_not_set_recovering(
5991        &mut self,
5992        intervals: &[(i32, i32)],
5993        min_vocabulary: i32,
5994        max_vocabulary: i32,
5995        follow_state: usize,
5996        atn: &Atn,
5997    ) -> Result<GeneratedMatch, AntlrError> {
5998        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
5999            line: 0,
6000            column: 0,
6001            message: "missing current token".to_owned(),
6002            offending: None,
6003        })?;
6004        let current_type = self.token_type_for_id(current);
6005        if (min_vocabulary..=max_vocabulary).contains(&current_type)
6006            && !interval_set_contains(intervals, current_type)
6007        {
6008            self.generated_sync_expected = None;
6009            self.reset_generated_recovery_state();
6010            let consumed_eof = current_type == TOKEN_EOF;
6011            self.consume();
6012            return Ok(GeneratedMatch {
6013                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
6014                consumed_eof,
6015            });
6016        }
6017        let expected_symbols =
6018            interval_complement_symbols(intervals, min_vocabulary, max_vocabulary);
6019        self.recover_generated_match(
6020            current,
6021            GeneratedExpectedSymbols::Tree(&expected_symbols),
6022            follow_state,
6023            atn,
6024            |symbol| {
6025                (min_vocabulary..=max_vocabulary).contains(&symbol)
6026                    && !interval_set_contains(intervals, symbol)
6027            },
6028        )
6029    }
6030
6031    pub fn match_not_token_set_recovering(
6032        &mut self,
6033        set: ParserIntervalSet<'_>,
6034        min_vocabulary: i32,
6035        max_vocabulary: i32,
6036        follow_state: usize,
6037        atn: &Atn,
6038    ) -> Result<GeneratedMatch, AntlrError> {
6039        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
6040            line: 0,
6041            column: 0,
6042            message: "missing current token".to_owned(),
6043            offending: None,
6044        })?;
6045        let current_type = self.token_type_for_id(current);
6046        if (min_vocabulary..=max_vocabulary).contains(&current_type) && !set.contains(current_type)
6047        {
6048            self.generated_sync_expected = None;
6049            self.reset_generated_recovery_state();
6050            let consumed_eof = current_type == TOKEN_EOF;
6051            self.consume();
6052            return Ok(GeneratedMatch {
6053                children: GeneratedMatchChildren::One(self.terminal_tree(current)),
6054                consumed_eof,
6055            });
6056        }
6057        self.recover_generated_match(
6058            current,
6059            GeneratedExpectedSymbols::TokenSetComplement {
6060                set,
6061                min_vocabulary,
6062                max_vocabulary,
6063            },
6064            follow_state,
6065            atn,
6066            |symbol| (min_vocabulary..=max_vocabulary).contains(&symbol) && !set.contains(symbol),
6067        )
6068    }
6069
6070    fn recover_generated_match(
6071        &mut self,
6072        current: TokenId,
6073        expected_symbols: GeneratedExpectedSymbols<'_>,
6074        follow_state: usize,
6075        atn: &Atn,
6076        matches: impl Fn(i32) -> bool,
6077    ) -> Result<GeneratedMatch, AntlrError> {
6078        let expected_display = expected_symbols.display(self.vocabulary());
6079        let (current_type, current_line, current_column, current_display) = {
6080            let token = self
6081                .input
6082                .token_view(current)
6083                .expect("current token ID should be valid");
6084            (
6085                token.token_type(),
6086                token.line(),
6087                token.column(),
6088                token_input_display(&token),
6089            )
6090        };
6091        if self.bail_on_error {
6092            return Err(AntlrError::ParserError {
6093                line: current_line,
6094                column: current_column,
6095                message: format!("mismatched input {current_display} expecting {expected_display}"),
6096                offending: Some(current),
6097            });
6098        }
6099        if current_type != TOKEN_EOF
6100            && let Some(next) = self.input.lt_id(2)
6101            && matches(self.token_type_for_id(next))
6102        {
6103            let message =
6104                format!("extraneous input {current_display} expecting {expected_display}");
6105            self.push_generated_parser_diagnostic(ParserDiagnostic {
6106                line: current_line,
6107                column: current_column,
6108                message,
6109                offending: Some(current),
6110            });
6111            self.record_syntax_errors(1);
6112            self.generated_sync_expected = None;
6113            // Single-token deletion: skip `current`, then accept `next`. The
6114            // accepted token can be EOF only if it is a real EOF terminal.
6115            let consumed_eof = self.token_type_for_id(next) == TOKEN_EOF;
6116            self.consume();
6117            self.consume();
6118            self.reset_generated_recovery_state();
6119            return Ok(GeneratedMatch {
6120                children: GeneratedMatchChildren::Many(vec![
6121                    self.error_tree(current),
6122                    self.terminal_tree(next),
6123                ]),
6124                consumed_eof,
6125            });
6126        }
6127        let follow_symbols = self.generated_recovery_follow_symbols(atn, follow_state);
6128        // ANTLR's `singleTokenInsertion` inserts a missing token when the state
6129        // *after* the current element can consume the current symbol. At EOF that
6130        // only holds when the follow state EXPLICITLY expects EOF (e.g. an `EOF`
6131        // terminal follows in the rule, as in `r: . EOF;` or `r: ID EOF;`), not
6132        // when EOF merely leaks in from the empty enclosing context (as in
6133        // `start: ID+;` on empty input — antlr#6 `InvalidEmptyInput`, which must
6134        // stay a `mismatched input` error). `follow_symbols` mixes both sources,
6135        // so consult the follow state's OWN expected set for the explicit case.
6136        let follow_explicitly_expects_eof = current_type == TOKEN_EOF
6137            && self
6138                .cached_state_expected_symbols(atn, follow_state)
6139                .contains(&TOKEN_EOF);
6140        if follow_symbols.contains(&current_type)
6141            && (current_type != TOKEN_EOF
6142                || self.rule_context_stack.len() > 1
6143                || expected_symbols.is_empty()
6144                || follow_explicitly_expects_eof)
6145        {
6146            let message = format!("missing {expected_display} at {current_display}");
6147            self.push_generated_parser_diagnostic(ParserDiagnostic {
6148                line: current_line,
6149                column: current_column,
6150                message,
6151                offending: Some(current),
6152            });
6153            self.record_syntax_errors(1);
6154            self.generated_sync_expected = None;
6155            let token_type = expected_symbols.first().unwrap_or(TOKEN_EOF);
6156            let missing_display = expected_symbol_display(token_type, self.vocabulary());
6157            let token = self.insert_synthetic_token(
6158                token_type,
6159                format!("<missing {missing_display}>"),
6160                current_line,
6161                current_column,
6162            )?;
6163            // Single-token insertion synthesizes a missing token and consumes
6164            // nothing, so no EOF terminal is consumed even when the lookahead is
6165            // EOF. Reporting consumed_eof=false here is what keeps `finish_rule`
6166            // from recording EOF as the rule stop on this recovery path.
6167            return Ok(GeneratedMatch {
6168                children: GeneratedMatchChildren::One(self.error_tree(token)),
6169                consumed_eof: false,
6170            });
6171        }
6172        let mismatch_expected_display = self
6173            .generated_sync_expected
6174            .take()
6175            .map_or(expected_display, |symbols| {
6176                expected_symbols_display_iter(symbols.symbols(), self.vocabulary())
6177            });
6178        Err(AntlrError::ParserError {
6179            line: current_line,
6180            column: current_column,
6181            message: format!(
6182                "mismatched input {current_display} expecting {mismatch_expected_display}"
6183            ),
6184            offending: Some(current),
6185        })
6186    }
6187
6188    fn generated_recovery_follow_symbols(
6189        &mut self,
6190        atn: &Atn,
6191        follow_state: usize,
6192    ) -> BTreeSet<i32> {
6193        let mut follow = self
6194            .cached_state_expected_symbols(atn, follow_state)
6195            .as_ref()
6196            .clone();
6197        if self.cached_state_can_reach_rule_stop(atn, follow_state) {
6198            follow.extend(self.context_expected_symbols(atn));
6199        }
6200        follow
6201    }
6202
6203    pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
6204        self.match_token(TOKEN_EOF)
6205    }
6206
6207    pub fn match_set(&mut self, intervals: &[(i32, i32)]) -> Result<ParseTree, AntlrError> {
6208        self.match_interval_condition(intervals, |symbol| interval_set_contains(intervals, symbol))
6209    }
6210
6211    pub fn match_not_set(
6212        &mut self,
6213        intervals: &[(i32, i32)],
6214        min_vocabulary: i32,
6215        max_vocabulary: i32,
6216    ) -> Result<ParseTree, AntlrError> {
6217        self.match_interval_condition(intervals, |symbol| {
6218            (min_vocabulary..=max_vocabulary).contains(&symbol)
6219                && !interval_set_contains(intervals, symbol)
6220        })
6221    }
6222
6223    fn match_interval_condition(
6224        &mut self,
6225        intervals: &[(i32, i32)],
6226        matches: impl FnOnce(i32) -> bool,
6227    ) -> Result<ParseTree, AntlrError> {
6228        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
6229            line: 0,
6230            column: 0,
6231            message: "missing current token".to_owned(),
6232            offending: None,
6233        })?;
6234        let current_type = self.token_type_for_id(current);
6235        if matches(current_type) {
6236            self.reset_generated_recovery_state();
6237            self.consume();
6238            Ok(self.terminal_tree(current))
6239        } else {
6240            Err(AntlrError::MismatchedInput {
6241                expected: self.interval_display(intervals),
6242                found: self.vocabulary().display_name(current_type),
6243            })
6244        }
6245    }
6246
6247    fn interval_display(&self, intervals: &[(i32, i32)]) -> String {
6248        let values = intervals
6249            .iter()
6250            .map(|(start, stop)| {
6251                if start == stop {
6252                    self.vocabulary().display_name(*start)
6253                } else {
6254                    format!(
6255                        "{}..{}",
6256                        self.vocabulary().display_name(*start),
6257                        self.vocabulary().display_name(*stop)
6258                    )
6259                }
6260            })
6261            .collect::<Vec<_>>()
6262            .join(", ");
6263        format!("{{{values}}}")
6264    }
6265
6266    pub fn rule_node(&mut self, context: ParserRuleContext) -> ParseTree {
6267        if self.build_parse_trees {
6268            self.tree.finish_rule(context)
6269        } else {
6270            NodeId::placeholder()
6271        }
6272    }
6273
6274    /// Reports whether the generated rule dispatch should sample native stack
6275    /// capacity before descending into the next rule body.
6276    ///
6277    /// Generated recursive-descent methods otherwise map unbounded grammar
6278    /// nesting straight onto native call depth; sampling every
6279    /// [`GENERATED_RULE_STACK_CHECK_INTERVAL`] rule-context frames keeps the
6280    /// hot path free of per-call probes while guaranteeing a check runs before
6281    /// the red zone can be crossed.
6282    #[must_use]
6283    pub const fn generated_rule_stack_check_due(&self) -> bool {
6284        self.rule_context_stack
6285            .len()
6286            .is_multiple_of(GENERATED_RULE_STACK_CHECK_INTERVAL)
6287    }
6288
6289    /// Returns the positioned error to abort with when the configured
6290    /// rule-nesting depth cap would be exceeded by one more level, or `None`
6291    /// to keep parsing.
6292    ///
6293    /// Generated rule dispatch calls this before deepening — ahead of the
6294    /// rule-frame push at the dispatch boundary and ahead of each
6295    /// left-recursive expansion — letting callers parsing untrusted input
6296    /// bound CPU and tree memory ([`Parser::set_max_rule_depth`]). The
6297    /// inline fast path is one `Option` check when no cap is set (the
6298    /// default) and one addition plus compare when one is; only an actual
6299    /// violation leaves the inline path.
6300    ///
6301    /// The violation is sticky: rule-level recovery absorbs the returned
6302    /// error like any other rule failure and would otherwise keep spending
6303    /// the very resources the cap exists to bound, so every check after the
6304    /// first violation fails until [`Self::take_rule_depth_error`] drains it
6305    /// at the top-level entry.
6306    #[inline]
6307    pub fn rule_depth_cap_violation(&mut self) -> Option<AntlrError> {
6308        let max = self.max_rule_depth?;
6309        // Left-recursive operator iterations deepen the tree without pushing
6310        // a rule frame, so they count alongside the rule-context stack.
6311        if self.rule_depth_error.is_none()
6312            && self.rule_context_stack.len() + self.recursion_expansions < max
6313        {
6314            return None;
6315        }
6316        Some(self.rule_depth_cap_violation_cold(max))
6317    }
6318
6319    #[cold]
6320    fn rule_depth_cap_violation_cold(&mut self, max: usize) -> AntlrError {
6321        if let Some(error) = &self.rule_depth_error {
6322            return error.clone();
6323        }
6324        let current = self.input.lt(1);
6325        let (line, column) = current
6326            .as_ref()
6327            .map_or((0, 0), |token| (token.line(), token.column()));
6328        let error = AntlrError::ParserError {
6329            line,
6330            column,
6331            message: format!("rule nesting depth limit of {max} exceeded"),
6332            offending: current.as_ref().map(Token::token_id),
6333        };
6334        self.rule_depth_error = Some(error.clone());
6335        error
6336    }
6337
6338    /// Drains the sticky depth-cap violation recorded by
6339    /// [`Self::rule_depth_cap_violation`], if any.
6340    ///
6341    /// Generated top-level rule entries call this after recognition so a
6342    /// recovered parse that crossed the cap still fails, and so a reused
6343    /// parser starts its next parse clean.
6344    pub const fn take_rule_depth_error(&mut self) -> Option<AntlrError> {
6345        self.rule_depth_error.take()
6346    }
6347
6348    /// Reports whether a rule-nesting depth cap is configured.
6349    ///
6350    /// Generated dispatch consults this when selecting between the guarded
6351    /// recursive-descent body and the ATN-preferred interpreted fast path:
6352    /// only the generated body enforces the cap, so a configured bound
6353    /// overrides the performance preference.
6354    #[must_use]
6355    pub const fn has_rule_depth_cap(&self) -> bool {
6356        self.max_rule_depth.is_some()
6357    }
6358
6359    /// Registers a listener for committed rule enter/exit events during
6360    /// recognition (ANTLR's `addParseListener`). See [`ParseListener`] for
6361    /// the delivery contract.
6362    pub fn add_parse_listener<L>(&mut self, listener: L)
6363    where
6364        L: ParseListener + 'static,
6365    {
6366        self.parse_listeners
6367            .push(ParseListenerSlot(Box::new(listener)));
6368    }
6369
6370    /// Removes every registered parse listener and returns them, dropping any
6371    /// sticky abort a removed listener had requested.
6372    ///
6373    /// Returning the boxed listeners gives callers back the state they
6374    /// accumulated (depth counters, collected events) without threading
6375    /// shared handles through the listener.
6376    pub fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
6377        self.parse_listener_abort = None;
6378        self.parse_listeners.drain(..).map(|slot| slot.0).collect()
6379    }
6380
6381    /// Reports whether any parse listener is registered.
6382    ///
6383    /// Generated dispatch consults this alongside [`Self::has_rule_depth_cap`]
6384    /// when choosing between the generated body (which fires events) and the
6385    /// ATN-preferred interpreted fast path (which does not).
6386    #[must_use]
6387    pub const fn has_parse_listeners(&self) -> bool {
6388        !self.parse_listeners.is_empty()
6389    }
6390
6391    /// Reports whether semantic hooks may override interpreted decisions.
6392    ///
6393    /// Generated parsers use this to keep adaptive performance routing from
6394    /// changing parse semantics after a decision DFA becomes warm.
6395    #[doc(hidden)]
6396    #[must_use]
6397    pub fn observes_parser_decisions(&self) -> bool {
6398        self.semantic_hooks.observes_parser_decisions()
6399    }
6400
6401    /// Fires `enter_every_rule` on registered parse listeners, returning the
6402    /// abort error if any listener requested one.
6403    ///
6404    /// Generated rule dispatch calls this after the depth-cap probe and
6405    /// before the rule body runs; the generated left-recursive loop calls it
6406    /// once per operator expansion, mirroring upstream ANTLR's simulated
6407    /// rule-entry event for `pushNewRecursionContext`. A listener abort is
6408    /// sticky exactly like a depth-cap violation: rule-level recovery absorbs
6409    /// the returned error, so the flag holds until the top-level entry drains
6410    /// it via [`Self::take_parse_listener_abort`] and fails the parse.
6411    pub fn parse_listener_enter_rule(&mut self, rule_index: usize) -> Option<AntlrError> {
6412        if self.parse_listeners.is_empty() {
6413            return None;
6414        }
6415        self.parse_listener_enter_rule_dispatch(rule_index)
6416    }
6417
6418    fn parse_listener_enter_rule_dispatch(&mut self, rule_index: usize) -> Option<AntlrError> {
6419        if let Some(error) = &self.parse_listener_abort {
6420            return Some(error.clone());
6421        }
6422        let event = EnterRuleEvent {
6423            rule_index,
6424            current: self.input.lt(1),
6425        };
6426        // Split borrows: the token view borrows the input while listeners
6427        // need `&mut`, so listeners are taken out for the dispatch. Listener
6428        // methods have no parser access and cannot observe the absence.
6429        let mut listeners = std::mem::take(&mut self.parse_listeners);
6430        let mut abort = None;
6431        for slot in &mut listeners {
6432            if let Err(error) = slot.0.enter_every_rule(&event) {
6433                abort = Some(error);
6434                break;
6435            }
6436        }
6437        self.parse_listeners = listeners;
6438        if let Some(error) = abort {
6439            self.parse_listener_abort = Some(error.clone());
6440            return Some(error);
6441        }
6442        None
6443    }
6444
6445    /// Fires `exit_every_rule` on registered parse listeners.
6446    ///
6447    /// Generated rule bodies call this on every exit path — success and
6448    /// recovery alike — keeping enter/exit pairs balanced, and the generated
6449    /// left-recursive loop calls it once per operator expansion when the rule
6450    /// finishes unrolling.
6451    pub fn parse_listener_exit_rule(&mut self, rule_index: usize) {
6452        if self.parse_listeners.is_empty() {
6453            return;
6454        }
6455        // Reverse registration order, matching upstream ANTLR
6456        // (`Parser.triggerExitRuleEvent` walks listeners back to front).
6457        for slot in self.parse_listeners.iter_mut().rev() {
6458            slot.0.exit_every_rule(rule_index);
6459        }
6460    }
6461
6462    /// Drains the sticky parse-listener abort recorded by
6463    /// [`Self::parse_listener_enter_rule`], if any.
6464    ///
6465    /// Generated top-level rule entries call this after recognition so an
6466    /// aborted parse fails even when recovery produced a tree, and so a
6467    /// reused parser starts its next parse clean.
6468    pub const fn take_parse_listener_abort(&mut self) -> Option<AntlrError> {
6469        self.parse_listener_abort.take()
6470    }
6471
6472    /// Drains every sticky parse abort — the depth-cap violation and the
6473    /// parse-listener abort — returning the depth error preferentially.
6474    ///
6475    /// Generated top-level rule entries call this on both exit paths: the
6476    /// recorded abort wins over errors derived from it (recovery may have
6477    /// absorbed the aborted rule and failed differently later), a recovered
6478    /// `Ok` tree still fails when an abort was recorded, and draining leaves
6479    /// the instance clean for the next entry-rule call.
6480    pub fn take_parse_abort(&mut self) -> Option<AntlrError> {
6481        if let Some(error) = self.rule_depth_error.take() {
6482            self.parse_listener_abort = None;
6483            return Some(error);
6484        }
6485        self.parse_listener_abort.take()
6486    }
6487
6488    /// Enters a generated parser rule and returns the context object the
6489    /// generated method should populate.
6490    pub fn enter_rule(&mut self, state: isize, rule_index: usize) -> ParserRuleContext {
6491        self.set_state(state);
6492        let invoking_state = self.pending_invoking_states.pop().unwrap_or(state);
6493        self.rule_context_stack.push(RuleContextFrame {
6494            rule_index,
6495            invoking_state,
6496        });
6497        self.advance_rule_context_version();
6498        let start_index = self.current_visible_index();
6499        let mut context = ParserRuleContext::new(rule_index, invoking_state);
6500        if let Some(token) = self.token_id_at(start_index) {
6501            self.set_context_start(&mut context, token);
6502        }
6503        context
6504    }
6505
6506    /// Records the ATN source state for the next generated rule invocation.
6507    ///
6508    /// ANTLR's full-context prediction reconstructs caller follow states from
6509    /// each active rule context's invoking state. Generated Rust rule methods are
6510    /// plain functions, so the caller supplies that ATN state just before making a
6511    /// rule call; `enter_rule` consumes it when the callee starts.
6512    pub fn push_invoking_state(&mut self, invoking_state: isize) -> usize {
6513        let marker = self.pending_invoking_states.len();
6514        self.pending_invoking_states.push(invoking_state);
6515        marker
6516    }
6517
6518    /// Discards an invoking-state marker if the callee did not consume it.
6519    pub fn discard_invoking_state(&mut self, marker: usize) {
6520        self.pending_invoking_states.truncate(marker);
6521    }
6522
6523    /// Exits the current generated parser rule.
6524    pub fn exit_rule(&mut self) {
6525        self.rule_context_stack.pop();
6526        self.advance_rule_context_version();
6527    }
6528
6529    /// Returns caller follow states for interning in a parser ATN simulator's
6530    /// prediction store. States are yielded outermost to innermost.
6531    pub fn prediction_context_return_states<'a>(
6532        &'a self,
6533        atn: &'a Atn,
6534    ) -> impl DoubleEndedIterator<Item = usize> + 'a {
6535        self.rule_context_stack.iter().skip(1).filter_map(|frame| {
6536            let Ok(state_number) = usize::try_from(frame.invoking_state) else {
6537                return None;
6538            };
6539            let Some(Transition::Rule { follow_state, .. }) = atn
6540                .state(state_number)
6541                .and_then(|state| state.transitions().first())
6542                .map(ParserTransition::data)
6543            else {
6544                return None;
6545            };
6546            Some(follow_state)
6547        })
6548    }
6549
6550    /// Returns a generation that changes whenever the active rule stack changes.
6551    ///
6552    /// A parser ATN simulator uses this to reuse an interned outer prediction
6553    /// context while generated predictions remain in the same rule context.
6554    pub const fn rule_context_version(&self) -> usize {
6555        self.rule_context_version
6556    }
6557
6558    const fn advance_rule_context_version(&mut self) {
6559        self.rule_context_version = self.rule_context_version.wrapping_add(1);
6560    }
6561
6562    /// Adds a generated parser child only when parse-tree construction is
6563    /// enabled. The match is recorded on the context either way (via `add_child`,
6564    /// or `note_matched_child` when trees are off) so generated recovery can tell
6565    /// whether the rule has matched anything yet without depending on `children`.
6566    pub fn add_parse_child(&mut self, context: &mut ParserRuleContext, child: ParseTree) {
6567        if self.build_parse_trees {
6568            self.tree.add_child(context, child);
6569        } else {
6570            context.note_matched_child();
6571        }
6572    }
6573
6574    /// Combined sync-decision + child-append + sync-error capture.
6575    ///
6576    /// Replaces the 9-line generated sync-decision motif with a single call.
6577    /// On success, appends any sync children to the context. On error, stores
6578    /// the error in `sync_error` and returns `Err` for the caller to propagate.
6579    #[inline]
6580    pub fn sync_into(
6581        &mut self,
6582        atn: &Atn,
6583        state_number: usize,
6584        context: &mut ParserRuleContext,
6585        loop_back: bool,
6586        sync_error: &mut Option<AntlrError>,
6587    ) -> Result<(), AntlrError> {
6588        let current_context_empty = !context.has_matched_child();
6589        match self.sync_decision(atn, state_number, current_context_empty, loop_back) {
6590            Ok(children) => {
6591                for child in children {
6592                    self.add_parse_child(context, child);
6593                }
6594                Ok(())
6595            }
6596            Err(error) => {
6597                *sync_error = Some(error.clone());
6598                Err(error)
6599            }
6600        }
6601    }
6602
6603    /// Combined token-match + EOF accounting + child append.
6604    ///
6605    /// Replaces the 3-line generated token-match motif with a single call.
6606    #[inline]
6607    pub fn match_token_into(
6608        &mut self,
6609        token_type: i32,
6610        follow_state: usize,
6611        atn: &Atn,
6612        context: &mut ParserRuleContext,
6613        consumed_eof: &mut bool,
6614    ) -> Result<(), AntlrError> {
6615        let m = self.match_token_recovering(token_type, follow_state, atn)?;
6616        *consumed_eof |= m.consumed_eof();
6617        for child in m.into_child_iter() {
6618            self.add_parse_child(context, child);
6619        }
6620        Ok(())
6621    }
6622
6623    /// Combined set-match + EOF accounting + child append (ATN token-set
6624    /// variant).
6625    #[inline]
6626    pub fn match_token_set_into(
6627        &mut self,
6628        token_set: ParserIntervalSet<'_>,
6629        follow_state: usize,
6630        atn: &Atn,
6631        context: &mut ParserRuleContext,
6632        consumed_eof: &mut bool,
6633    ) -> Result<(), AntlrError> {
6634        let m = self.match_token_set_recovering(token_set, follow_state, atn)?;
6635        *consumed_eof |= m.consumed_eof();
6636        for child in m.into_child_iter() {
6637            self.add_parse_child(context, child);
6638        }
6639        Ok(())
6640    }
6641
6642    /// Combined set-match + EOF accounting + child append (inline intervals
6643    /// variant).
6644    #[inline]
6645    pub fn match_set_into(
6646        &mut self,
6647        intervals: &[(i32, i32)],
6648        follow_state: usize,
6649        atn: &Atn,
6650        context: &mut ParserRuleContext,
6651        consumed_eof: &mut bool,
6652    ) -> Result<(), AntlrError> {
6653        let m = self.match_set_recovering(intervals, follow_state, atn)?;
6654        *consumed_eof |= m.consumed_eof();
6655        for child in m.into_child_iter() {
6656            self.add_parse_child(context, child);
6657        }
6658        Ok(())
6659    }
6660
6661    /// Combined not-set-match + EOF accounting + child append (ATN token-set
6662    /// complement variant).
6663    #[allow(clippy::too_many_arguments)]
6664    #[inline]
6665    pub fn match_not_token_set_into(
6666        &mut self,
6667        token_set: ParserIntervalSet<'_>,
6668        min_vocabulary: i32,
6669        max_vocabulary: i32,
6670        follow_state: usize,
6671        atn: &Atn,
6672        context: &mut ParserRuleContext,
6673        consumed_eof: &mut bool,
6674    ) -> Result<(), AntlrError> {
6675        let m = self.match_not_token_set_recovering(
6676            token_set,
6677            min_vocabulary,
6678            max_vocabulary,
6679            follow_state,
6680            atn,
6681        )?;
6682        *consumed_eof |= m.consumed_eof();
6683        for child in m.into_child_iter() {
6684            self.add_parse_child(context, child);
6685        }
6686        Ok(())
6687    }
6688
6689    /// Combined not-set-match + EOF accounting + child append (inline intervals
6690    /// complement variant).
6691    #[allow(clippy::too_many_arguments)]
6692    #[inline]
6693    pub fn match_not_set_into(
6694        &mut self,
6695        intervals: &[(i32, i32)],
6696        min_vocabulary: i32,
6697        max_vocabulary: i32,
6698        follow_state: usize,
6699        atn: &Atn,
6700        context: &mut ParserRuleContext,
6701        consumed_eof: &mut bool,
6702    ) -> Result<(), AntlrError> {
6703        let m = self.match_not_set_recovering(
6704            intervals,
6705            min_vocabulary,
6706            max_vocabulary,
6707            follow_state,
6708            atn,
6709        )?;
6710        *consumed_eof |= m.consumed_eof();
6711        for child in m.into_child_iter() {
6712            self.add_parse_child(context, child);
6713        }
6714        Ok(())
6715    }
6716
6717    fn release_tree_scratch_if_idle(&mut self) {
6718        if self.rule_context_stack.is_empty() {
6719            self.tree.release_scratch();
6720        }
6721    }
6722
6723    /// Finishes a generated parser rule and returns its parse-tree node.
6724    pub fn finish_rule(&mut self, mut context: ParserRuleContext, consumed_eof: bool) -> ParseTree {
6725        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6726        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
6727            self.set_context_stop(&mut context, token);
6728        }
6729        let node = self.rule_node(context);
6730        self.exit_rule();
6731        self.release_tree_scratch_if_idle();
6732        node
6733    }
6734
6735    /// Recovers a generated rule catch block after a committed mismatch.
6736    ///
6737    /// ANTLR's generated parsers catch recognition errors inside each rule,
6738    /// report the original error, then consume unexpected tokens until the
6739    /// caller's recovery set can resume. Tokens consumed during recovery become
6740    /// error nodes in the current rule context.
6741    pub fn recover_generated_rule(
6742        &mut self,
6743        context: &mut ParserRuleContext,
6744        atn: &Atn,
6745        error: AntlrError,
6746    ) {
6747        let diagnostic = self.generated_rule_error_diagnostic(error);
6748        self.push_generated_parser_diagnostic(diagnostic);
6749        self.generated_sync_expected = None;
6750        let error_index = self.input.index();
6751        let error_state = self.data.state();
6752        // Match ANTLR's lastErrorIndex/lastErrorStates failsafe: a recovery
6753        // token can also be in the caller's follow set, leaving the cursor
6754        // unchanged and allowing generated outer decisions to revisit the same
6755        // failed state forever.
6756        if self.generated_recovery_error_index == Some(error_index)
6757            && self.generated_recovery_error_states.contains(&error_state)
6758            && self.la(1) != TOKEN_EOF
6759            && let Some(token) = self.input.lt_id(1)
6760        {
6761            self.consume();
6762            let child = self.error_tree(token);
6763            self.add_parse_child(context, child);
6764        }
6765        let recovery_index = self.input.index();
6766        if self.generated_recovery_error_index != Some(recovery_index) {
6767            self.generated_recovery_error_index = Some(recovery_index);
6768            self.generated_recovery_error_states.clear();
6769        }
6770        self.generated_recovery_error_states.insert(error_state);
6771        let recovery_symbols = self.context_expected_symbols(atn);
6772        loop {
6773            let symbol = self.la(1);
6774            if symbol == TOKEN_EOF || recovery_symbols.contains(&symbol) {
6775                break;
6776            }
6777            let Some(token) = self.input.lt_id(1) else {
6778                break;
6779            };
6780            self.consume();
6781            let child = self.error_tree(token);
6782            self.add_parse_child(context, child);
6783        }
6784        self.record_syntax_errors(1);
6785    }
6786
6787    fn reset_generated_recovery_state(&mut self) {
6788        if self.generated_recovery_error_index.is_some() {
6789            self.generated_recovery_error_index = None;
6790            self.generated_recovery_error_states.clear();
6791        }
6792    }
6793
6794    fn push_generated_parser_diagnostic(&mut self, diagnostic: ParserDiagnostic) {
6795        if self
6796            .generated_parser_diagnostics
6797            .iter()
6798            .any(|existing| existing == &diagnostic)
6799        {
6800            return;
6801        }
6802        self.generated_parser_diagnostics.push(diagnostic);
6803    }
6804
6805    fn generated_rule_error_diagnostic(&self, error: AntlrError) -> ParserDiagnostic {
6806        match error {
6807            // The anchor recorded where the error was built wins over the
6808            // current lookahead: prediction restores the cursor, so lt(1)
6809            // here can point at the decision start rather than the error.
6810            AntlrError::ParserError {
6811                line,
6812                column,
6813                message,
6814                offending,
6815            } => ParserDiagnostic {
6816                line,
6817                column,
6818                message,
6819                offending,
6820            },
6821            AntlrError::MismatchedInput { expected, found } => diagnostic_for_token(
6822                self.input.lt(1),
6823                format!("mismatched input {found} expecting {expected}"),
6824            ),
6825            AntlrError::NoViableAlternative { input } => diagnostic_for_token(
6826                self.input.lt(1),
6827                format!("no viable alternative at input {input}"),
6828            ),
6829            AntlrError::LexerError {
6830                line,
6831                column,
6832                message,
6833            } => ParserDiagnostic {
6834                line,
6835                column,
6836                message,
6837                offending: None,
6838            },
6839            AntlrError::Unsupported(message) => diagnostic_for_token(self.input.lt(1), message),
6840        }
6841    }
6842
6843    /// Finishes a generated left-recursive parser rule and returns its parse-tree node.
6844    pub fn finish_recursion_rule(
6845        &mut self,
6846        mut context: ParserRuleContext,
6847        consumed_eof: bool,
6848    ) -> ParseTree {
6849        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
6850        if let Some(token) = stop_index.and_then(|index| self.token_id_at(index)) {
6851            self.set_context_stop(&mut context, token);
6852        }
6853        let node = self.rule_node(context);
6854        self.unroll_recursion_context();
6855        self.release_tree_scratch_if_idle();
6856        node
6857    }
6858
6859    /// Enters a generated left-recursive rule at `precedence`.
6860    pub fn enter_recursion_rule(
6861        &mut self,
6862        state: isize,
6863        rule_index: usize,
6864        precedence: i32,
6865    ) -> ParserRuleContext {
6866        self.precedence_stack.push(precedence);
6867        self.recursion_expansion_marks
6868            .push(self.recursion_expansions);
6869        self.enter_rule(state, rule_index)
6870    }
6871
6872    /// Replaces the current context while expanding a left-recursive rule.
6873    pub fn push_new_recursion_context(
6874        &mut self,
6875        state: isize,
6876        rule_index: usize,
6877    ) -> ParserRuleContext {
6878        self.set_state(state);
6879        // Counts toward the depth cap: upstream treats this as rule entry
6880        // (`Parser.pushNewRecursionContext` fires `triggerEnterRuleEvent`).
6881        self.recursion_expansions += 1;
6882        ParserRuleContext::new(rule_index, state)
6883    }
6884
6885    /// Wraps the previous left-recursive context before parsing the next
6886    /// recursive operator alternative.
6887    pub fn push_new_recursion_context_with_previous(
6888        &mut self,
6889        state: isize,
6890        rule_index: usize,
6891        current: &mut ParserRuleContext,
6892    ) {
6893        self.set_state(state);
6894        // Counts toward the depth cap: each operator iteration deepens the
6895        // parse tree one level without pushing a rule frame, and upstream
6896        // fires a rule-entry listener event for it. The parse-listener enter
6897        // event for this expansion fires from the generated loop's probe
6898        // just before this call, where a listener abort can propagate.
6899        self.recursion_expansions += 1;
6900        if let Some(stop) = self
6901            .rule_stop_token_index(self.input.index(), false)
6902            .and_then(|index| self.token_id_at(index))
6903        {
6904            self.set_context_stop(current, stop);
6905        }
6906        let invoking_state = current.invoking_state();
6907        let start = current.start_id();
6908        let mut replacement = ParserRuleContext::new(rule_index, invoking_state);
6909        if start.is_some() {
6910            replacement.set_start_from_context(current);
6911        }
6912        let previous = std::mem::replace(current, replacement);
6913        if self.build_parse_trees {
6914            let previous = self.rule_node(previous);
6915            self.tree.add_child(current, previous);
6916        }
6917    }
6918
6919    /// Leaves a generated left-recursive rule.
6920    pub fn unroll_recursion_context(&mut self) {
6921        if self.precedence_stack.len() > 1 {
6922            self.precedence_stack.pop();
6923        }
6924        // Parse-listener exits for expansions fire inside the generated
6925        // operator loop (top of each pass, upstream's `recRuleSetPrevCtx`),
6926        // and the dispatch wrapper's exit covers the final live context —
6927        // upstream's `unrollRecursionContexts` walks exactly one link, so no
6928        // batched exits happen here. Only the depth-cap accounting rewinds.
6929        if let Some(mark) = self.recursion_expansion_marks.pop() {
6930            self.recursion_expansions = mark;
6931        }
6932        self.exit_rule();
6933    }
6934
6935    /// Predicts a generated left-recursive loop from one-token lookahead.
6936    ///
6937    /// `Some(true)` enters the operator alternative, `Some(false)` exits, and
6938    /// `None` means caller overlap, a dangerous multi-token prefix, or an
6939    /// unresolved semantic predicate requires full `StarLoopEntry` adaptive
6940    /// prediction (which includes the exit alt and precedence filtering).
6941    ///
6942    /// Single-token operators and multi-token prefixes that do not shadow a
6943    /// lower-precedence single-token operator keep the one-token enter fast path.
6944    ///
6945    /// Multi-token prefixes that **do** shadow a lower-precedence single-token
6946    /// operator must not force enter; the adaptive decision may need to select
6947    /// the loop exit instead.
6948    pub fn left_recursive_loop_enter_prediction(
6949        &mut self,
6950        atn: &Atn,
6951        state_number: usize,
6952        precedence: i32,
6953    ) -> Option<bool> {
6954        let symbol = self.la(1);
6955        if symbol == TOKEN_EOF {
6956            return Some(false);
6957        }
6958        let operator_lookahead =
6959            Self::cached_left_recursive_operator_lookahead(atn, state_number, precedence);
6960        let can_single = operator_lookahead.single_token.contains(symbol);
6961        let can_multi = operator_lookahead.multi_token_prefix.contains(symbol);
6962        let can_predicate = operator_lookahead.predicate_dependent.contains(symbol);
6963        if !can_single && !can_multi && !can_predicate {
6964            return Some(false);
6965        }
6966        if can_predicate && !can_single {
6967            return None;
6968        }
6969        // Multi-token-only at this precedence, but the same symbol is a
6970        // single-token operator at precedence 0: defer so exit can win when the
6971        // multi-token sequence does not actually match (e.g. `>` vs `>>`).
6972        if !can_single && can_multi && precedence > 0 {
6973            let baseline = Self::cached_left_recursive_operator_lookahead(atn, state_number, 0);
6974            if baseline.single_token.contains(symbol) {
6975                return None;
6976            }
6977        }
6978        let atn_key = SharedAtnCacheKey::for_atn(atn);
6979        let cached_overlap = self
6980            .left_recursive_caller_overlap_cache
6981            .iter()
6982            .flatten()
6983            .find(|entry| {
6984                entry.atn_key == atn_key
6985                    && entry.state_number == state_number
6986                    && entry.symbol == symbol
6987                    && entry.context_version == self.rule_context_version
6988            })
6989            .map(|entry| entry.overlaps);
6990        let caller_overlaps = cached_overlap.unwrap_or_else(|| {
6991            let overlaps = caller_context_can_match_symbol_before_state(
6992                atn,
6993                self.prediction_context_return_states(atn),
6994                state_number,
6995                symbol,
6996            );
6997            if let Some(slot) = self
6998                .left_recursive_caller_overlap_cache
6999                .iter_mut()
7000                .find(|slot| slot.is_none())
7001            {
7002                *slot = Some(LeftRecursiveCallerOverlap {
7003                    atn_key,
7004                    state_number,
7005                    symbol,
7006                    context_version: self.rule_context_version,
7007                    overlaps,
7008                });
7009            }
7010            overlaps
7011        });
7012        if caller_overlaps {
7013            return None;
7014        }
7015        Some(true)
7016    }
7017
7018    fn cached_left_recursive_operator_lookahead(
7019        atn: &Atn,
7020        state_number: usize,
7021        precedence: i32,
7022    ) -> Rc<LeftRecursiveOperatorLookahead> {
7023        with_shared_atn_caches(atn, |cache| {
7024            let key = (state_number, precedence);
7025            if let Some(cached) = cache.left_recursive_operator_lookahead.get(&key) {
7026                return Rc::clone(cached);
7027            }
7028            let lookahead = Rc::new(left_recursive_operator_lookahead(
7029                atn,
7030                state_number,
7031                precedence,
7032            ));
7033            cache
7034                .left_recursive_operator_lookahead
7035                .insert(key, Rc::clone(&lookahead));
7036            lookahead
7037        })
7038    }
7039
7040    /// Checks whether a generated left-recursive loop can unambiguously enter
7041    /// its operator alternative from one-token lookahead.
7042    pub fn left_recursive_loop_enter_matches(
7043        &mut self,
7044        atn: &Atn,
7045        state_number: usize,
7046        precedence: i32,
7047    ) -> bool {
7048        self.left_recursive_loop_enter_prediction(atn, state_number, precedence) == Some(true)
7049    }
7050
7051    /// Implements generated `precpred(_ctx, k)` checks.
7052    pub fn precpred(&self, precedence: i32) -> bool {
7053        precedence >= self.precedence_stack.last().copied().unwrap_or_default()
7054    }
7055
7056    /// Evaluates a generated parser semantic predicate at the current input
7057    /// position.
7058    pub fn parser_semantic_predicate_matches(
7059        &mut self,
7060        predicates: &[(usize, usize, ParserPredicate)],
7061        rule_index: usize,
7062        pred_index: usize,
7063    ) -> bool {
7064        self.parser_semantic_predicate_matches_inner(predicates, rule_index, pred_index, None)
7065    }
7066
7067    /// Evaluates a generated parser semantic predicate with the current integer
7068    /// rule argument exposed as `$_p`/`$i` metadata where applicable.
7069    pub fn parser_semantic_predicate_matches_with_local(
7070        &mut self,
7071        predicates: &[(usize, usize, ParserPredicate)],
7072        rule_index: usize,
7073        pred_index: usize,
7074        local_int_arg: i32,
7075    ) -> bool {
7076        self.parser_semantic_predicate_matches_inner(
7077            predicates,
7078            rule_index,
7079            pred_index,
7080            Some((rule_index, i64::from(local_int_arg))),
7081        )
7082    }
7083
7084    fn parser_semantic_predicate_matches_inner(
7085        &mut self,
7086        predicates: &[(usize, usize, ParserPredicate)],
7087        rule_index: usize,
7088        pred_index: usize,
7089        local_int_arg: Option<(usize, i64)>,
7090    ) -> bool {
7091        let index = self.input.index();
7092        let member_values = self.int_members.clone();
7093        self.parser_predicate_matches(PredicateEval {
7094            index,
7095            rule_index,
7096            pred_index,
7097            predicates,
7098            semantics: None,
7099            context: None,
7100            local_int_arg,
7101            member_values: &member_values,
7102        })
7103    }
7104
7105    /// Evaluates a generated parser semantic predicate with access to the
7106    /// current generated rule context.
7107    pub fn parser_semantic_predicate_matches_with_context_and_local(
7108        &mut self,
7109        predicates: &[(usize, usize, ParserPredicate)],
7110        rule_index: usize,
7111        pred_index: usize,
7112        context: &ParserRuleContext,
7113        local_int_arg: i32,
7114    ) -> bool {
7115        let index = self.input.index();
7116        let member_values = self.int_members.clone();
7117        self.parser_predicate_matches(PredicateEval {
7118            index,
7119            rule_index,
7120            pred_index,
7121            predicates,
7122            semantics: None,
7123            context: Some(context),
7124            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
7125            member_values: &member_values,
7126        })
7127    }
7128
7129    /// Evaluates a generated `SemIR` parser predicate with access to the current
7130    /// generated rule context.
7131    pub fn parser_semantic_ir_predicate_matches_with_context_and_local(
7132        &mut self,
7133        semantics: &ParserSemantics,
7134        rule_index: usize,
7135        pred_index: usize,
7136        context: &ParserRuleContext,
7137        local_int_arg: i32,
7138    ) -> bool {
7139        let index = self.input.index();
7140        let member_values = self.int_members.clone();
7141        self.parser_predicate_matches(PredicateEval {
7142            index,
7143            rule_index,
7144            pred_index,
7145            predicates: &[],
7146            semantics: Some(semantics),
7147            context: Some(context),
7148            local_int_arg: Some((rule_index, i64::from(local_int_arg))),
7149            member_values: &member_values,
7150        })
7151    }
7152
7153    /// Returns a generated fail-option message for a parser semantic
7154    /// predicate coordinate.
7155    pub fn parser_semantic_predicate_failure_message(
7156        &self,
7157        rule_index: usize,
7158        pred_index: usize,
7159        predicates: &[(usize, usize, ParserPredicate)],
7160    ) -> Option<&'static str> {
7161        self.parser_predicate_failure_message(rule_index, pred_index, predicates)
7162    }
7163
7164    /// Matches any non-EOF token.
7165    pub fn match_wildcard(&mut self) -> Result<ParseTree, AntlrError> {
7166        let current = self.input.lt_id(1).ok_or_else(|| AntlrError::ParserError {
7167            line: 0,
7168            column: 0,
7169            message: "missing current token".to_owned(),
7170            offending: None,
7171        })?;
7172        if self.token_type_for_id(current) == TOKEN_EOF {
7173            return Err(AntlrError::MismatchedInput {
7174                expected: "wildcard".to_owned(),
7175                found: self.vocabulary().display_name(TOKEN_EOF),
7176            });
7177        }
7178        self.reset_generated_recovery_state();
7179        self.consume();
7180        Ok(self.terminal_tree(current))
7181    }
7182
7183    /// Generated parser synchronization hook. The current interpreter owns
7184    /// recovery; direct generated methods can call this as a no-op until the
7185    /// generated recovery strategy is expanded.
7186    #[allow(clippy::unnecessary_wraps)]
7187    pub fn sync(&mut self, state: isize) -> Result<(), AntlrError> {
7188        self.set_state(state);
7189        Ok(())
7190    }
7191
7192    /// Synchronizes a generated parser decision against the ATN lookahead set.
7193    ///
7194    /// ANTLR generated parsers call the error strategy before optional and loop
7195    /// decisions. When the current token cannot start any alternative, follow a
7196    /// nullable exit, or be deleted before a later synchronization token, the
7197    /// generated Rust method reports that decision-level mismatch instead of
7198    /// descending into a child rule that cannot start at the current token.
7199    pub fn sync_decision(
7200        &mut self,
7201        atn: &Atn,
7202        state_number: usize,
7203        _current_context_empty: bool,
7204        loop_back: bool,
7205    ) -> Result<Vec<ParseTree>, AntlrError> {
7206        self.set_state(isize::try_from(state_number).unwrap_or(isize::MAX));
7207        self.generated_sync_expected = None;
7208        let Some(state) = atn.state(state_number) else {
7209            return Ok(Vec::new());
7210        };
7211        let Some(rule_index) = state.rule_index() else {
7212            return Ok(Vec::new());
7213        };
7214        let Some(rule_stop) = atn.rule_to_stop_state().get(rule_index) else {
7215            return Ok(Vec::new());
7216        };
7217        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
7218        let symbol = self.la(1);
7219        let mut has_expected_symbols = false;
7220        let mut nullable = false;
7221        // Whether EOF is an EXPLICIT expected token of this decision (a real `EOF`
7222        // reference in the grammar, e.g. `A* EOF`), as opposed to merely the
7223        // implicit rule-follow that a nullable exit inherits (e.g. a start rule's
7224        // end). Only an explicit EOF makes a token-before-EOF genuinely extraneous
7225        // and worth deleting; an implicit-follow EOF means the loop should simply
7226        // exit and leave the token for the (absent) caller — matching ANTLR, which
7227        // exits the loop via prediction rather than consuming up to a synthetic EOF.
7228        let mut explicit_eof_expected = false;
7229        for transition in &entry.transitions {
7230            if transition.symbols.contains(symbol) {
7231                return Ok(Vec::new());
7232            }
7233            has_expected_symbols |= !transition.symbols.is_empty();
7234            nullable |= transition.nullable;
7235            explicit_eof_expected |= transition.symbols.contains(TOKEN_EOF);
7236        }
7237        // Java's DefaultErrorStrategy.sync returns as soon as nextTokens
7238        // contains EPSILON. It remembers the decision/context expected set for
7239        // a later mismatch, but must not attempt single-token deletion or
7240        // loop-back recovery first: a nullable decision leaves the current
7241        // token to its caller even when that token is not in the context-free
7242        // FOLLOW set.
7243        if nullable {
7244            // Valid exits only need a membership probe. Materialize the full
7245            // expected set below solely when a later caller mismatch may need
7246            // the combined decision/context diagnostic.
7247            if self.context_expected_contains(atn, symbol) {
7248                return Ok(Vec::new());
7249            }
7250            let mut expected = self.context_expected_token_set(atn);
7251            for transition in &entry.transitions {
7252                expected.extend_from(&transition.symbols);
7253            }
7254            self.generated_sync_expected = Some(expected);
7255            return Ok(Vec::new());
7256        }
7257        if !has_expected_symbols {
7258            return Ok(Vec::new());
7259        }
7260        let mut expected = TokenBitSet::default();
7261        for transition in &entry.transitions {
7262            expected.extend_from(&transition.symbols);
7263        }
7264        // ANTLR's `DefaultErrorStrategy.sync` recovers differently by decision kind:
7265        // a loop-BACK sync (STAR_LOOP_BACK / PLUS_LOOP_BACK — reached only after at
7266        // least one iteration) does `consumeUntil` the follow set — multi-token
7267        // deletion, one error per skipped token across iterations; a loop ENTRY
7268        // (STAR_LOOP_ENTRY) and a plain optional/block entry (BLOCK_START /
7269        // *-block / +-block starts) do `singleTokenDeletion` — delete the one
7270        // unexpected token only when LA(2) is expected, otherwise report a mismatch
7271        // and leave recovery to the rule.
7272        //
7273        // The generated loop always presents the loop-ENTRY state to this method on
7274        // every pass, so `state.kind()` cannot distinguish entry from back; the caller
7275        // passes `loop_back` (false on a `*` loop's first sync / on a block, true once
7276        // an iteration has been taken, and true on a `+` loop's first sync since its
7277        // mandatory first element is iteration 1). Treating a loop entry as a
7278        // loop-back would over-consume (e.g. `s: A* EOF;` on `c c` would delete both
7279        // `c`s, which ANTLR rejects with `mismatched input`).
7280        let loop_sync = loop_back;
7281        if symbol != TOKEN_EOF {
7282            let mut cursor = self.input.index();
7283            let mut skipped = Vec::new();
7284            loop {
7285                let current = self.token_type_at(cursor);
7286                if current == TOKEN_EOF {
7287                    break;
7288                }
7289                skipped.push(cursor);
7290                let next = self.consume_index(cursor, current);
7291                if next == cursor {
7292                    break;
7293                }
7294                let next_symbol = self.token_type_at(next);
7295                // Stop (and delete the skipped tokens as error nodes) when the next
7296                // token is a real expected continuation. EOF counts only when it is
7297                // an EXPLICIT grammar token (`A* EOF`): then the deleted tokens are
7298                // genuinely extraneous and the generated EOF match consumes the real
7299                // EOF afterwards. An implicit-follow EOF (a nullable exit's inherited
7300                // rule-follow) does NOT count — the loop must exit and leave the
7301                // token, as ANTLR does, instead of deleting up to a synthetic EOF.
7302                let next_is_expected_stop = if next_symbol == TOKEN_EOF {
7303                    explicit_eof_expected
7304                } else {
7305                    expected.contains(next_symbol)
7306                };
7307                if next_is_expected_stop {
7308                    let current_token = self.input.lt(1);
7309                    let expected_symbols = expected.to_btree_set();
7310                    let message = format!(
7311                        "extraneous input {} expecting {}",
7312                        current_token
7313                            .as_ref()
7314                            .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7315                        self.expected_symbols_display(&expected_symbols)
7316                    );
7317                    self.push_generated_parser_diagnostic(diagnostic_for_token(
7318                        current_token,
7319                        message,
7320                    ));
7321                    self.record_syntax_errors(1);
7322                    let mut children = Vec::with_capacity(skipped.len());
7323                    for index in skipped {
7324                        if let Some(token) = self.token_id_at(index) {
7325                            self.consume();
7326                            children.push(self.error_tree(token));
7327                        }
7328                    }
7329                    if !loop_sync {
7330                        self.reset_generated_recovery_state();
7331                    }
7332                    return Ok(children);
7333                }
7334                // A non-loop block entry deletes at most one token (single-token
7335                // deletion): if LA(2) is not expected, stop scanning so the mismatch
7336                // is reported at the first token instead of skipping ahead.
7337                if !loop_sync {
7338                    break;
7339                }
7340                cursor = next;
7341            }
7342        }
7343        let current = self.input.lt(1);
7344        let expected_symbols = expected.to_btree_set();
7345        Err(AntlrError::ParserError {
7346            line: current.as_ref().map(Token::line).unwrap_or_default(),
7347            column: current.as_ref().map(Token::column).unwrap_or_default(),
7348            message: format!(
7349                "mismatched input {} expecting {}",
7350                current
7351                    .as_ref()
7352                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
7353                self.expected_symbols_display(&expected_symbols)
7354            ),
7355            offending: current.as_ref().map(Token::token_id),
7356        })
7357    }
7358
7359    /// Returns a generated-parser prediction when one token of lookahead
7360    /// uniquely selects an alternative for `state_number`.
7361    ///
7362    /// This mirrors the interpreter's LL(1) commit point and lets generated
7363    /// recursive-descent methods avoid invoking the adaptive simulator for
7364    /// simple optional/block/loop decisions.
7365    pub fn ll1_decision_prediction(
7366        &mut self,
7367        atn: &Atn,
7368        state_number: usize,
7369    ) -> Option<ParserAtnPrediction> {
7370        let state = atn.state(state_number)?;
7371        if state.precedence_rule_decision() {
7372            return None;
7373        }
7374        let rule_stop = state
7375            .rule_index()
7376            .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))?;
7377        let symbol = self.la(1);
7378        let entry = self.cached_decision_lookahead(atn, state, rule_stop);
7379        ll1_greedy_alt(&entry, symbol, state.non_greedy()).map(|alt| ParserAtnPrediction {
7380            alt: alt + 1,
7381            requires_full_context: false,
7382            has_semantic_context: false,
7383            diagnostic: None,
7384        })
7385    }
7386
7387    fn context_expected_symbols(&mut self, atn: &Atn) -> BTreeSet<i32> {
7388        let mut expected = BTreeSet::new();
7389        for index in (1..self.rule_context_stack.len()).rev() {
7390            let invoking_state = self.rule_context_stack[index].invoking_state;
7391            let Ok(state_number) = usize::try_from(invoking_state) else {
7392                continue;
7393            };
7394            let Some(Transition::Rule { follow_state, .. }) = atn
7395                .state(state_number)
7396                .and_then(|state| state.transitions().first())
7397                .map(ParserTransition::data)
7398            else {
7399                continue;
7400            };
7401            let return_state = follow_state;
7402            expected.extend(self.cached_state_expected_symbols(atn, return_state).iter());
7403            if !self.cached_state_can_reach_rule_stop(atn, return_state) {
7404                return expected;
7405            }
7406        }
7407        expected.insert(TOKEN_EOF);
7408        expected
7409    }
7410
7411    fn context_expected_token_set(&mut self, atn: &Atn) -> TokenBitSet {
7412        let mut expected = TokenBitSet::default();
7413        for index in (1..self.rule_context_stack.len()).rev() {
7414            let invoking_state = self.rule_context_stack[index].invoking_state;
7415            let Ok(state_number) = usize::try_from(invoking_state) else {
7416                continue;
7417            };
7418            let Some(Transition::Rule { follow_state, .. }) = atn
7419                .state(state_number)
7420                .and_then(|state| state.transitions().first())
7421                .map(ParserTransition::data)
7422            else {
7423                continue;
7424            };
7425            expected.extend_from(&self.cached_state_expected_token_set(atn, follow_state));
7426            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
7427                return expected;
7428            }
7429        }
7430        expected.insert(TOKEN_EOF);
7431        expected
7432    }
7433
7434    /// Reports whether `symbol` is in `context_expected_token_set(atn)`
7435    /// without materializing the union.
7436    ///
7437    /// The walk follows the same rule-stack return chain as adaptive
7438    /// prediction. Valid nullable exits normally match the innermost frame,
7439    /// keeping their synchronization path to one cached membership probe.
7440    fn context_expected_contains(&mut self, atn: &Atn, symbol: i32) -> bool {
7441        for index in (1..self.rule_context_stack.len()).rev() {
7442            let invoking_state = self.rule_context_stack[index].invoking_state;
7443            let Ok(state_number) = usize::try_from(invoking_state) else {
7444                continue;
7445            };
7446            let Some(Transition::Rule { follow_state, .. }) = atn
7447                .state(state_number)
7448                .and_then(|state| state.transitions().first())
7449                .map(ParserTransition::data)
7450            else {
7451                continue;
7452            };
7453            if self
7454                .cached_state_expected_token_set(atn, follow_state)
7455                .contains(symbol)
7456            {
7457                return true;
7458            }
7459            if !self.cached_state_can_reach_rule_stop(atn, follow_state) {
7460                return false;
7461            }
7462        }
7463        symbol == TOKEN_EOF
7464    }
7465
7466    /// Builds a generated no-viable-alternative parser error.
7467    pub fn no_viable_alternative_error(&self, start_index: usize) -> AntlrError {
7468        let error_index = self.input.index();
7469        self.no_viable_alternative_error_at(start_index, error_index)
7470    }
7471
7472    /// Builds a generated no-viable-alternative parser error at the simulator's
7473    /// failing lookahead index. `adaptive_predict` restores the input cursor
7474    /// before returning, so generated parsers have to pass the recorded index
7475    /// explicitly to preserve ANTLR's LL(k) diagnostic span.
7476    pub fn no_viable_alternative_error_at(
7477        &self,
7478        start_index: usize,
7479        error_index: usize,
7480    ) -> AntlrError {
7481        let diagnostic = self.no_viable_alternative(start_index, error_index);
7482        AntlrError::ParserError {
7483            line: diagnostic.line,
7484            column: diagnostic.column,
7485            message: diagnostic.message,
7486            offending: diagnostic.offending,
7487        }
7488    }
7489
7490    /// Builds a generated failed-predicate parser error.
7491    pub fn failed_predicate_error(&self, message: impl Into<String>) -> AntlrError {
7492        let current = self.input.lt(1);
7493        AntlrError::ParserError {
7494            line: current.as_ref().map(Token::line).unwrap_or_default(),
7495            column: current.as_ref().map(Token::column).unwrap_or_default(),
7496            message: format!("rule failed predicate: {}", message.into()),
7497            offending: current.as_ref().map(Token::token_id),
7498        }
7499    }
7500
7501    /// Builds a generated parser error for a semantic predicate with ANTLR's
7502    /// `<fail='...'>` option.
7503    pub fn failed_predicate_option_error(
7504        &self,
7505        rule_index: usize,
7506        message: impl Into<String>,
7507    ) -> AntlrError {
7508        let current = self.input.lt(1);
7509        let rule_name = self
7510            .rule_names()
7511            .get(rule_index)
7512            .map_or_else(|| rule_index.to_string(), Clone::clone);
7513        AntlrError::ParserError {
7514            line: current.as_ref().map(Token::line).unwrap_or_default(),
7515            column: current.as_ref().map(Token::column).unwrap_or_default(),
7516            message: format!("rule {rule_name} {}", message.into()),
7517            offending: current.as_ref().map(Token::token_id),
7518        }
7519    }
7520
7521    /// Builds a generated parser-action event at the current input position.
7522    pub fn parser_action_at_current(
7523        &mut self,
7524        source_state: usize,
7525        rule_index: usize,
7526        start_index: usize,
7527        consumed_eof: bool,
7528    ) -> ParserAction {
7529        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
7530        ParserAction::new(source_state, rule_index, start_index, stop_index)
7531    }
7532
7533    /// Builds an indexed generated parser-action event at the current input position.
7534    pub fn parser_action_at_current_indexed(
7535        &mut self,
7536        source_state: usize,
7537        rule_index: usize,
7538        action_index: usize,
7539        start_index: usize,
7540        consumed_eof: bool,
7541    ) -> ParserAction {
7542        let stop_index = self.rule_stop_token_index(self.input.index(), consumed_eof);
7543        ParserAction::new_indexed(
7544            source_state,
7545            rule_index,
7546            action_index,
7547            start_index,
7548            stop_index,
7549        )
7550    }
7551
7552    /// Offers a committed parser action event to the user semantic hook.
7553    ///
7554    /// Generated parsers call this for action source states that were present
7555    /// in the ATN but not translated into a built-in Rust action template.
7556    pub fn parser_action_hook(&mut self, action: ParserAction, tree: ParseTree) -> bool {
7557        self.parser_action_hook_inner(action, None, Some(tree), None, true)
7558    }
7559
7560    /// Offers an action to semantic hooks at its committed grammar position.
7561    ///
7562    /// The current rule context contains children completed before the action;
7563    /// the full rule tree is not available until the rule returns.
7564    pub fn parser_action_hook_with_context(
7565        &mut self,
7566        action: ParserAction,
7567        context: &ParserRuleContext,
7568    ) -> bool {
7569        self.parser_action_hook_inner(action, Some(context), None, None, true)
7570    }
7571
7572    /// Offers an action with the current generated rule's integer argument.
7573    ///
7574    /// Generated parameterized rules use the same integer carrier as generated
7575    /// predicate evaluation. The context exposes it through
7576    /// [`ParserSemCtx::local_int_arg`].
7577    pub fn parser_action_hook_with_context_and_local(
7578        &mut self,
7579        action: ParserAction,
7580        context: &ParserRuleContext,
7581        local_int_arg: i32,
7582    ) -> bool {
7583        self.parser_action_hook_inner(
7584            action,
7585            Some(context),
7586            None,
7587            Some((action.rule_index(), i64::from(local_int_arg))),
7588            true,
7589        )
7590    }
7591
7592    /// Offers a rule-init action at rule entry while preserving legacy replay.
7593    ///
7594    /// A declined init is returned to the generated caller, so it is not an
7595    /// unhandled action yet and must not trip the fail-loud policy here.
7596    fn parser_rule_init_hook_with_context(
7597        &mut self,
7598        action: ParserAction,
7599        context: &ParserRuleContext,
7600        local_int_arg: Option<(usize, i64)>,
7601    ) -> bool {
7602        debug_assert!(action.is_rule_init());
7603        self.parser_action_hook_inner(action, Some(context), None, local_int_arg, false)
7604    }
7605
7606    fn parser_action_hook_inner(
7607        &mut self,
7608        action: ParserAction,
7609        context: Option<&ParserRuleContext>,
7610        tree: Option<ParseTree>,
7611        local_int_arg: Option<(usize, i64)>,
7612        record_unhandled: bool,
7613    ) -> bool {
7614        let rule_index = action.rule_index();
7615        let rule_name = self.rule_names().get(rule_index).cloned();
7616        let input = &mut self.input;
7617        let semantic_hooks = &mut self.semantic_hooks;
7618        let member_values = &self.int_members;
7619        let mut ctx = ParserSemCtx {
7620            input,
7621            tree_storage: &self.tree,
7622            rule_index,
7623            coordinate_index: action.action_index().unwrap_or(usize::MAX),
7624            rule_name,
7625            context,
7626            tree,
7627            local_int_arg,
7628            member_values,
7629            action: Some(action),
7630        };
7631        let handled = semantic_hooks.action(&mut ctx, action);
7632        // This action reached the hook because it had no translated arm. If no
7633        // hook handled it either (`SemanticHooks::action` returns `false`), the
7634        // committed action is silently dropped — record it so the parse entry
7635        // can fail loud under the fail-loud boundary, mirroring unknown
7636        // predicates. `assume-*` policies opt out of the fail-loud recording.
7637        if record_unhandled
7638            && !handled
7639            && matches!(self.unknown_predicate_policy, UnknownSemanticPolicy::Error)
7640        {
7641            let coordinate = (rule_index, action.source_state());
7642            if !self.unhandled_action_hits.contains(&coordinate) {
7643                self.unhandled_action_hits.push(coordinate);
7644            }
7645        }
7646        handled
7647    }
7648
7649    /// Attempts to execute a whole generated rule by committing simulator
7650    /// decisions directly. Unsupported constructs or decisions that need
7651    /// full-context / predicate evaluation restore the input cursor and fall
7652    /// back to [`Self::parse_atn_rule`].
7653    pub fn parse_atn_rule_adaptive_or_fallback<'atn>(
7654        &mut self,
7655        atn: &'atn Atn,
7656        simulator: &mut ParserAtnSimulator<'atn>,
7657        rule_index: usize,
7658    ) -> Result<ParseTree, AntlrError> {
7659        let start_index = self.current_visible_index();
7660        self.clear_prediction_diagnostics();
7661        self.reset_per_parse_caches();
7662        self.reset_recognition_arena();
7663        let tree_checkpoint = self.tree.checkpoint();
7664        let mut decision_by_state = vec![None; atn.states().len()];
7665        for (decision, state_number) in atn.decision_to_state().iter().enumerate() {
7666            if let Some(slot) = decision_by_state.get_mut(state_number) {
7667                *slot = Some(decision);
7668            }
7669        }
7670
7671        let result = DirectAdaptiveParser {
7672            parser: self,
7673            atn,
7674            simulator,
7675            decision_by_state,
7676            steps: 0,
7677        }
7678        .parse_rule(rule_index, -1, 0);
7679
7680        match result {
7681            Ok(tree) => {
7682                self.report_token_source_errors();
7683                self.release_tree_scratch_if_idle();
7684                Ok(tree)
7685            }
7686            Err(DirectAdaptiveParseControl::Fallback(reason)) => {
7687                let _ = reason;
7688                self.tree.rollback(tree_checkpoint);
7689                self.input.seek(start_index);
7690                self.parse_atn_rule(atn, rule_index)
7691            }
7692        }
7693    }
7694
7695    /// Parses a generated rule by interpreting the parser ATN from the rule's
7696    /// start state to its stop state.
7697    ///
7698    /// The recognizer backtracks across alternatives and loop exits using token
7699    /// stream indices instead of committing to input consumption immediately.
7700    /// Once a viable ATN path is found, the parser commits the accepted token
7701    /// interval and returns a rule node whose children mirror every grammar
7702    /// rule invocation reached on that path, matching ANTLR's parse-tree
7703    /// shape.
7704    pub fn parse_atn_rule(
7705        &mut self,
7706        atn: &Atn,
7707        rule_index: usize,
7708    ) -> Result<ParseTree, AntlrError> {
7709        self.parse_atn_rule_with_precedence(atn, rule_index, 0)
7710    }
7711
7712    /// Parses a generated rule by interpreting the parser ATN with an initial
7713    /// left-recursive precedence threshold.
7714    pub fn parse_atn_rule_with_precedence(
7715        &mut self,
7716        atn: &Atn,
7717        rule_index: usize,
7718        precedence: i32,
7719    ) -> Result<ParseTree, AntlrError> {
7720        self.parse_atn_rule_with_precedence_inner(
7721            atn,
7722            rule_index,
7723            precedence,
7724            None,
7725            AltNumberTracking::default(),
7726        )
7727    }
7728
7729    fn parse_atn_rule_with_precedence_inner(
7730        &mut self,
7731        atn: &Atn,
7732        rule_index: usize,
7733        precedence: i32,
7734        predicate_context: Option<FastPredicateContext<'_>>,
7735        alt_tracking: AltNumberTracking,
7736    ) -> Result<ParseTree, AntlrError> {
7737        let report_unrecovered_error = self.is_top_level_entry();
7738        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
7739            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
7740        })?;
7741        let stop_state = atn
7742            .rule_to_stop_state()
7743            .get(rule_index)
7744            .filter(|state| *state != usize::MAX)
7745            .ok_or_else(|| {
7746                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
7747            })?;
7748
7749        let start_index = self.current_visible_index();
7750        self.clear_prediction_diagnostics();
7751        self.reset_per_parse_caches();
7752        self.reset_recognition_arena();
7753        let caller_follow_state = self.pending_invoking_follow_state(atn);
7754        self.fast_recovery_enabled = false;
7755        self.fast_token_nodes_enabled = false;
7756        self.fast_track_alt_numbers = alt_tracking.any();
7757        let top_request = FastRecognizeTopRequest {
7758            start_state,
7759            stop_state,
7760            start_index,
7761            precedence,
7762            caller_follow_state,
7763        };
7764        let first_pass = self.fast_recognize_top(atn, top_request, predicate_context);
7765        self.fast_token_nodes_enabled = self.build_parse_trees;
7766        let needs_tree_retry = matches!(
7767            &first_pass,
7768            Ok((outcome, _, _))
7769                if self.build_parse_trees
7770                    && self
7771                        .recognition_arena
7772                        .sequence_has_left_recursive_boundary(outcome.nodes)
7773        );
7774        let needs_retry = match &first_pass {
7775            // The FIRST-set prefilter trims speculative rule calls that can't
7776            // match the current lookahead — useful for perf on grammars with
7777            // many epsilon-reachable rules, but the trim also bypasses
7778            // single-token insertion / deletion recovery that ANTLR's
7779            // reference parser runs at the child rule's first consuming
7780            // transition. Retry without the prefilter whenever the first pass
7781            // either produced no outcome at all or produced a recovered
7782            // outcome (diagnostics non-empty), since the second pass might
7783            // surface a child-level recovery with cleaner diagnostics or
7784            // closer parity to ANTLR's tree shape. Left-recursive tree
7785            // boundaries also need the token-node pass; otherwise the fold has
7786            // no concrete left operand to wrap into ANTLR's recursive context.
7787            Err(_) => true,
7788            Ok((outcome, _, _)) => !outcome.diagnostics.is_empty() || needs_tree_retry,
7789        };
7790        let (outcome, _expected, alt_number) = if needs_retry {
7791            self.fast_first_set_prefilter = false;
7792            self.fast_recovery_enabled = false;
7793            let clean_retry = self.fast_recognize_top(atn, top_request, predicate_context);
7794            let clean_selected = if needs_tree_retry {
7795                match clean_retry {
7796                    ok @ Ok(_) => ok,
7797                    Err(_) => first_pass,
7798                }
7799            } else {
7800                select_better_top_outcome(first_pass, clean_retry, &self.recognition_arena)
7801            };
7802            let selected = if clean_selected.is_err()
7803                || matches!(&clean_selected, Ok((outcome, _, _)) if !outcome.diagnostics.is_empty())
7804            {
7805                self.fast_recovery_enabled = true;
7806                let recovery_retry = self.fast_recognize_top(atn, top_request, predicate_context);
7807                select_better_top_outcome(clean_selected, recovery_retry, &self.recognition_arena)
7808            } else {
7809                clean_selected
7810            };
7811            self.fast_first_set_prefilter = true;
7812            self.fast_recovery_enabled = true;
7813            selected.map_err(|expected| {
7814                if predicate_context.is_some()
7815                    && let Some(error) = self.unknown_semantic_error()
7816                {
7817                    self.report_token_source_errors();
7818                    return error;
7819                }
7820                let error = self.recognition_error(rule_index, start_index, &expected);
7821                self.record_syntax_errors(1);
7822                self.report_token_source_errors();
7823                if report_unrecovered_error {
7824                    self.report_unrecovered_parser_error(&error);
7825                }
7826                error
7827            })?
7828        } else {
7829            first_pass.expect("first_pass is Ok in the no-retry branch")
7830        };
7831        if predicate_context.is_some()
7832            && let Some(error) = self.unknown_semantic_error()
7833        {
7834            self.report_token_source_errors();
7835            return Err(error);
7836        }
7837        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
7838        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
7839        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
7840        self.report_token_source_errors();
7841        let mut context = ParserRuleContext::with_child_capacity(
7842            rule_index,
7843            self.state(),
7844            if self.build_parse_trees {
7845                self.recognition_arena.sequence_len(outcome.nodes)
7846            } else {
7847                0
7848            },
7849        );
7850        if alt_tracking.public {
7851            context.set_alt_number(alt_number.max(1));
7852        }
7853        if alt_tracking.context {
7854            context.set_context_alt_number(alt_number);
7855        }
7856        if let Some(token) = self.token_id_at(start_index) {
7857            self.set_context_start(&mut context, token);
7858        }
7859        let stop_index = self.rule_stop_token_index(outcome.index, outcome.consumed_eof);
7860        if let Some(token) = stop_index.and_then(|token_index| self.token_id_at(token_index)) {
7861            self.set_context_stop(&mut context, token);
7862        }
7863        let live_root = if self.build_parse_trees {
7864            self.recognition_arena
7865                .fold_left_recursive_boundaries(outcome.nodes)
7866        } else {
7867            outcome.nodes
7868        };
7869        if self.build_parse_trees {
7870            if self
7871                .recognition_arena
7872                .sequence_has_explicit_token(live_root)
7873            {
7874                let mut cursor = live_root;
7875                while let Some(link) = self.recognition_arena.link(cursor) {
7876                    let child = self.arena_recognized_node_tree(
7877                        link.head,
7878                        alt_tracking.public,
7879                        alt_tracking.context,
7880                    )?;
7881                    self.tree.add_child(&mut context, child);
7882                    cursor = link.tail;
7883                }
7884            } else {
7885                self.add_arena_implicit_token_children(
7886                    &mut context,
7887                    start_index,
7888                    stop_index,
7889                    live_root,
7890                    alt_tracking,
7891                )?;
7892            }
7893        }
7894        self.finish_recognition_arena(live_root, outcome.diagnostics);
7895        self.input.seek(outcome.index);
7896
7897        let tree = self.rule_node(context);
7898        self.release_tree_scratch_if_idle();
7899        Ok(tree)
7900    }
7901
7902    fn pending_invoking_follow_state(&self, atn: &Atn) -> Option<usize> {
7903        let invoking_state = self.pending_invoking_states.last().copied()?;
7904        let state_number = usize::try_from(invoking_state).ok()?;
7905        match atn.state(state_number)?.transitions().first()?.data() {
7906            Transition::Rule { follow_state, .. } => Some(follow_state),
7907            _ => None,
7908        }
7909    }
7910
7911    #[cfg(test)]
7912    fn caller_follow_token_info(&mut self, index: usize) -> (i32, bool, bool) {
7913        caller_follow_token_info_for_stream(&mut self.input, index)
7914    }
7915
7916    /// Runs the fast recognizer once from the rule's start state and returns
7917    /// the best outcome or the per-attempt expected-token accumulator. The
7918    /// caller flips `fast_first_set_prefilter` between calls when a retry is
7919    /// needed, so the FIRST-set cache is left intact across both passes.
7920    fn fast_recognize_top(
7921        &mut self,
7922        atn: &Atn,
7923        request: FastRecognizeTopRequest,
7924        predicate_context: Option<FastPredicateContext<'_>>,
7925    ) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
7926        let FastRecognizeTopRequest {
7927            start_state,
7928            stop_state,
7929            start_index,
7930            precedence,
7931            caller_follow_state,
7932        } = request;
7933        // `input.size()` is intentionally only the currently buffered token
7934        // count here. Do not restore an up-front fill just to size this map:
7935        // a small floor avoids tiny-input churn, and larger inputs reserve from
7936        // the buffered token count without forcing startup tokenization. The
7937        // 8x multiplier matches the empirical
7938        // memo-insert / token ratio on heavy grammars (C# averages ~6× and
7939        // Kotlin ~12× memo entries per token), so the table avoids one
7940        // rehash on the typical hot path.
7941        let memo_capacity = fast_recognize_memo_capacity(self.input.size());
7942        let mut recognize_scratch = std::mem::take(&mut self.fast_recognize_scratch);
7943        recognize_scratch.prepare(memo_capacity);
7944        let mut expected = ExpectedTokens::default();
7945        let empty_recovery = self.empty_recovery_symbols();
7946        let outcomes = self.recognize_state_fast(
7947            atn,
7948            FastRecognizeRequest {
7949                state_number: start_state,
7950                stop_state,
7951                index: start_index,
7952                rule_start_index: start_index,
7953                decision_start_index: None,
7954                precedence,
7955                depth: 0,
7956                recovery_symbols: empty_recovery,
7957                recovery_state: None,
7958            },
7959            FastRecognizeScratch {
7960                predicate_context,
7961                visiting: &mut recognize_scratch.visiting,
7962                memo: &mut recognize_scratch.memo,
7963                expected: &mut expected,
7964                native_depth: 0,
7965            },
7966        );
7967        recognize_scratch.release_oversized_memo();
7968        self.fast_recognize_scratch = recognize_scratch;
7969        #[cfg(feature = "perf-counters")]
7970        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
7971            perf_counters::dump();
7972            perf_counters::reset();
7973        }
7974        let caller_follow =
7975            caller_follow_state.map(|state| self.cached_state_expected_token_set(atn, state));
7976        let selected = {
7977            let arena = &self.recognition_arena;
7978            let input = &mut self.input;
7979            select_best_fast_outcome(
7980                outcomes.into_iter(),
7981                self.prediction_mode,
7982                caller_follow.as_deref(),
7983                |index| caller_follow_token_info_for_stream(input, index),
7984                arena,
7985            )
7986        };
7987        match selected {
7988            Some(mut outcome) => {
7989                let alt_number = if self.build_parse_trees || self.fast_track_alt_numbers {
7990                    self.materialize_fast_outcome_nodes(&mut outcome)
7991                } else {
7992                    0
7993                };
7994                Ok((outcome, expected, alt_number))
7995            }
7996            None => Err(expected),
7997        }
7998    }
7999
8000    /// Converts one speculative arena record into the flat public CST.
8001    fn arena_recognized_node_tree(
8002        &mut self,
8003        node_id: RecognizedNodeId,
8004        track_alt_numbers: bool,
8005        track_context_alt_numbers: bool,
8006    ) -> Result<ParseTree, AntlrError> {
8007        let node = self.recognition_arena.node(node_id);
8008        match node {
8009            ArenaRecognizedNode::Token { token } => Ok(self.terminal_tree(token)),
8010            ArenaRecognizedNode::ErrorToken { token } => Ok(self.error_tree(token)),
8011            ArenaRecognizedNode::MissingToken { extra } => {
8012                let (token_type, at_index, text) = match self.recognition_arena.extra(extra) {
8013                    RecognitionExtra::MissingToken {
8014                        token_type,
8015                        at_index,
8016                        text,
8017                    } => (*token_type, *at_index as usize, text.clone()),
8018                    RecognitionExtra::ReturnValues(_) | RecognitionExtra::Diagnostic(_) => {
8019                        unreachable!("missing-token node must reference missing-token extra")
8020                    }
8021                };
8022                let (line, column) = self
8023                    .token_at(at_index)
8024                    .map_or((0, 0), |token| (token.line(), token.column()));
8025                let token = self.insert_synthetic_token(token_type, text, line, column)?;
8026                Ok(self.error_tree(token))
8027            }
8028            ArenaRecognizedNode::Rule {
8029                rule_index,
8030                invoking_state,
8031                alt_number,
8032                start_index,
8033                stop_index,
8034                return_values,
8035                children,
8036            } => {
8037                let mut context = ParserRuleContext::with_child_capacity(
8038                    rule_index as usize,
8039                    invoking_state as isize,
8040                    self.recognition_arena.sequence_len(children),
8041                );
8042                if track_alt_numbers {
8043                    context.set_alt_number((alt_number as usize).max(1));
8044                }
8045                if track_context_alt_numbers {
8046                    context.set_context_alt_number(alt_number as usize);
8047                }
8048                if let Some(extra) = return_values {
8049                    let RecognitionExtra::ReturnValues(values) =
8050                        self.recognition_arena.extra(extra)
8051                    else {
8052                        unreachable!("rule node must reference return-values extra");
8053                    };
8054                    for (name, value) in values {
8055                        context.set_int_return(name.clone(), *value);
8056                    }
8057                }
8058                if let Some(token) = self.token_id_at(start_index as usize) {
8059                    self.set_context_start(&mut context, token);
8060                }
8061                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
8062                    self.set_context_stop(&mut context, token);
8063                }
8064                let mut cursor = self
8065                    .recognition_arena
8066                    .fold_left_recursive_boundaries(children);
8067                while let Some(link) = self.recognition_arena.link(cursor) {
8068                    let child = self.arena_recognized_node_tree(
8069                        link.head,
8070                        track_alt_numbers,
8071                        track_context_alt_numbers,
8072                    )?;
8073                    self.tree.add_child(&mut context, child);
8074                    cursor = link.tail;
8075                }
8076                Ok(self.rule_node(context))
8077            }
8078            ArenaRecognizedNode::LeftRecursiveBoundary { rule_index, .. } => {
8079                Err(AntlrError::Unsupported(format!(
8080                    "unfolded left-recursive boundary for rule {rule_index}"
8081                )))
8082            }
8083        }
8084    }
8085
8086    fn arena_recognized_node_tree_with_implicit_tokens(
8087        &mut self,
8088        node_id: RecognizedNodeId,
8089        alt_tracking: AltNumberTracking,
8090    ) -> Result<ParseTree, AntlrError> {
8091        let node = self.recognition_arena.node(node_id);
8092        match node {
8093            ArenaRecognizedNode::Rule {
8094                rule_index,
8095                invoking_state,
8096                alt_number,
8097                start_index,
8098                stop_index,
8099                children,
8100                ..
8101            } => {
8102                let mut context = ParserRuleContext::with_child_capacity(
8103                    rule_index as usize,
8104                    invoking_state as isize,
8105                    self.recognition_arena.sequence_len(children),
8106                );
8107                if alt_tracking.public {
8108                    context.set_alt_number((alt_number as usize).max(1));
8109                }
8110                if alt_tracking.context {
8111                    context.set_context_alt_number(alt_number as usize);
8112                }
8113                if let Some(token) = self.token_id_at(start_index as usize) {
8114                    self.set_context_start(&mut context, token);
8115                }
8116                if let Some(token) = stop_index.and_then(|index| self.token_id_at(index as usize)) {
8117                    self.set_context_stop(&mut context, token);
8118                }
8119                let children = self
8120                    .recognition_arena
8121                    .fold_left_recursive_boundaries(children);
8122                self.add_arena_implicit_token_children(
8123                    &mut context,
8124                    start_index as usize,
8125                    stop_index.map(|index| index as usize),
8126                    children,
8127                    alt_tracking,
8128                )?;
8129                Ok(self.rule_node(context))
8130            }
8131            _ => {
8132                self.arena_recognized_node_tree(node_id, alt_tracking.public, alt_tracking.context)
8133            }
8134        }
8135    }
8136
8137    fn add_arena_implicit_token_children(
8138        &mut self,
8139        context: &mut ParserRuleContext,
8140        start_index: usize,
8141        stop_index: Option<usize>,
8142        mut children: NodeSeqId,
8143        alt_tracking: AltNumberTracking,
8144    ) -> Result<(), AntlrError> {
8145        let mut cursor = Some(start_index);
8146        while let Some(link) = self.recognition_arena.link(children) {
8147            if let Some((child_start, child_stop)) = self.recognition_arena.node_span(link.head) {
8148                self.add_visible_terminals_before(context, &mut cursor, child_start)?;
8149                let child =
8150                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
8151                self.tree.add_child(context, child);
8152                if let Some(child_stop) = child_stop {
8153                    let next = self.next_visible_after_token(child_stop);
8154                    cursor = match (cursor, next) {
8155                        (None, _) | (_, None) => None,
8156                        (Some(current), Some(next)) => Some(current.max(next)),
8157                    };
8158                }
8159            } else {
8160                let child =
8161                    self.arena_recognized_node_tree_with_implicit_tokens(link.head, alt_tracking)?;
8162                self.tree.add_child(context, child);
8163            }
8164            children = link.tail;
8165        }
8166        if let Some(stop) = stop_index {
8167            self.add_visible_terminals_through(context, cursor, stop)?;
8168        }
8169        Ok(())
8170    }
8171
8172    fn add_visible_terminals_before(
8173        &mut self,
8174        context: &mut ParserRuleContext,
8175        cursor: &mut Option<usize>,
8176        before: usize,
8177    ) -> Result<(), AntlrError> {
8178        let Some(stop) = before.checked_sub(1) else {
8179            return Ok(());
8180        };
8181        let next = self.add_visible_terminals_through(context, *cursor, stop)?;
8182        *cursor = next;
8183        Ok(())
8184    }
8185
8186    fn add_visible_terminals_through(
8187        &mut self,
8188        context: &mut ParserRuleContext,
8189        mut cursor: Option<usize>,
8190        stop: usize,
8191    ) -> Result<Option<usize>, AntlrError> {
8192        while let Some(index) = cursor {
8193            if index > stop {
8194                return Ok(Some(index));
8195            }
8196            let token = self
8197                .input
8198                .get_id(index)
8199                .ok_or_else(|| AntlrError::ParserError {
8200                    line: 0,
8201                    column: 0,
8202                    message: format!("missing token at index {index}"),
8203                    offending: None,
8204                })?;
8205            let is_eof = self.token_type_for_id(token) == TOKEN_EOF;
8206            let child = self.terminal_tree(token);
8207            self.tree.add_child(context, child);
8208            if is_eof {
8209                return Ok(None);
8210            }
8211            cursor = self.next_visible_after_token(index);
8212        }
8213        Ok(None)
8214    }
8215
8216    fn next_visible_after_token(&mut self, index: usize) -> Option<usize> {
8217        let next = self.input.next_visible_after(index);
8218        (next != index).then_some(next)
8219    }
8220
8221    /// Parses a generated rule and returns semantic actions reached on the
8222    /// selected ATN path.
8223    ///
8224    /// This slower path preserves action ordering and token intervals for
8225    /// generated code that replays target-specific action templates after the
8226    /// recognizer has chosen one viable parse path.
8227    pub fn parse_atn_rule_with_actions(
8228        &mut self,
8229        atn: &Atn,
8230        rule_index: usize,
8231    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
8232        self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
8233    }
8234
8235    /// Parses a generated rule and emits ATN actions plus selected rule-init
8236    /// actions reached on the chosen path.
8237    ///
8238    /// Generated parsers use this when a grammar contains rule-level `@init`
8239    /// templates that must run for nested rule invocations. The runtime keeps
8240    /// the action list path-sensitive, so init templates are replayed only for
8241    /// rules that were actually entered by the selected parse.
8242    pub fn parse_atn_rule_with_action_inits(
8243        &mut self,
8244        atn: &Atn,
8245        rule_index: usize,
8246        init_action_rules: &[usize],
8247    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
8248        self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
8249    }
8250
8251    /// Parses a generated rule with optional semantic-action replay features.
8252    ///
8253    /// `track_alt_numbers` is used by grammars that opt into ANTLR's
8254    /// alt-numbered context behavior. It keeps ordinary parse-tree rendering
8255    /// unchanged for grammars that do not request that target template.
8256    pub fn parse_atn_rule_with_action_options(
8257        &mut self,
8258        atn: &Atn,
8259        rule_index: usize,
8260        init_action_rules: &[usize],
8261        track_alt_numbers: bool,
8262    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
8263        self.parse_atn_rule_with_runtime_options(
8264            atn,
8265            rule_index,
8266            ParserRuntimeOptions {
8267                init_action_rules,
8268                track_alt_numbers,
8269                ..ParserRuntimeOptions::default()
8270            },
8271        )
8272    }
8273
8274    /// Parses a generated rule with action replay and parser predicate support.
8275    ///
8276    /// `predicates` maps serialized `(rule_index, pred_index)` coordinates to
8277    /// target-template predicate semantics emitted by the generator. Missing
8278    /// entries are treated as true so unsupported predicate-free grammars keep
8279    /// the previous unconditional transition behavior.
8280    pub fn parse_atn_rule_with_runtime_options(
8281        &mut self,
8282        atn: &Atn,
8283        rule_index: usize,
8284        options: ParserRuntimeOptions<'_>,
8285    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
8286        self.parse_atn_rule_with_runtime_options_and_precedence(atn, rule_index, 0, options)
8287    }
8288
8289    fn parse_atn_rule_committed_with_runtime_options(
8290        &mut self,
8291        atn: &Atn,
8292        rule_index: usize,
8293        precedence: i32,
8294        options: ParserRuntimeOptions<'_>,
8295    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
8296        let top_level_entry = self.is_top_level_entry();
8297        self.unknown_predicate_policy = options.unknown_predicate_policy;
8298        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
8299        let prior_unhandled_action_hits = std::mem::take(&mut self.unhandled_action_hits);
8300        self.clear_prediction_diagnostics();
8301        self.reset_per_parse_caches();
8302        self.reset_recognition_arena();
8303
8304        let mut decision_by_state = vec![None; atn.states().len()];
8305        for (decision, state_number) in atn.decision_to_state().iter().enumerate() {
8306            if let Some(slot) = decision_by_state.get_mut(state_number) {
8307                *slot = Some(decision);
8308            }
8309        }
8310        let mut action_index_by_state = FxHashMap::default();
8311        for &(state, index) in options.action_indices {
8312            action_index_by_state.entry(state).or_insert(index);
8313        }
8314        let mut simulator = ParserAtnSimulator::new(atn);
8315        simulator.set_track_prediction_rule_calls(!options.rule_args.is_empty());
8316        let (result, deferred_actions) = {
8317            let mut committed = CommittedAtnParser {
8318                parser: self,
8319                atn,
8320                simulator,
8321                options,
8322                decision_by_state,
8323                action_index_by_state,
8324                deferred_actions: Vec::new(),
8325            };
8326            let result = committed.parse_rule(rule_index, precedence, None, None);
8327            (result, committed.deferred_actions)
8328        };
8329
8330        if top_level_entry {
8331            self.report_generated_parser_diagnostics();
8332        }
8333        let semantic_error = self.unknown_semantic_error();
8334        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
8335        self.restore_prior_unhandled_action_hits(prior_unhandled_action_hits);
8336        if top_level_entry && let Some(error) = self.take_parse_abort() {
8337            self.reset_unknown_semantic_hits();
8338            return Err(error);
8339        }
8340        if let Some(error) = semantic_error {
8341            if top_level_entry {
8342                self.reset_unknown_semantic_hits();
8343            }
8344            return Err(error);
8345        }
8346        let result = result.map(|outcome| (outcome.tree, deferred_actions));
8347        if top_level_entry && let Err(error) = &result {
8348            self.report_unrecovered_parser_error(error);
8349        }
8350        result
8351    }
8352
8353    /// Parses a generated rule with action replay, parser predicate support,
8354    /// and an initial left-recursive precedence threshold.
8355    pub fn parse_atn_rule_with_runtime_options_and_precedence(
8356        &mut self,
8357        atn: &Atn,
8358        rule_index: usize,
8359        precedence: i32,
8360        options: ParserRuntimeOptions<'_>,
8361    ) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
8362        if !options.action_indices.is_empty() {
8363            return self.parse_atn_rule_committed_with_runtime_options(
8364                atn, rule_index, precedence, options,
8365            );
8366        }
8367        let report_unrecovered_error = self.is_top_level_entry();
8368        let ParserRuntimeOptions {
8369            init_action_rules,
8370            track_alt_numbers,
8371            track_context_alt_numbers,
8372            predicates,
8373            semantics,
8374            rule_args,
8375            member_actions,
8376            return_actions,
8377            unknown_predicate_policy,
8378            ..
8379        } = options;
8380        let capture_alt_numbers = track_alt_numbers || track_context_alt_numbers;
8381        if init_action_rules.is_empty()
8382            && !capture_alt_numbers
8383            && predicates.is_empty()
8384            && semantics.is_none()
8385            && rule_args.is_empty()
8386            && member_actions.is_empty()
8387            && return_actions.is_empty()
8388            && unknown_predicate_policy == UnknownSemanticPolicy::AssumeTrue
8389            && !atn_has_observable_action_transitions(atn)
8390            && !self.semantic_hooks.observes_parser_decisions()
8391            && (!self.semantic_hooks.observes_parser_predicates()
8392                || !atn_has_predicate_transitions(atn))
8393        {
8394            return self
8395                .parse_atn_rule_with_precedence(atn, rule_index, precedence)
8396                .map(|tree| (tree, Vec::new()));
8397        }
8398        if !self.semantic_hooks.observes_parser_decisions()
8399            && can_use_fast_predicate_recognizer(atn, &options)
8400        {
8401            self.unknown_predicate_policy = unknown_predicate_policy;
8402            let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
8403            let member_values = self.int_members.clone();
8404            let result = self
8405                .parse_atn_rule_with_precedence_inner(
8406                    atn,
8407                    rule_index,
8408                    precedence,
8409                    Some(FastPredicateContext {
8410                        predicates,
8411                        semantics,
8412                        member_values: &member_values,
8413                    }),
8414                    AltNumberTracking {
8415                        public: track_alt_numbers,
8416                        context: track_context_alt_numbers,
8417                    },
8418                )
8419                .map(|tree| (tree, Vec::new()));
8420            if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
8421                self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
8422            }
8423            return result;
8424        }
8425        self.unknown_predicate_policy = unknown_predicate_policy;
8426        // A generated parent may have already recorded unknown-predicate
8427        // coordinates before descending into this (interpreted) child. Clearing
8428        // unconditionally would drop them before the parent's public entry
8429        // surfaces them, so stash and restore around this call: recognition sees
8430        // only the hits it records itself (so the fail-loud check below reflects
8431        // this rule), and the parent's prior hits are merged back afterward.
8432        let prior_unknown_predicate_hits = std::mem::take(&mut self.unknown_predicate_hits);
8433        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
8434            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
8435        })?;
8436        let stop_state = atn
8437            .rule_to_stop_state()
8438            .get(rule_index)
8439            .filter(|state| *state != usize::MAX)
8440            .ok_or_else(|| {
8441                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
8442            })?;
8443
8444        let start_index = self.current_visible_index();
8445        self.clear_prediction_diagnostics();
8446        self.reset_per_parse_caches();
8447        self.reset_recognition_arena();
8448        let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
8449        let invoking_state = self.pending_invoking_states.pop();
8450        let local_int_arg = invoking_state
8451            .and_then(|state| usize::try_from(state).ok())
8452            .and_then(|state| rule_local_int_arg(rule_args, state, rule_index, None));
8453        let mut visiting = BTreeSet::new();
8454        let mut memo = BTreeMap::new();
8455        let mut expected = ExpectedTokens::default();
8456        let member_values = self.int_members.clone();
8457        let return_values = BTreeMap::new();
8458        let outcomes = self.recognize_state(
8459            atn,
8460            RecognizeRequest {
8461                state_number: start_state,
8462                stop_state,
8463                index: start_index,
8464                rule_start_index: start_index,
8465                decision_start_index: None,
8466                init_action_rules: &init_action_rules,
8467                predicates,
8468                semantics,
8469                rule_args,
8470                member_actions,
8471                return_actions,
8472                local_int_arg,
8473                member_values,
8474                return_values,
8475                rule_alt_number: 0,
8476                track_alt_numbers: capture_alt_numbers,
8477                consumed_eof: false,
8478                committed_decision: false,
8479                precedence,
8480                depth: 0,
8481                recovery_symbols: BTreeSet::new(),
8482                recovery_state: None,
8483            },
8484            &mut visiting,
8485            &mut memo,
8486            &mut expected,
8487        );
8488        if let Some(error) = self.unknown_semantic_error() {
8489            self.report_token_source_errors();
8490            // Keep the recorded coordinates: when this interpreted rule is a
8491            // child of a generated parent, the parent's catch block recovers an
8492            // ordinary `AntlrError` into a partial subtree, so the fail-loud
8493            // coordinate must survive on the parser for the top-level entry's
8494            // `take_unknown_semantic_error` to surface it. Cross-parse staleness
8495            // is handled by clearing at the top-level generated entry instead.
8496            return Err(error);
8497        }
8498        // Recognition recorded no unresolved coordinate of its own; merge the
8499        // parent's prior hits back so its public entry can still surface them.
8500        self.restore_prior_unknown_predicate_hits(prior_unknown_predicate_hits);
8501        let Some(outcome) = select_best_outcome(
8502            outcomes.into_iter(),
8503            self.prediction_mode,
8504            &self.recognition_arena,
8505        ) else {
8506            let error = self.recognition_error(rule_index, start_index, &expected);
8507            self.record_syntax_errors(1);
8508            self.report_token_source_errors();
8509            if report_unrecovered_error {
8510                self.report_unrecovered_parser_error(&error);
8511            }
8512            return Err(error);
8513        };
8514
8515        self.record_syntax_errors(self.recognition_arena.diagnostics_len(outcome.diagnostics));
8516        self.dispatch_parser_diagnostics(&self.prediction_diagnostics);
8517        self.dispatch_parser_diagnostics(self.recognition_arena.diagnostics(outcome.diagnostics));
8518        self.report_token_source_errors();
8519        let mut actions = outcome.actions;
8520        if init_action_rules.contains(&rule_index) {
8521            actions.insert(
8522                0,
8523                ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
8524            );
8525        }
8526        let mut context =
8527            ParserRuleContext::new(rule_index, invoking_state.unwrap_or_else(|| self.state()));
8528        if track_alt_numbers {
8529            context.set_alt_number(outcome.alt_number.max(1));
8530        }
8531        if track_context_alt_numbers {
8532            context.set_context_alt_number(outcome.alt_number);
8533        }
8534        for (name, value) in outcome.return_values {
8535            context.set_int_return(name, value);
8536        }
8537        if let Some(token) = self.token_id_at(start_index) {
8538            self.set_context_start(&mut context, token);
8539        }
8540        if let Some(token) = self.rule_stop_token_id(outcome.index, outcome.consumed_eof) {
8541            self.set_context_stop(&mut context, token);
8542        }
8543        let live_root = if self.build_parse_trees {
8544            self.recognition_arena
8545                .fold_left_recursive_boundaries(outcome.nodes)
8546        } else {
8547            outcome.nodes
8548        };
8549        if self.build_parse_trees {
8550            let mut nodes = live_root;
8551            while let Some(link) = self.recognition_arena.link(nodes) {
8552                let child = self.arena_recognized_node_tree(
8553                    link.head,
8554                    track_alt_numbers,
8555                    track_context_alt_numbers,
8556                )?;
8557                self.tree.add_child(&mut context, child);
8558                nodes = link.tail;
8559            }
8560        }
8561        self.finish_recognition_arena(live_root, outcome.diagnostics);
8562        self.input.seek(outcome.index);
8563
8564        let tree = self.rule_node(context);
8565        self.release_tree_scratch_if_idle();
8566        Ok((tree, actions))
8567    }
8568
8569    /// Temporary parser entry used by generated parser methods while the parser
8570    /// ATN simulator is being implemented.
8571    ///
8572    /// This keeps generated parser crates buildable and gives us a stable method
8573    /// surface for every grammar rule. It intentionally accepts all remaining
8574    /// tokens into one rule context; it is not the final parser semantics.
8575    pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
8576        let mut context = ParserRuleContext::new(rule_index, self.state());
8577        while self.la(1) != TOKEN_EOF {
8578            let token_type = self.la(1);
8579            let child = self.match_token(token_type)?;
8580            if self.build_parse_trees {
8581                self.tree.add_child(&mut context, child);
8582            }
8583        }
8584        if self.build_parse_trees {
8585            let child = self.match_eof()?;
8586            self.tree.add_child(&mut context, child);
8587        }
8588        let tree = self.rule_node(context);
8589        self.release_tree_scratch_if_idle();
8590        Ok(tree)
8591    }
8592
8593    /// Builds the parser error reported when no ATN path can reach the active
8594    /// rule stop state.
8595    fn recognition_error(
8596        &mut self,
8597        rule_index: usize,
8598        start_index: usize,
8599        expected: &ExpectedTokens,
8600    ) -> AntlrError {
8601        let (index, message) = self.expected_error_message(rule_index, start_index, expected);
8602        self.input.seek(index);
8603        let current = self.input.lt(1);
8604        let line = current.as_ref().map(Token::line).unwrap_or_default();
8605        let column = current.as_ref().map(Token::column).unwrap_or_default();
8606        AntlrError::ParserError {
8607            line,
8608            column,
8609            message,
8610            offending: current.as_ref().map(Token::token_id),
8611        }
8612    }
8613
8614    /// Builds the token index and ANTLR-compatible message for a failed rule.
8615    fn expected_error_message(
8616        &mut self,
8617        rule_index: usize,
8618        start_index: usize,
8619        expected: &ExpectedTokens,
8620    ) -> (usize, String) {
8621        let index = expected
8622            .index
8623            .or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
8624            .unwrap_or_else(|| self.input.index());
8625        self.input.seek(index);
8626        let current = self.input.lt(1);
8627        let message = if expected
8628            .no_viable
8629            .as_ref()
8630            .is_some_and(|no_viable| no_viable.error_index == index)
8631        {
8632            let start = expected
8633                .no_viable
8634                .as_ref()
8635                .map_or(start_index, |no_viable| no_viable.start_index);
8636            let text = display_input_text(&self.input.text(start, index));
8637            format!("no viable alternative at input '{text}'")
8638        } else if expected.symbols.is_empty() {
8639            if expected.index.is_some() {
8640                let found = current
8641                    .as_ref()
8642                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
8643                if current
8644                    .as_ref()
8645                    .is_some_and(|token| token.token_type() == TOKEN_EOF)
8646                {
8647                    format!(
8648                        "missing {} at {found}",
8649                        self.expected_symbols_display(&expected.symbols)
8650                    )
8651                } else {
8652                    format!("mismatched input {found}")
8653                }
8654            } else {
8655                format!("no viable alternative while parsing rule {rule_index}")
8656            }
8657        } else {
8658            format!(
8659                "mismatched input {} expecting {}",
8660                current
8661                    .as_ref()
8662                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8663                self.expected_symbols_display(&expected.symbols)
8664            )
8665        };
8666        (index, message)
8667    }
8668
8669    /// Converts a failed child rule into a recovered outcome so the parent can
8670    /// continue after reporting the child diagnostic.
8671    fn child_rule_failure_recovery(
8672        &mut self,
8673        rule_index: usize,
8674        start_index: usize,
8675        sync_symbols: &BTreeSet<i32>,
8676        member_values: MemberEnv,
8677        expected: &ExpectedTokens,
8678    ) -> Option<RecognizeOutcome> {
8679        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
8680        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
8681        let mut next_index = error_index;
8682        loop {
8683            let symbol = self.token_type_at(next_index);
8684            if sync_symbols.contains(&symbol) {
8685                if next_index == error_index {
8686                    return None;
8687                }
8688                break;
8689            }
8690            if symbol == TOKEN_EOF {
8691                break;
8692            }
8693            let after = self.consume_index(next_index, symbol);
8694            if after == next_index {
8695                break;
8696            }
8697            next_index = after;
8698        }
8699        let mut nodes = NodeSeqId::EMPTY;
8700        let error = self.arena_token_node(error_index, true);
8701        self.arena_prepend(&mut nodes, error);
8702        let diagnostics = self
8703            .recognition_arena
8704            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
8705        Some(RecognizeOutcome {
8706            index: next_index,
8707            consumed_eof: false,
8708            alt_number: 0,
8709            member_values,
8710            return_values: BTreeMap::new(),
8711            diagnostics,
8712            decisions: Vec::new(),
8713            actions: Vec::new(),
8714            nodes,
8715        })
8716    }
8717
8718    /// Adapts the optional recovery result to the normal outcome list used by
8719    /// rule-call transitions.
8720    fn child_rule_failure_recovery_outcomes(
8721        &mut self,
8722        request: ChildRuleFailureRecovery<'_>,
8723    ) -> Vec<RecognizeOutcome> {
8724        let sync_symbols =
8725            state_sync_symbols(request.atn, request.follow_state, request.stop_state);
8726        self.child_rule_failure_recovery(
8727            request.rule_index,
8728            request.start_index,
8729            &sync_symbols,
8730            request.member_values,
8731            request.expected,
8732        )
8733        .into_iter()
8734        .collect()
8735    }
8736
8737    /// Formats expected token types using ANTLR's single-token or set syntax.
8738    fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
8739        expected_symbols_display(symbols, self.vocabulary())
8740    }
8741
8742    /// Returns the single-token deletion repair if the token after `index`
8743    /// satisfies the failed consuming transition.
8744    fn single_token_deletion(
8745        &mut self,
8746        transition: ParserTransition<'_>,
8747        index: usize,
8748        max_token_type: i32,
8749        expected_symbols: &BTreeSet<i32>,
8750    ) -> Option<(ParserDiagnostic, usize, i32)> {
8751        let current_symbol = self.token_type_at(index);
8752        if current_symbol == TOKEN_EOF {
8753            return None;
8754        }
8755        let next_index = self.consume_index(index, current_symbol);
8756        if next_index == index {
8757            return None;
8758        }
8759        let next_symbol = self.token_type_at(next_index);
8760        if !transition.matches(next_symbol, 1, max_token_type) {
8761            return None;
8762        }
8763        let transition_expected = transition_expected_symbols(transition, max_token_type);
8764        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
8765            &transition_expected
8766        } else {
8767            expected_symbols
8768        });
8769        let current = self.token_at(index);
8770        let message = format!(
8771            "extraneous input {} expecting {expected_display}",
8772            current
8773                .as_ref()
8774                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
8775        );
8776        Some((
8777            diagnostic_for_token(current, message),
8778            next_index,
8779            next_symbol,
8780        ))
8781    }
8782
8783    /// Returns the repair used when deleting the current token lets a recovery
8784    /// state continue with the following token.
8785    fn current_token_deletion(
8786        &mut self,
8787        index: usize,
8788        expected_symbols: &BTreeSet<i32>,
8789    ) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
8790        if expected_symbols.is_empty() {
8791            return None;
8792        }
8793        let current_symbol = self.token_type_at(index);
8794        if current_symbol == TOKEN_EOF {
8795            return None;
8796        }
8797        let current = self.token_at(index);
8798        let message = format!(
8799            "extraneous input {} expecting {}",
8800            current
8801                .as_ref()
8802                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
8803            self.expected_symbols_display(expected_symbols)
8804        );
8805        let diagnostic = diagnostic_for_token(current, message);
8806        let mut skipped = Vec::new();
8807        let mut cursor = index;
8808        loop {
8809            let symbol = self.token_type_at(cursor);
8810            if symbol == TOKEN_EOF {
8811                return None;
8812            }
8813            skipped.push(cursor);
8814            let next_index = self.consume_index(cursor, symbol);
8815            if next_index == cursor {
8816                return None;
8817            }
8818            let next_symbol = self.token_type_at(next_index);
8819            if expected_symbols.contains(&next_symbol) {
8820                return Some((diagnostic, next_index, skipped));
8821            }
8822            cursor = next_index;
8823        }
8824    }
8825
8826    /// Returns the single-token insertion repair for a failed consuming
8827    /// transition. The caller validates the repair by continuing from the
8828    /// transition target at the same input index.
8829    fn single_token_insertion(
8830        &mut self,
8831        transition: ParserTransition<'_>,
8832        index: usize,
8833        max_token_type: i32,
8834        expected_symbols: &BTreeSet<i32>,
8835        follow_symbols: &BTreeSet<i32>,
8836    ) -> Option<(ParserDiagnostic, i32, String)> {
8837        let current_symbol = self.token_type_at(index);
8838        if !follow_symbols.contains(&current_symbol) {
8839            return None;
8840        }
8841        let transition_expected = transition_expected_symbols(transition, max_token_type);
8842        let token_type = transition_expected.iter().next().copied()?;
8843        let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
8844            &transition_expected
8845        } else {
8846            expected_symbols
8847        });
8848        let mut token_symbols = BTreeSet::new();
8849        token_symbols.insert(token_type);
8850        let missing_token_display = self.expected_symbols_display(&token_symbols);
8851        let current = self.token_at(index);
8852        let message = format!(
8853            "missing {expected_display} at {}",
8854            current
8855                .as_ref()
8856                .map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
8857        );
8858        let text = format!("<missing {missing_token_display}>");
8859        Some((
8860            diagnostic_for_token(current.as_ref(), message),
8861            token_type,
8862            text,
8863        ))
8864    }
8865
8866    /// Explores ANTLR's single-token deletion recovery for the fast recognizer:
8867    /// skip the unexpected current token when the following token satisfies the
8868    /// transition that failed.
8869    fn fast_single_token_deletion_recovery(
8870        &mut self,
8871        recovery: FastRecoveryRequest<'_, '_>,
8872        predicate_context: Option<FastPredicateContext<'_>>,
8873    ) -> Vec<FastRecognizeOutcome> {
8874        let FastRecoveryRequest {
8875            atn,
8876            transition,
8877            expected_symbols,
8878            target,
8879            request,
8880            visiting,
8881            memo,
8882            expected,
8883        } = recovery;
8884        let FastRecognizeRequest {
8885            stop_state,
8886            index,
8887            rule_start_index,
8888            decision_start_index,
8889            precedence,
8890            depth,
8891            ..
8892        } = request;
8893        let Some((diagnostic, next_index, next_symbol)) =
8894            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
8895        else {
8896            return Vec::new();
8897        };
8898        let after_next = self.consume_index(next_index, next_symbol);
8899        let empty_recovery = self.empty_recovery_symbols();
8900        self.recognize_state_fast(
8901            atn,
8902            FastRecognizeRequest {
8903                state_number: target,
8904                stop_state,
8905                index: after_next,
8906                rule_start_index,
8907                decision_start_index,
8908                precedence,
8909                depth: depth + 1,
8910                recovery_symbols: empty_recovery,
8911                recovery_state: None,
8912            },
8913            FastRecognizeScratch {
8914                predicate_context,
8915                visiting,
8916                memo,
8917                expected,
8918                native_depth: 0,
8919            },
8920        )
8921        .into_iter()
8922        .map(|mut outcome| {
8923            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
8924            outcome.diagnostics = self
8925                .recognition_arena
8926                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
8927            if self.fast_token_nodes_enabled {
8928                let token = self.arena_token_node(next_index, false);
8929                self.defer_fast_outcome_node(&mut outcome, token);
8930                let error = self.arena_token_node(index, true);
8931                self.defer_fast_outcome_node(&mut outcome, error);
8932            }
8933            outcome
8934        })
8935        .collect()
8936    }
8937
8938    /// Explores ANTLR's single-token insertion recovery for the fast recognizer:
8939    /// pretend the expected transition token was present and continue without
8940    /// consuming the current token.
8941    fn fast_single_token_insertion_recovery(
8942        &mut self,
8943        recovery: FastRecoveryRequest<'_, '_>,
8944        predicate_context: Option<FastPredicateContext<'_>>,
8945    ) -> Vec<FastRecognizeOutcome> {
8946        let FastRecoveryRequest {
8947            atn,
8948            transition,
8949            expected_symbols,
8950            target,
8951            request,
8952            visiting,
8953            memo,
8954            expected,
8955        } = recovery;
8956        let FastRecognizeRequest {
8957            stop_state,
8958            index,
8959            rule_start_index,
8960            decision_start_index,
8961            precedence,
8962            depth,
8963            ..
8964        } = request;
8965        let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
8966        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
8967            transition,
8968            index,
8969            atn.max_token_type(),
8970            &expected_symbols,
8971            &follow_symbols,
8972        ) else {
8973            return Vec::new();
8974        };
8975        let empty_recovery = self.empty_recovery_symbols();
8976        self.recognize_state_fast(
8977            atn,
8978            FastRecognizeRequest {
8979                state_number: target,
8980                stop_state,
8981                index,
8982                rule_start_index,
8983                decision_start_index,
8984                precedence,
8985                depth: depth + 1,
8986                recovery_symbols: empty_recovery,
8987                recovery_state: None,
8988            },
8989            FastRecognizeScratch {
8990                predicate_context,
8991                visiting,
8992                memo,
8993                expected,
8994                native_depth: 0,
8995            },
8996        )
8997        .into_iter()
8998        .map(|mut outcome| {
8999            outcome.diagnostics = self
9000                .recognition_arena
9001                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9002            let missing = self.arena_missing_token_node(token_type, index, text.clone());
9003            self.defer_fast_outcome_node(&mut outcome, missing);
9004            outcome
9005        })
9006        .collect()
9007    }
9008
9009    /// Retries the current fast-recognition state after deleting one
9010    /// unexpected token that precedes a valid loop or block continuation.
9011    fn fast_current_token_deletion_recovery(
9012        &mut self,
9013        recovery: FastCurrentTokenDeletionRequest<'_, '_>,
9014        predicate_context: Option<FastPredicateContext<'_>>,
9015    ) -> Vec<FastRecognizeOutcome> {
9016        let FastCurrentTokenDeletionRequest {
9017            atn,
9018            expected_symbols,
9019            mut request,
9020            visiting,
9021            memo,
9022            expected,
9023        } = recovery;
9024        if request.index == request.rule_start_index {
9025            return Vec::new();
9026        }
9027        let Some((diagnostic, next_index, skipped)) =
9028            self.current_token_deletion(request.index, &expected_symbols)
9029        else {
9030            return Vec::new();
9031        };
9032        request.state_number = request.recovery_state.unwrap_or(request.state_number);
9033        request.index = next_index;
9034        request.depth += 1;
9035        request.recovery_state = None;
9036        self.recognize_state_fast(
9037            atn,
9038            request,
9039            FastRecognizeScratch {
9040                predicate_context,
9041                visiting,
9042                memo,
9043                expected,
9044                native_depth: 0,
9045            },
9046        )
9047        .into_iter()
9048        .map(|mut outcome| {
9049            outcome.diagnostics = self
9050                .recognition_arena
9051                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
9052            for index in skipped.iter().rev() {
9053                let error = self.arena_token_node(*index, true);
9054                self.defer_fast_outcome_node(&mut outcome, error);
9055            }
9056            outcome
9057        })
9058        .collect()
9059    }
9060
9061    /// Converts a failed child rule into a recovered fast-recognizer outcome so
9062    /// the parent can keep its child rule context and continue at a sync token.
9063    fn fast_child_rule_failure_recovery(
9064        &mut self,
9065        rule_index: usize,
9066        start_index: usize,
9067        sync_symbols: &BTreeSet<i32>,
9068        expected: &ExpectedTokens,
9069    ) -> Option<FastRecognizeOutcome> {
9070        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
9071        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
9072        let mut next_index = error_index;
9073        loop {
9074            let symbol = self.token_type_at(next_index);
9075            if sync_symbols.contains(&symbol) {
9076                if next_index == error_index {
9077                    return None;
9078                }
9079                break;
9080            }
9081            if symbol == TOKEN_EOF {
9082                break;
9083            }
9084            let after = self.consume_index(next_index, symbol);
9085            if after == next_index {
9086                break;
9087            }
9088            next_index = after;
9089        }
9090        let diagnostics = self
9091            .recognition_arena
9092            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
9093        let mut nodes = NodeSeqId::EMPTY;
9094        if self.fast_token_nodes_enabled {
9095            let error = self.arena_token_node(error_index, true);
9096            self.arena_prepend(&mut nodes, error);
9097        }
9098        Some(FastRecognizeOutcome {
9099            index: next_index,
9100            consumed_eof: false,
9101            diagnostics,
9102            deferred_nodes: FastDeferredNodeId::EMPTY,
9103            nodes,
9104        })
9105    }
9106
9107    /// Adapts the optional child-rule recovery result to the fast-recognizer
9108    /// outcome list used by rule-call transitions.
9109    fn fast_child_rule_failure_recovery_outcomes(
9110        &mut self,
9111        request: FastChildRuleFailureRecoveryRequest<'_>,
9112    ) -> Vec<FastRecognizeOutcome> {
9113        let FastChildRuleFailureRecoveryRequest {
9114            atn,
9115            rule_index,
9116            start_index,
9117            follow_state,
9118            stop_state,
9119            expected,
9120        } = request;
9121        let sync_symbols = state_sync_symbols(atn, follow_state, stop_state);
9122        self.fast_child_rule_failure_recovery(rule_index, start_index, &sync_symbols, expected)
9123            .into_iter()
9124            .collect()
9125    }
9126
9127    fn defer_fast_outcome_node(
9128        &mut self,
9129        outcome: &mut FastRecognizeOutcome,
9130        node: RecognizedNodeId,
9131    ) {
9132        if outcome.deferred_nodes.is_empty() {
9133            self.arena_prepend(&mut outcome.nodes, node);
9134            return;
9135        }
9136        let fragment = self.recognition_arena.prepend(NodeSeqId::EMPTY, node);
9137        let fragment = self.recognition_arena.deferred_fragment(fragment);
9138        outcome.deferred_nodes = self
9139            .recognition_arena
9140            .concat_deferred_nodes(fragment, outcome.deferred_nodes);
9141    }
9142
9143    fn defer_fast_outcome_alternative(
9144        &mut self,
9145        outcome: &mut FastRecognizeOutcome,
9146        alt_number: usize,
9147    ) {
9148        let alternative = self.recognition_arena.deferred_alternative(alt_number);
9149        outcome.deferred_nodes = self
9150            .recognition_arena
9151            .concat_deferred_nodes(alternative, outcome.deferred_nodes);
9152    }
9153
9154    fn defer_fast_outcome_boundary(
9155        &mut self,
9156        outcome: &mut FastRecognizeOutcome,
9157        rule_index: usize,
9158    ) {
9159        let boundary = self
9160            .recognition_arena
9161            .deferred_left_recursive_boundary(rule_index);
9162        outcome.deferred_nodes = self
9163            .recognition_arena
9164            .concat_deferred_nodes(boundary, outcome.deferred_nodes);
9165    }
9166
9167    fn materialize_fast_deferred_nodes(
9168        &mut self,
9169        root: FastDeferredNodeId,
9170        initial_suffix: NodeSeqId,
9171    ) -> (NodeSeqId, usize) {
9172        if root.is_empty() {
9173            return (initial_suffix, 0);
9174        }
9175
9176        enum Frame {
9177            Visit(FastDeferredNodeId),
9178            ContinuePrefix(FastDeferredNodeId),
9179            FinishRule {
9180                rule: FastDeferredRule,
9181                parent_suffix: NodeSeqId,
9182                parent_alt_number: u32,
9183                parent_pending_boundary: Option<RecognizedNodeId>,
9184            },
9185        }
9186
9187        let mut result = initial_suffix;
9188        // The rope is visited suffix-first while nodes are prepended. Later
9189        // alternatives arrive first, so earlier markers overwrite them; a
9190        // boundary redirects those earlier markers to the wrapped context.
9191        let mut alt_number = 0;
9192        let mut pending_boundary = None;
9193        let mut pending = Vec::with_capacity(16);
9194        pending.push(Frame::Visit(root));
9195        let mut fragment_nodes = Vec::new();
9196        while let Some(frame) = pending.pop() {
9197            match frame {
9198                Frame::Visit(deferred) => {
9199                    if deferred.is_empty() {
9200                        continue;
9201                    }
9202
9203                    match self.recognition_arena.deferred_node(deferred) {
9204                        FastDeferredNode::Fragment(sequence) => {
9205                            fragment_nodes.clear();
9206                            fragment_nodes.extend(self.recognition_arena.iter(sequence));
9207                            while let Some(node) = fragment_nodes.pop() {
9208                                self.arena_prepend(&mut result, node);
9209                            }
9210                        }
9211                        FastDeferredNode::Rule(rule) => {
9212                            let rule = self.recognition_arena.deferred_rule(rule);
9213                            let parent_suffix = result;
9214                            let parent_alt_number = alt_number;
9215                            let parent_pending_boundary = pending_boundary;
9216                            result = rule.children;
9217                            alt_number = 0;
9218                            pending_boundary = None;
9219                            pending.push(Frame::FinishRule {
9220                                rule,
9221                                parent_suffix,
9222                                parent_alt_number,
9223                                parent_pending_boundary,
9224                            });
9225                            pending.push(Frame::Visit(rule.deferred_children));
9226                        }
9227                        FastDeferredNode::Alternative(selected) => {
9228                            if let Some(boundary) = pending_boundary {
9229                                self.recognition_arena
9230                                    .set_boundary_alt_number(boundary, selected);
9231                            } else {
9232                                alt_number = selected;
9233                            }
9234                        }
9235                        FastDeferredNode::LeftRecursiveBoundary { rule_index } => {
9236                            let boundary = self.arena_boundary_node(rule_index as usize, 0);
9237                            self.arena_prepend(&mut result, boundary);
9238                            pending_boundary = Some(boundary);
9239                        }
9240                        FastDeferredNode::Concat {
9241                            prefix,
9242                            suffix: deferred_suffix,
9243                        } => {
9244                            pending.push(Frame::ContinuePrefix(prefix));
9245                            pending.push(Frame::Visit(deferred_suffix));
9246                        }
9247                    }
9248                }
9249                Frame::ContinuePrefix(prefix) => pending.push(Frame::Visit(prefix)),
9250                Frame::FinishRule {
9251                    rule,
9252                    parent_suffix,
9253                    parent_alt_number,
9254                    parent_pending_boundary,
9255                } => {
9256                    let node = self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
9257                        rule_index: rule.rule_index,
9258                        invoking_state: rule.invoking_state,
9259                        alt_number,
9260                        start_index: rule.start_index,
9261                        stop_index: rule.stop_index,
9262                        return_values: None,
9263                        children: result,
9264                    });
9265                    result = parent_suffix;
9266                    self.arena_prepend(&mut result, node);
9267                    alt_number = parent_alt_number;
9268                    pending_boundary = parent_pending_boundary;
9269                }
9270            }
9271        }
9272        (result, alt_number as usize)
9273    }
9274
9275    fn materialize_fast_outcome_nodes(&mut self, outcome: &mut FastRecognizeOutcome) -> usize {
9276        let deferred_nodes = std::mem::take(&mut outcome.deferred_nodes);
9277        let (nodes, alt_number) =
9278            self.materialize_fast_deferred_nodes(deferred_nodes, outcome.nodes);
9279        outcome.nodes = nodes;
9280        alt_number
9281    }
9282
9283    /// Walks one ordinary `*`/`+` repetition at a time so input length grows
9284    /// heap work instead of the native call stack.
9285    fn recognize_repetition_fast(
9286        &mut self,
9287        atn: &Atn,
9288        request: &FastRecognizeRequest,
9289        shape: FastRepetitionShape,
9290        scratch: FastRecognizeScratch<'_, '_>,
9291    ) -> Vec<FastRecognizeOutcome> {
9292        let FastRecognizeScratch {
9293            predicate_context,
9294            visiting,
9295            memo,
9296            expected,
9297            native_depth,
9298        } = scratch;
9299        let lookahead = if self.fast_first_set_prefilter {
9300            atn.state(request.state_number).and_then(|state| {
9301                state
9302                    .rule_index()
9303                    .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
9304                    .map(|rule_stop| self.cached_decision_lookahead(atn, state, rule_stop))
9305            })
9306        } else {
9307            None
9308        };
9309        let (enter_alt_number, exit_alt_number) = if self.fast_track_alt_numbers {
9310            let state = atn
9311                .state(request.state_number)
9312                .expect("repetition request state must exist");
9313            (
9314                next_alt_number(state, 2, shape.enter_transition_index, 0, true),
9315                next_alt_number(state, 2, shape.exit_transition_index, 0, true),
9316            )
9317        } else {
9318            (0, 0)
9319        };
9320        let mut work = Vec::with_capacity(2);
9321        push_fast_repetition_work(
9322            &mut work,
9323            shape,
9324            FastRepetitionPath {
9325                index: request.index,
9326                deferred_nodes: FastDeferredNodeId::EMPTY,
9327                diagnostics: DiagnosticSeqId::EMPTY,
9328                consumed_eof: false,
9329            },
9330            lookahead.as_deref(),
9331            self.token_type_at(request.index),
9332        );
9333        let mut coordinates = FastRepetitionCoordinates::new(request.index);
9334        let mut outcomes = Vec::new();
9335        while let Some(item) = work.pop() {
9336            match item {
9337                FastRepetitionWork::Enter(path) => {
9338                    if !coordinates.insert_entered(path) {
9339                        continue;
9340                    }
9341                    let path_nodes = if enter_alt_number == 0 {
9342                        path.deferred_nodes
9343                    } else {
9344                        let alternative = self
9345                            .recognition_arena
9346                            .deferred_alternative(enter_alt_number);
9347                        self.recognition_arena
9348                            .concat_deferred_nodes(path.deferred_nodes, alternative)
9349                    };
9350                    let body_outcomes = self.recognize_state_fast(
9351                        atn,
9352                        FastRecognizeRequest {
9353                            state_number: shape.enter_target,
9354                            stop_state: shape.body_stop_state,
9355                            index: path.index,
9356                            rule_start_index: request.rule_start_index,
9357                            decision_start_index: request.decision_start_index,
9358                            precedence: request.precedence,
9359                            depth: request.depth.saturating_add(1),
9360                            recovery_symbols: Rc::clone(&request.recovery_symbols),
9361                            recovery_state: request.recovery_state,
9362                        },
9363                        FastRecognizeScratch {
9364                            predicate_context,
9365                            visiting: &mut *visiting,
9366                            memo: &mut *memo,
9367                            expected: &mut *expected,
9368                            native_depth: native_depth + 1,
9369                        },
9370                    );
9371                    for body in body_outcomes.into_iter().rev() {
9372                        // ANTLR rejects nullable repetition bodies. Keep the
9373                        // interpreter bounded for malformed or recovered ATNs
9374                        // by mirroring the existing same-coordinate cycle cut.
9375                        if body.index <= path.index {
9376                            continue;
9377                        }
9378                        let body_fragment = self.recognition_arena.deferred_fragment(body.nodes);
9379                        let body_nodes = self
9380                            .recognition_arena
9381                            .concat_deferred_nodes(body.deferred_nodes, body_fragment);
9382                        let deferred_nodes = self
9383                            .recognition_arena
9384                            .concat_deferred_nodes(path_nodes, body_nodes);
9385                        let next_path = FastRepetitionPath {
9386                            index: body.index,
9387                            deferred_nodes,
9388                            diagnostics: self
9389                                .recognition_arena
9390                                .concat_diagnostics(path.diagnostics, body.diagnostics),
9391                            consumed_eof: path.consumed_eof || body.consumed_eof,
9392                        };
9393                        let symbol = self.token_type_at(next_path.index);
9394                        push_fast_repetition_work(
9395                            &mut work,
9396                            shape,
9397                            next_path,
9398                            lookahead.as_deref(),
9399                            symbol,
9400                        );
9401                    }
9402                }
9403                FastRepetitionWork::Exit(path) => {
9404                    if !coordinates.insert_exited(path) {
9405                        continue;
9406                    }
9407                    let path_nodes = if exit_alt_number == 0 {
9408                        path.deferred_nodes
9409                    } else {
9410                        let alternative =
9411                            self.recognition_arena.deferred_alternative(exit_alt_number);
9412                        self.recognition_arena
9413                            .concat_deferred_nodes(path.deferred_nodes, alternative)
9414                    };
9415                    let suffixes = self.recognize_state_fast(
9416                        atn,
9417                        FastRecognizeRequest {
9418                            state_number: shape.exit_target,
9419                            stop_state: request.stop_state,
9420                            index: path.index,
9421                            rule_start_index: request.rule_start_index,
9422                            decision_start_index: request.decision_start_index,
9423                            precedence: request.precedence,
9424                            depth: request.depth.saturating_add(1),
9425                            recovery_symbols: Rc::clone(&request.recovery_symbols),
9426                            recovery_state: request.recovery_state,
9427                        },
9428                        FastRecognizeScratch {
9429                            predicate_context,
9430                            visiting: &mut *visiting,
9431                            memo: &mut *memo,
9432                            expected: &mut *expected,
9433                            native_depth: native_depth + 1,
9434                        },
9435                    );
9436                    for mut outcome in suffixes {
9437                        outcome.deferred_nodes = self
9438                            .recognition_arena
9439                            .concat_deferred_nodes(path_nodes, outcome.deferred_nodes);
9440                        outcome.diagnostics = self
9441                            .recognition_arena
9442                            .concat_diagnostics(path.diagnostics, outcome.diagnostics);
9443                        outcome.consumed_eof |= path.consumed_eof;
9444                        outcomes.push(outcome);
9445                    }
9446                }
9447            }
9448        }
9449        dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
9450        outcomes
9451    }
9452
9453    /// Attempts to reach `stop_state` from `state_number` without committing
9454    /// token consumption to the parser's public stream position.
9455    fn recognize_state_fast(
9456        &mut self,
9457        atn: &Atn,
9458        request: FastRecognizeRequest,
9459        scratch: FastRecognizeScratch<'_, '_>,
9460    ) -> Vec<FastRecognizeOutcome> {
9461        if scratch.native_depth != 0 && scratch.native_depth < FAST_RECOGNIZE_STACK_CHECK_INTERVAL {
9462            return self.recognize_state_fast_inner(atn, request, scratch);
9463        }
9464        self.recognize_state_fast_checked(atn, request, scratch)
9465    }
9466
9467    #[inline(never)]
9468    fn recognize_state_fast_checked(
9469        &mut self,
9470        atn: &Atn,
9471        request: FastRecognizeRequest,
9472        mut scratch: FastRecognizeScratch<'_, '_>,
9473    ) -> Vec<FastRecognizeOutcome> {
9474        scratch.native_depth = 1;
9475        stacker::maybe_grow(FAST_RECOGNIZE_RED_ZONE, FAST_RECOGNIZE_STACK_SIZE, || {
9476            self.recognize_state_fast_inner(atn, request, scratch)
9477        })
9478    }
9479
9480    #[allow(clippy::too_many_lines)]
9481    fn recognize_state_fast_inner(
9482        &mut self,
9483        atn: &Atn,
9484        request: FastRecognizeRequest,
9485        scratch: FastRecognizeScratch<'_, '_>,
9486    ) -> Vec<FastRecognizeOutcome> {
9487        #[cfg(feature = "perf-counters")]
9488        perf_counters::inc(&perf_counters::RFS_CALLS, 1);
9489        let FastRecognizeScratch {
9490            predicate_context,
9491            visiting,
9492            memo,
9493            expected,
9494            native_depth,
9495        } = scratch;
9496        let FastRecognizeRequest {
9497            mut state_number,
9498            stop_state,
9499            mut index,
9500            rule_start_index,
9501            decision_start_index,
9502            precedence,
9503            mut depth,
9504            recovery_symbols,
9505            recovery_state,
9506        } = request;
9507        let max_token_type = atn.max_token_type();
9508        // Walk straight-line epsilon chains in a loop instead of recursing
9509        // into `recognize_state_fast` for each intermediate state. ATN
9510        // serialization places long sequences of `BasicBlock` epsilon
9511        // transitions between decisions: turning that chain into a loop
9512        // collapses many recursive calls (and their memo lookups, vec
9513        // allocations, and visit-set churn) into a single function frame.
9514        // The loop exits as soon as we hit the original state's logic
9515        // (multi-alt, decision, rule call, unmatched atom/range/set, gated
9516        // precedence) so existing fanout, recovery, and memoization still
9517        // apply unchanged.
9518        //
9519        // The inline case also handles single-atom-match states on the
9520        // happy-pass path: when the lone consuming transition matches the
9521        // current lookahead, advance the index and continue without paying
9522        // for a full `recognize_state_fast` recursion. We track tokens we
9523        // consumed inline in `inline_consumed_tokens` so they can be
9524        // prepended onto the eventual outcome list once we hit a state
9525        // whose handling falls outside this fast loop.
9526        let mut inline_consumed_tokens: Vec<usize> = Vec::new();
9527        let mut inline_consumed_eof = false;
9528        loop {
9529            if depth > RECOGNITION_DEPTH_LIMIT {
9530                return Vec::new();
9531            }
9532            if state_number == stop_state {
9533                let mut nodes = NodeSeqId::EMPTY;
9534                if self.fast_token_nodes_enabled {
9535                    for token_index in inline_consumed_tokens.iter().rev() {
9536                        let token = self.arena_token_node(*token_index, false);
9537                        self.arena_prepend(&mut nodes, token);
9538                    }
9539                }
9540                return vec![FastRecognizeOutcome {
9541                    index,
9542                    consumed_eof: inline_consumed_eof,
9543                    diagnostics: DiagnosticSeqId::EMPTY,
9544                    deferred_nodes: FastDeferredNodeId::EMPTY,
9545                    nodes,
9546                }];
9547            }
9548            let Some(state) = atn.state(state_number) else {
9549                return Vec::new();
9550            };
9551            let transitions = state.transitions();
9552            if transitions.len() == 1 && !state.precedence_rule_decision() {
9553                let transition = transitions
9554                    .first()
9555                    .expect("single transition checked above");
9556                let transition_kind = transition.kind();
9557                let target = transition.target();
9558                match transition_kind {
9559                    ParserTransitionKind::Epsilon | ParserTransitionKind::Action
9560                        if left_recursive_boundary(atn, state, target).is_none() =>
9561                    {
9562                        #[cfg(feature = "perf-counters")]
9563                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9564                        state_number = target;
9565                        depth += 1;
9566                        continue;
9567                    }
9568                    ParserTransitionKind::Predicate
9569                        if left_recursive_boundary(atn, state, target).is_none() =>
9570                    {
9571                        #[cfg(feature = "perf-counters")]
9572                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9573                        if !self.fast_parser_predicate_matches(predicate_context, transition, index)
9574                        {
9575                            record_predicate_no_viable(expected, decision_start_index, index);
9576                            return Vec::new();
9577                        }
9578                        state_number = target;
9579                        depth += 1;
9580                        continue;
9581                    }
9582                    ParserTransitionKind::Precedence
9583                        if packed_i32(transition.arg0()) >= precedence
9584                            && left_recursive_boundary(atn, state, target).is_none() =>
9585                    {
9586                        #[cfg(feature = "perf-counters")]
9587                        perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9588                        state_number = target;
9589                        depth += 1;
9590                        continue;
9591                    }
9592                    // Single-atom / range / set / wildcard / not-set states
9593                    // are common (~17K of ~125K calls on C#) and almost
9594                    // always succeed in pass 1: no fanout, no recovery, no
9595                    // diagnostics. Inline the token match and continue
9596                    // walking instead of recursing — the recursive path
9597                    // would just allocate a Vec, build one outcome, prepend
9598                    // a Token node, and return. Skip pass 2 (recovery
9599                    // enabled): there the failure branch matters and the
9600                    // existing recursive code records expected symbols.
9601                    ParserTransitionKind::Atom
9602                    | ParserTransitionKind::Range
9603                    | ParserTransitionKind::Set
9604                    | ParserTransitionKind::NotSet
9605                    | ParserTransitionKind::Wildcard
9606                        if !self.fast_recovery_enabled =>
9607                    {
9608                        let symbol = self.token_type_at(index);
9609                        if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
9610                            #[cfg(feature = "perf-counters")]
9611                            perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
9612                            if self.fast_token_nodes_enabled {
9613                                inline_consumed_tokens.push(index);
9614                            }
9615                            inline_consumed_eof |= symbol == TOKEN_EOF;
9616                            index = self.consume_index(index, symbol);
9617                            state_number = target;
9618                            depth += 1;
9619                            continue;
9620                        }
9621                        // Fall through to break and let the regular
9622                        // body handle the no-match case (returns empty).
9623                    }
9624                    _ => {}
9625                }
9626            }
9627            break;
9628        }
9629        // If we collected token nodes inline but bail to the recursive
9630        // body (decision state, rule call, etc.), the outcomes returned
9631        // below will need those token nodes prepended.
9632        let inline_pending = !inline_consumed_tokens.is_empty() || inline_consumed_eof;
9633        let Some(state) = atn.state(state_number) else {
9634            return Vec::new();
9635        };
9636        let transitions = state.transitions();
9637        let transition_count = transitions.len();
9638        if !self.fast_recovery_enabled
9639            && let Some(shape) = fast_repetition_shape(atn, state)
9640        {
9641            let mut outcomes = self.recognize_repetition_fast(
9642                atn,
9643                &FastRecognizeRequest {
9644                    state_number,
9645                    stop_state,
9646                    index,
9647                    rule_start_index,
9648                    decision_start_index,
9649                    precedence,
9650                    depth,
9651                    recovery_symbols: Rc::clone(&recovery_symbols),
9652                    recovery_state,
9653                },
9654                shape,
9655                FastRecognizeScratch {
9656                    predicate_context,
9657                    visiting: &mut *visiting,
9658                    memo: &mut *memo,
9659                    expected: &mut *expected,
9660                    native_depth: native_depth + 1,
9661                },
9662            );
9663            if inline_pending {
9664                for outcome in &mut outcomes {
9665                    outcome.consumed_eof |= inline_consumed_eof;
9666                    if self.fast_token_nodes_enabled {
9667                        for token_index in inline_consumed_tokens.iter().rev() {
9668                            let token = self.arena_token_node(*token_index, false);
9669                            self.defer_fast_outcome_node(outcome, token);
9670                        }
9671                    }
9672                }
9673            }
9674            return outcomes;
9675        }
9676        // In pass 1 (`fast_recovery_enabled == false`) the recovery-related
9677        // fields and the rule/decision boundary indices are pure plumbing —
9678        // they only affect the recovery branch and the no-viable diagnostic
9679        // recording, neither of which fires when recovery is off. Zeroing
9680        // them in the memo key collapses calls that visit the same
9681        // `(state, index)` from different rule-call sites onto one cache
9682        // entry, which is the dominant cost on large grammars (e.g. C#) where
9683        // many rules eventually delegate into the same `expression` /
9684        // `primary_expression` / `type` branches.
9685        let key = if self.fast_recovery_enabled {
9686            FastRecognizeKey {
9687                state_number,
9688                stop_state,
9689                index,
9690                rule_start_index,
9691                decision_start_index,
9692                precedence,
9693                recovery_symbols_id: Rc::as_ptr(&recovery_symbols) as usize,
9694                recovery_state,
9695            }
9696        } else {
9697            FastRecognizeKey {
9698                state_number,
9699                stop_state,
9700                index,
9701                rule_start_index: 0,
9702                decision_start_index: None,
9703                precedence,
9704                recovery_symbols_id: 0,
9705                recovery_state: None,
9706            }
9707        };
9708        // Once the clean-pass probe has established that coordinates do not
9709        // repeat, stop paying for the full memo table. Recovery always keeps
9710        // memoization because cached failures carry diagnostics, while
9711        // repeat-heavy clean parses promote before reaching sparse mode.
9712        let memo_lookup_enabled = self.fast_recovery_enabled
9713            || (transition_count > 1 && self.clean_memo_enabled_for_key(&key));
9714        if memo_lookup_enabled {
9715            if let Some(outcomes) = memo.get(&key) {
9716                #[cfg(feature = "perf-counters")]
9717                {
9718                    perf_counters::inc(&perf_counters::RFS_MEMO_HITS, 1);
9719                    perf_counters::inc(&perf_counters::OUTCOMES_CLONED, outcomes.len() as u64);
9720                }
9721                // Materialize a fresh `Vec` from the cached slice; the caller
9722                // mutates per-outcome state (eof flags, prepended nodes) so we
9723                // can't hand them the shared backing.
9724                if !inline_consumed_tokens.is_empty() || inline_consumed_eof {
9725                    let inline_eof = inline_consumed_eof;
9726                    let inline_tokens = &inline_consumed_tokens;
9727                    return outcomes
9728                        .iter()
9729                        .copied()
9730                        .map(|mut outcome| {
9731                            if inline_eof {
9732                                outcome.consumed_eof = true;
9733                            }
9734                            if self.fast_token_nodes_enabled {
9735                                for token_index in inline_tokens.iter().rev() {
9736                                    let token = self.arena_token_node(*token_index, false);
9737                                    self.defer_fast_outcome_node(&mut outcome, token);
9738                                }
9739                            }
9740                            outcome
9741                        })
9742                        .collect();
9743                }
9744                return outcomes.to_vec();
9745            }
9746            #[cfg(feature = "perf-counters")]
9747            perf_counters::inc(&perf_counters::RFS_MEMO_MISSES, 1);
9748        }
9749
9750        // Cycle detection: clean recognition keeps the narrow static cycle
9751        // guard used on hot paths. Recovery needs the broader epsilon-state
9752        // guard because an otherwise non-nullable loop body can recover as an
9753        // empty child at EOF and re-enter the loop at the same token.
9754        let needs_cycle_guard = if self.fast_recovery_enabled {
9755            transitions.iter().any(ParserTransition::is_epsilon)
9756        } else {
9757            transition_count > 1 && self.state_can_reenter_without_consuming(atn, state_number)
9758        };
9759        #[cfg(feature = "perf-counters")]
9760        if needs_cycle_guard {
9761            perf_counters::inc(&perf_counters::MULTI_TRANS_BODY, 1);
9762        } else {
9763            perf_counters::inc(&perf_counters::SINGLE_TRANS_BODY, 1);
9764            match state
9765                .transitions()
9766                .first()
9767                .expect("single-transition path requires one transition")
9768                .data()
9769            {
9770                Transition::Rule { .. } => {
9771                    perf_counters::inc(&perf_counters::SINGLE_TRANS_RULE, 1);
9772                }
9773                Transition::Atom { .. }
9774                | Transition::Range { .. }
9775                | Transition::Set { .. }
9776                | Transition::NotSet { .. }
9777                | Transition::Wildcard { .. } => {
9778                    perf_counters::inc(&perf_counters::SINGLE_TRANS_ATOM, 1);
9779                }
9780                _ => {
9781                    perf_counters::inc(&perf_counters::SINGLE_TRANS_OTHER, 1);
9782                }
9783            }
9784        }
9785        let has_inserted_cycle_guard = if needs_cycle_guard {
9786            if !visiting.insert(key.clone()) {
9787                #[cfg(feature = "perf-counters")]
9788                perf_counters::inc(&perf_counters::RFS_VISITING_CYCLE, 1);
9789                return Vec::new();
9790            }
9791            true
9792        } else {
9793            false
9794        };
9795        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
9796            Some(index)
9797        } else {
9798            decision_start_index
9799        };
9800        let (epsilon_recovery_symbols, epsilon_recovery_state) = if self.fast_recovery_enabled {
9801            fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state)
9802        } else {
9803            (Rc::clone(&recovery_symbols), recovery_state)
9804        };
9805
9806        // Lookahead-based pruning. At a multi-alternative state we cache the
9807        // look-1 set of every outgoing transition; on visit we keep only the
9808        // transitions whose look-1 can accept the current lookahead (or that
9809        // can be reached without consuming and so could legitimately match a
9810        // shorter input). This is the main speedup vs. blind speculative
9811        // recursion: it lets each visit fan out only to the alternatives that
9812        // could possibly contribute a clean parse, mirroring the SLL phase of
9813        // ANTLR's adaptive prediction.
9814        //
9815        // Pruning is skipped at:
9816        //   * rule-start states (a child rule call may need every internal
9817        //     transition to surface single-token recovery diagnostics that
9818        //     ANTLR's reference parser emits at the rule's first consuming
9819        //     transition; the FIRST-set retry path turns the prefilter off
9820        //     entirely so let's keep this lightweight too),
9821        //   * left-recursive precedence loops (the precedence transition's
9822        //     gating is dynamic),
9823        //   * states with too few alternatives to benefit.
9824        let lookahead_filter = if transition_count > 1
9825            && self.fast_first_set_prefilter
9826            && !state.precedence_rule_decision()
9827            && (!self.fast_recovery_enabled || state.kind() != AtnStateKind::RuleStart)
9828        {
9829            state
9830                .rule_index()
9831                .and_then(|rule_index| atn.rule_to_stop_state().get(rule_index))
9832                .map(|rule_stop| {
9833                    let symbol = self.token_type_at(index);
9834                    let entry = self.cached_decision_lookahead(atn, state, rule_stop);
9835                    (symbol, entry)
9836                })
9837        } else {
9838            None
9839        };
9840        // LL(1) fast path: when the FIRST sets for the decision are disjoint
9841        // and none is nullable, the lookahead deterministically selects one
9842        // alternative. The recursive recognizer can then commit to that single
9843        // alt without iterating every transition through `should_skip_via_lookahead`
9844        // — saving (transition_count - 1) filter probes per visit.
9845        //
9846        // Result is cached per `(state, lookahead_token)` on the parser
9847        // instance, so subsequent visits skip the FIRST-set scan entirely.
9848        let ll1_only_alt: Option<usize> = if transition_count > 1
9849            && let Some((symbol, entry)) = lookahead_filter.as_ref()
9850        {
9851            let key = (state.state_number(), *symbol);
9852            if let Some(&cached) = self.ll1_decision_cache.get(&key) {
9853                cached
9854            } else {
9855                let result = ll1_unique_alt(entry, *symbol);
9856                self.ll1_decision_cache.insert(key, result);
9857                result
9858            }
9859        } else {
9860            None
9861        };
9862        let lookahead_filter = lookahead_filter.as_ref();
9863        // Pre-size only when we expect at least one outcome to land — most
9864        // single-transition fall-throughs (the loop above didn't catch
9865        // because they're atom/rule/predicate) push at most one entry, so
9866        // reserving one slot avoids a reallocation while keeping the
9867        // unused-slot waste at one element.
9868        let mut outcomes: Vec<FastRecognizeOutcome> = Vec::with_capacity(transition_count.min(2));
9869        for (transition_index, transition) in transitions.iter().enumerate() {
9870            if let Some(alt) = ll1_only_alt {
9871                // LL(1) determinism: skip every alt except the chosen one.
9872                if alt != transition_index {
9873                    continue;
9874                }
9875            }
9876            let transition_kind = transition.kind();
9877            if ll1_only_alt.is_none()
9878                && should_skip_via_lookahead(
9879                    transition_kind,
9880                    transition_index,
9881                    lookahead_filter,
9882                    index,
9883                    self.fast_recovery_enabled,
9884                    expected,
9885                )
9886            {
9887                continue;
9888            }
9889            let target = transition.target();
9890            let outcomes_before_transition = outcomes.len();
9891            let left_recursive_boundary = match transition_kind {
9892                ParserTransitionKind::Epsilon
9893                | ParserTransitionKind::Action
9894                | ParserTransitionKind::Predicate
9895                | ParserTransitionKind::Precedence => left_recursive_boundary(atn, state, target),
9896                ParserTransitionKind::Atom
9897                | ParserTransitionKind::Range
9898                | ParserTransitionKind::Set
9899                | ParserTransitionKind::NotSet
9900                | ParserTransitionKind::Wildcard
9901                | ParserTransitionKind::Rule => None,
9902            };
9903            match transition_kind {
9904                ParserTransitionKind::Epsilon | ParserTransitionKind::Action => {
9905                    #[cfg(feature = "perf-counters")]
9906                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9907                    outcomes.extend(self.recognize_state_fast(
9908                        atn,
9909                        FastRecognizeRequest {
9910                            state_number: target,
9911                            stop_state,
9912                            index,
9913                            rule_start_index,
9914                            decision_start_index: next_decision_start_index,
9915                            precedence,
9916                            depth: depth + 1,
9917                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9918                            recovery_state: epsilon_recovery_state,
9919                        },
9920                        FastRecognizeScratch {
9921                            predicate_context,
9922                            visiting,
9923                            memo,
9924                            expected,
9925                            native_depth: native_depth + 1,
9926                        },
9927                    ));
9928                }
9929                ParserTransitionKind::Predicate => {
9930                    #[cfg(feature = "perf-counters")]
9931                    perf_counters::inc(&perf_counters::EPSILON_TRANSITIONS, 1);
9932                    if self.fast_parser_predicate_matches(predicate_context, transition, index) {
9933                        outcomes.extend(self.recognize_state_fast(
9934                            atn,
9935                            FastRecognizeRequest {
9936                                state_number: target,
9937                                stop_state,
9938                                index,
9939                                rule_start_index,
9940                                decision_start_index: next_decision_start_index,
9941                                precedence,
9942                                depth: depth + 1,
9943                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9944                                recovery_state: epsilon_recovery_state,
9945                            },
9946                            FastRecognizeScratch {
9947                                predicate_context,
9948                                visiting,
9949                                memo,
9950                                expected,
9951                                native_depth: native_depth + 1,
9952                            },
9953                        ));
9954                    } else {
9955                        record_predicate_no_viable(expected, next_decision_start_index, index);
9956                    }
9957                }
9958                ParserTransitionKind::Precedence => {
9959                    let transition_precedence = packed_i32(transition.arg0());
9960                    if transition_precedence >= precedence {
9961                        outcomes.extend(self.recognize_state_fast(
9962                            atn,
9963                            FastRecognizeRequest {
9964                                state_number: target,
9965                                stop_state,
9966                                index,
9967                                rule_start_index,
9968                                decision_start_index: next_decision_start_index,
9969                                precedence,
9970                                depth: depth + 1,
9971                                recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
9972                                recovery_state: epsilon_recovery_state,
9973                            },
9974                            FastRecognizeScratch {
9975                                predicate_context,
9976                                visiting,
9977                                memo,
9978                                expected,
9979                                native_depth: native_depth + 1,
9980                            },
9981                        ));
9982                    }
9983                }
9984                ParserTransitionKind::Rule => {
9985                    let rule_index = transition.arg0() as usize;
9986                    let follow_state = transition.arg1() as usize;
9987                    let rule_precedence = packed_i32(transition.arg2());
9988                    #[cfg(feature = "perf-counters")]
9989                    perf_counters::inc(&perf_counters::RULE_TRANSITIONS, 1);
9990                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
9991                        continue;
9992                    };
9993                    // Lookahead-based pruning. The recognizer would otherwise
9994                    // explore every speculative rule call, producing exponential
9995                    // work on grammars with many epsilon-reachable rules. When
9996                    // the rule is non-nullable and its FIRST set excludes the
9997                    // current lookahead, recursion can't find a clean path
9998                    // *through this rule*. Skipping is only safe if some sibling
9999                    // transition can still consume the lookahead — otherwise the
10000                    // rule call is the sole continuation and must run so the
10001                    // single-token insertion / deletion recovery inside the
10002                    // called rule can fire (mirroring ANTLR's reference behavior
10003                    // of conjuring a missing token at child-rule entry).
10004                    let symbol = self.token_type_at(index);
10005                    if self.fast_first_set_prefilter {
10006                        // Probe the shared cross-parse cache first; build
10007                        // the entry on miss and intern it there. The
10008                        // computation is purely a function of the ATN, so
10009                        // the cached entry is reused across parses (and
10010                        // freshly-instantiated parser values that share
10011                        // the same `&'static Atn`).
10012                        //
10013                        // `rule_first_set` returns the computed entry
10014                        // directly — it intentionally skips inserting into
10015                        // the cache when the FIRST-set walk hit a cycle, so
10016                        // we cannot assume the entry is in the cache after
10017                        // computing it.
10018                        let first = self.cached_rule_first_set(atn, target, child_stop);
10019                        if should_skip_rule_via_first_set(
10020                            &first,
10021                            symbol,
10022                            self.fast_recovery_enabled,
10023                            index,
10024                            expected,
10025                        ) {
10026                            continue;
10027                        }
10028                    }
10029                    let expected_before_child =
10030                        self.fast_recovery_enabled.then(|| expected.clone());
10031                    let mut children = self.recognize_state_fast(
10032                        atn,
10033                        FastRecognizeRequest {
10034                            state_number: target,
10035                            stop_state: child_stop,
10036                            index,
10037                            rule_start_index: index,
10038                            decision_start_index: None,
10039                            precedence: rule_precedence,
10040                            depth: depth + 1,
10041                            recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
10042                            recovery_state: epsilon_recovery_state,
10043                        },
10044                        FastRecognizeScratch {
10045                            predicate_context,
10046                            visiting,
10047                            memo,
10048                            expected,
10049                            native_depth: native_depth + 1,
10050                        },
10051                    );
10052                    if children.is_empty() && self.fast_recovery_enabled {
10053                        children = self.fast_child_rule_failure_recovery_outcomes(
10054                            FastChildRuleFailureRecoveryRequest {
10055                                atn,
10056                                rule_index,
10057                                start_index: index,
10058                                follow_state,
10059                                stop_state,
10060                                expected,
10061                            },
10062                        );
10063                    }
10064                    if let Some(expected_before_child) = expected_before_child {
10065                        if children
10066                            .iter()
10067                            .any(|child| child.diagnostics.is_empty() && child.index > index)
10068                        {
10069                            *expected = expected_before_child;
10070                        }
10071                    }
10072                    for child in children {
10073                        let child_index = child.index;
10074                        let child_consumed_eof = child.consumed_eof;
10075                        let child_diagnostics = child.diagnostics;
10076                        let empty_recovery = self.empty_recovery_symbols();
10077                        let follow_outcomes = self.recognize_state_fast(
10078                            atn,
10079                            FastRecognizeRequest {
10080                                state_number: follow_state,
10081                                stop_state,
10082                                index: child_index,
10083                                rule_start_index,
10084                                decision_start_index: next_decision_start_index,
10085                                precedence,
10086                                depth: depth + 1,
10087                                recovery_symbols: empty_recovery,
10088                                recovery_state: None,
10089                            },
10090                            FastRecognizeScratch {
10091                                predicate_context,
10092                                visiting,
10093                                memo,
10094                                expected,
10095                                native_depth: native_depth + 1,
10096                            },
10097                        );
10098                        if follow_outcomes.is_empty() {
10099                            continue;
10100                        }
10101                        let child_stop_index =
10102                            self.rule_stop_token_index(child_index, child_consumed_eof);
10103                        let child_node = self.build_parse_trees.then(|| {
10104                            self.recognition_arena.deferred_rule_node(FastDeferredRule {
10105                                rule_index: u32::try_from(rule_index)
10106                                    .expect("rule index fits in u32"),
10107                                invoking_state: i32::try_from(invoking_state_number(state_number))
10108                                    .expect("invoking state fits in i32"),
10109                                start_index: u32::try_from(index)
10110                                    .expect("rule start index fits in u32"),
10111                                stop_index: child_stop_index.map(|stop_index| {
10112                                    u32::try_from(stop_index).expect("rule stop index fits in u32")
10113                                }),
10114                                deferred_children: child.deferred_nodes,
10115                                children: child.nodes,
10116                            })
10117                        });
10118                        let child_diags_empty = child_diagnostics.is_empty();
10119                        outcomes.extend(follow_outcomes.into_iter().map(|mut outcome| {
10120                            outcome.consumed_eof |= child_consumed_eof;
10121                            // Skip the prepend dance when there's nothing to
10122                            // merge from the child — common case in pass 1.
10123                            if !child_diags_empty {
10124                                outcome.diagnostics = self
10125                                    .recognition_arena
10126                                    .concat_diagnostics(child_diagnostics, outcome.diagnostics);
10127                            }
10128                            if let Some(child_node) = child_node {
10129                                outcome.deferred_nodes = self
10130                                    .recognition_arena
10131                                    .concat_deferred_nodes(child_node, outcome.deferred_nodes);
10132                            }
10133                            outcome
10134                        }));
10135                    }
10136                }
10137                ParserTransitionKind::Atom
10138                | ParserTransitionKind::Range
10139                | ParserTransitionKind::Set
10140                | ParserTransitionKind::NotSet
10141                | ParserTransitionKind::Wildcard => {
10142                    #[cfg(feature = "perf-counters")]
10143                    perf_counters::inc(&perf_counters::ATOM_RANGE_TRANSITIONS, 1);
10144                    let symbol = self.token_type_at(index);
10145                    if transition.matches_kind(transition_kind, symbol, 1, max_token_type) {
10146                        let next_index = self.consume_index(index, symbol);
10147                        let empty_recovery = self.empty_recovery_symbols();
10148                        outcomes.extend(
10149                            self.recognize_state_fast(
10150                                atn,
10151                                FastRecognizeRequest {
10152                                    state_number: target,
10153                                    stop_state,
10154                                    index: next_index,
10155                                    rule_start_index,
10156                                    decision_start_index: next_decision_start_index,
10157                                    precedence,
10158                                    depth: depth + 1,
10159                                    recovery_symbols: empty_recovery,
10160                                    recovery_state: None,
10161                                },
10162                                FastRecognizeScratch {
10163                                    predicate_context,
10164                                    visiting,
10165                                    memo,
10166                                    expected,
10167                                    native_depth: native_depth + 1,
10168                                },
10169                            )
10170                            .into_iter()
10171                            .map(|mut outcome| {
10172                                outcome.consumed_eof |= symbol == TOKEN_EOF;
10173                                if self.fast_token_nodes_enabled {
10174                                    let token = self.arena_token_node(index, false);
10175                                    self.defer_fast_outcome_node(&mut outcome, token);
10176                                }
10177                                outcome
10178                            }),
10179                        );
10180                    } else {
10181                        if !self.fast_recovery_enabled {
10182                            // In pass 1 there is no recovery to attempt; the
10183                            // recovery branch below would never run, and the
10184                            // `expected_symbols` computation is just there
10185                            // to gate that branch. Skipping it eliminates
10186                            // ~1× `state_expected_symbols` lookup per failed
10187                            // atom transition (≈82K on mono-statement.cs)
10188                            // for zero observable behavior change.
10189                            continue;
10190                        }
10191                        let expected_symbols = fast_recovery_expected_symbols(
10192                            self,
10193                            atn,
10194                            state.state_number(),
10195                            &recovery_symbols,
10196                        );
10197                        if expected_symbols.contains(&symbol) {
10198                            continue;
10199                        }
10200                        {
10201                            expected.record_transition(index, transition, max_token_type);
10202                            record_no_viable_if_ambiguous(
10203                                expected,
10204                                next_decision_start_index,
10205                                index,
10206                            );
10207                            outcomes.extend(self.fast_single_token_deletion_recovery(
10208                                FastRecoveryRequest {
10209                                    atn,
10210                                    transition,
10211                                    expected_symbols: Rc::clone(&expected_symbols),
10212                                    target,
10213                                    request: FastRecognizeRequest {
10214                                        state_number,
10215                                        stop_state,
10216                                        index,
10217                                        rule_start_index,
10218                                        decision_start_index,
10219                                        precedence,
10220                                        depth,
10221                                        recovery_symbols: Rc::clone(&recovery_symbols),
10222                                        recovery_state,
10223                                    },
10224                                    visiting,
10225                                    memo,
10226                                    expected,
10227                                },
10228                                predicate_context,
10229                            ));
10230                            if !state_is_left_recursive_rule(atn, state) {
10231                                outcomes.extend(self.fast_single_token_insertion_recovery(
10232                                    FastRecoveryRequest {
10233                                        atn,
10234                                        transition,
10235                                        expected_symbols: Rc::clone(&expected_symbols),
10236                                        target,
10237                                        request: FastRecognizeRequest {
10238                                            state_number,
10239                                            stop_state,
10240                                            index,
10241                                            rule_start_index,
10242                                            decision_start_index,
10243                                            precedence,
10244                                            depth,
10245                                            recovery_symbols: Rc::clone(&recovery_symbols),
10246                                            recovery_state,
10247                                        },
10248                                        visiting,
10249                                        memo,
10250                                        expected,
10251                                    },
10252                                    predicate_context,
10253                                ));
10254                            }
10255                            outcomes.extend(self.fast_current_token_deletion_recovery(
10256                                FastCurrentTokenDeletionRequest {
10257                                    atn,
10258                                    expected_symbols,
10259                                    request: FastRecognizeRequest {
10260                                        state_number,
10261                                        stop_state,
10262                                        index,
10263                                        rule_start_index,
10264                                        decision_start_index,
10265                                        precedence,
10266                                        depth,
10267                                        recovery_symbols: Rc::clone(&recovery_symbols),
10268                                        recovery_state,
10269                                    },
10270                                    visiting,
10271                                    memo,
10272                                    expected,
10273                                },
10274                                predicate_context,
10275                            ));
10276                        }
10277                    }
10278                }
10279            }
10280            let alt_number = next_alt_number(
10281                state,
10282                transition_count,
10283                transition_index,
10284                0,
10285                self.fast_track_alt_numbers,
10286            );
10287            if alt_number != 0 || left_recursive_boundary.is_some() {
10288                for outcome in &mut outcomes[outcomes_before_transition..] {
10289                    if alt_number != 0 {
10290                        self.defer_fast_outcome_alternative(outcome, alt_number);
10291                    }
10292                    if let Some(rule_index) = left_recursive_boundary {
10293                        self.defer_fast_outcome_boundary(outcome, rule_index);
10294                    }
10295                }
10296            }
10297        }
10298
10299        if has_inserted_cycle_guard {
10300            visiting.remove(&key);
10301        }
10302        if matches!(
10303            self.prediction_mode,
10304            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
10305        ) && self.fast_recovery_enabled
10306        {
10307            // Without recovery enabled every outcome already has empty
10308            // diagnostics, so the discard pass is a no-op — skipping it
10309            // saves an iter+retain on each of the ~1M visits.
10310            discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
10311        }
10312        if self.fast_recovery_enabled {
10313            dedupe_fast_outcomes(&mut outcomes, &self.recognition_arena);
10314        } else {
10315            dedupe_clean_fast_outcomes(&mut outcomes, &mut self.fast_outcome_dedup);
10316        }
10317        // Skip memoization for single-transition states whose outcome is
10318        // unambiguous: they only get re-entered if the caller revisits the
10319        // exact same call site, which is rare since the loop above already
10320        // collapsed straight-line epsilon walks. Multi-alternative states
10321        // are where backtracking actually revisits the same coordinate, so
10322        // we still memoize there. With recovery on we keep the existing
10323        // memoization unconditionally because the recovery branch may
10324        // record diagnostics that the cache must surface to repeated
10325        // failed visits.
10326        let should_memoize = self.fast_recovery_enabled
10327            || (transition_count > 1 && self.clean_memo_mode != CleanMemoMode::Sparse);
10328        // Apply inline pending state to each outcome before returning.
10329        // Tokens consumed inline by the loop-collapse don't appear in the
10330        // recursive recognizer's output, so we need to prepend them here.
10331        let mut apply_inline_pending = |mut outcome: FastRecognizeOutcome| -> FastRecognizeOutcome {
10332            if inline_consumed_eof {
10333                outcome.consumed_eof = true;
10334            }
10335            if !inline_consumed_tokens.is_empty() {
10336                for token_index in inline_consumed_tokens.iter().rev() {
10337                    let token = self.arena_token_node(*token_index, false);
10338                    self.defer_fast_outcome_node(&mut outcome, token);
10339                }
10340            }
10341            outcome
10342        };
10343        if should_memoize {
10344            #[cfg(feature = "perf-counters")]
10345            {
10346                perf_counters::inc(&perf_counters::MEMO_INSERTED, 1);
10347                perf_counters::inc(&perf_counters::OUTCOMES_PUSHED, outcomes.len() as u64);
10348                match outcomes.len() {
10349                    0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
10350                    1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
10351                    _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
10352                }
10353            }
10354            // The memo is keyed by the loop-exit `(state_number, index)` so
10355            // the inline-consumed tokens belong to *this* call's output, not
10356            // the cached result. Memoize the bare outcomes (without the
10357            // inline-pending data), then prepend the inline data on return.
10358            let stored: Rc<[FastRecognizeOutcome]> = Rc::from(outcomes);
10359            memo.insert(key, Rc::clone(&stored));
10360            if inline_pending {
10361                return stored
10362                    .iter()
10363                    .copied()
10364                    .map(&mut apply_inline_pending)
10365                    .collect();
10366            }
10367            return stored.to_vec();
10368        }
10369        #[cfg(feature = "perf-counters")]
10370        match outcomes.len() {
10371            0 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_0, 1),
10372            1 => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_1, 1),
10373            _ => perf_counters::inc(&perf_counters::OUTCOMES_RETURN_N, 1),
10374        }
10375        if inline_pending {
10376            return outcomes.into_iter().map(apply_inline_pending).collect();
10377        }
10378        outcomes
10379    }
10380
10381    /// Explores single-token deletion recovery while preserving the matched
10382    /// token and skipped error token in the selected parse tree path.
10383    fn single_token_deletion_recovery(
10384        &mut self,
10385        recovery: RecoveryRequest<'_, '_>,
10386    ) -> Vec<RecognizeOutcome> {
10387        let RecoveryRequest {
10388            atn,
10389            transition,
10390            expected_symbols,
10391            target,
10392            request,
10393            visiting,
10394            memo,
10395            expected,
10396        } = recovery;
10397        let RecognizeRequest {
10398            stop_state,
10399            index,
10400            rule_start_index,
10401            decision_start_index,
10402            init_action_rules,
10403            predicates,
10404            semantics,
10405            rule_args,
10406            member_actions,
10407            return_actions,
10408            local_int_arg,
10409            member_values,
10410            return_values,
10411            rule_alt_number,
10412            track_alt_numbers,
10413            consumed_eof,
10414            precedence,
10415            depth,
10416            ..
10417        } = request;
10418        let Some((diagnostic, next_index, next_symbol)) =
10419            self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
10420        else {
10421            return Vec::new();
10422        };
10423        let after_next = self.consume_index(next_index, next_symbol);
10424        self.recognize_state(
10425            atn,
10426            RecognizeRequest {
10427                state_number: target,
10428                stop_state,
10429                index: after_next,
10430                rule_start_index,
10431                decision_start_index,
10432                init_action_rules,
10433                predicates,
10434                semantics,
10435                rule_args,
10436                member_actions,
10437                return_actions,
10438                local_int_arg,
10439                member_values,
10440                return_values,
10441                rule_alt_number,
10442                track_alt_numbers,
10443                consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
10444                committed_decision: false,
10445                precedence,
10446                depth: depth + 1,
10447                recovery_symbols: BTreeSet::new(),
10448                recovery_state: None,
10449            },
10450            visiting,
10451            memo,
10452            expected,
10453        )
10454        .into_iter()
10455        .map(|mut outcome| {
10456            outcome.consumed_eof |= next_symbol == TOKEN_EOF;
10457            outcome.diagnostics = self
10458                .recognition_arena
10459                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
10460            let token = self.arena_token_node(next_index, false);
10461            self.arena_prepend(&mut outcome.nodes, token);
10462            let error = self.arena_token_node(index, true);
10463            self.arena_prepend(&mut outcome.nodes, error);
10464            outcome
10465        })
10466        .collect()
10467    }
10468
10469    /// Retries the current recognition state after deleting one unexpected
10470    /// token, preserving the deleted token as an error node in the parse tree.
10471    fn current_token_deletion_recovery(
10472        &mut self,
10473        recovery: CurrentTokenDeletionRequest<'_, '_>,
10474    ) -> Vec<RecognizeOutcome> {
10475        let CurrentTokenDeletionRequest {
10476            atn,
10477            expected_symbols,
10478            mut request,
10479            visiting,
10480            memo,
10481            expected,
10482        } = recovery;
10483        let error_index = request.index;
10484        if error_index == request.rule_start_index {
10485            return Vec::new();
10486        }
10487        let Some((diagnostic, next_index, skipped)) =
10488            self.current_token_deletion(error_index, &expected_symbols)
10489        else {
10490            return Vec::new();
10491        };
10492        request.state_number = request.recovery_state.unwrap_or(request.state_number);
10493        request.index = next_index;
10494        request.committed_decision = false;
10495        request.depth += 1;
10496        request.recovery_state = None;
10497        self.recognize_state(atn, request, visiting, memo, expected)
10498            .into_iter()
10499            .map(|mut outcome| {
10500                outcome.diagnostics = self
10501                    .recognition_arena
10502                    .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
10503                for index in skipped.iter().rev() {
10504                    let error = self.arena_token_node(*index, true);
10505                    self.arena_prepend(&mut outcome.nodes, error);
10506                }
10507                outcome
10508            })
10509            .collect()
10510    }
10511
10512    /// Falls back after deletion/insertion repairs cannot continue from a
10513    /// failed consuming transition.
10514    fn consuming_failure_fallback(
10515        &mut self,
10516        fallback: ConsumingFailureFallback<'_>,
10517        visiting: &mut BTreeSet<RecognizeKey>,
10518        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
10519        expected: &mut ExpectedTokens,
10520    ) -> Vec<RecognizeOutcome> {
10521        if fallback.expected_symbols.is_empty() {
10522            return Vec::new();
10523        }
10524        if fallback.symbol == TOKEN_EOF {
10525            return self.eof_consuming_failure_fallback(fallback, expected);
10526        }
10527        self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
10528    }
10529
10530    /// Keeps unexpected non-EOF input visible as an error node when no repair
10531    /// path can otherwise reach the transition target.
10532    fn non_eof_consuming_failure_fallback(
10533        &mut self,
10534        fallback: ConsumingFailureFallback<'_>,
10535        visiting: &mut BTreeSet<RecognizeKey>,
10536        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
10537        expected: &mut ExpectedTokens,
10538    ) -> Vec<RecognizeOutcome> {
10539        let ConsumingFailureFallback {
10540            atn,
10541            target,
10542            request,
10543            symbol,
10544            expected_symbols,
10545            decision_start_index,
10546            decision,
10547        } = fallback;
10548        let error_index = request.index;
10549        let diagnostic =
10550            self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
10551        let next_index = self.consume_index(error_index, symbol);
10552        self.recognize_state(
10553            atn,
10554            RecognizeRequest {
10555                state_number: target,
10556                stop_state: request.stop_state,
10557                index: next_index,
10558                rule_start_index: request.rule_start_index,
10559                decision_start_index,
10560                init_action_rules: request.init_action_rules,
10561                predicates: request.predicates,
10562                semantics: request.semantics,
10563                rule_args: request.rule_args,
10564                member_actions: request.member_actions,
10565                return_actions: request.return_actions,
10566                local_int_arg: request.local_int_arg,
10567                member_values: request.member_values,
10568                return_values: request.return_values,
10569                rule_alt_number: request.rule_alt_number,
10570                track_alt_numbers: request.track_alt_numbers,
10571                consumed_eof: request.consumed_eof,
10572                committed_decision: false,
10573                precedence: request.precedence,
10574                depth: request.depth + 1,
10575                recovery_symbols: BTreeSet::new(),
10576                recovery_state: None,
10577            },
10578            visiting,
10579            memo,
10580            expected,
10581        )
10582        .into_iter()
10583        .map(|mut outcome| {
10584            prepend_decision(&mut outcome, decision);
10585            outcome.diagnostics = self
10586                .recognition_arena
10587                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
10588            let error = self.arena_token_node(error_index, true);
10589            self.arena_prepend(&mut outcome.nodes, error);
10590            outcome
10591        })
10592        .collect()
10593    }
10594
10595    /// Stops the current rule at EOF after a nested failure, matching ANTLR's
10596    /// behavior of unwinding instead of inserting caller tokens at EOF.
10597    fn eof_consuming_failure_fallback(
10598        &mut self,
10599        fallback: ConsumingFailureFallback<'_>,
10600        expected: &ExpectedTokens,
10601    ) -> Vec<RecognizeOutcome> {
10602        let request = fallback.request;
10603        if request.index == request.rule_start_index {
10604            return Vec::new();
10605        }
10606        let diagnostic =
10607            self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
10608        let diagnostics = self
10609            .recognition_arena
10610            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
10611        vec![RecognizeOutcome {
10612            index: request.index,
10613            consumed_eof: request.consumed_eof,
10614            alt_number: request.rule_alt_number,
10615            member_values: request.member_values,
10616            return_values: request.return_values,
10617            diagnostics,
10618            decisions: Vec::new(),
10619            actions: Vec::new(),
10620            nodes: NodeSeqId::EMPTY,
10621        }]
10622    }
10623
10624    /// Explores single-token insertion recovery while adding a conjured
10625    /// missing-token error node to the selected parse tree path.
10626    fn single_token_insertion_recovery(
10627        &mut self,
10628        recovery: RecoveryRequest<'_, '_>,
10629    ) -> Vec<RecognizeOutcome> {
10630        let RecoveryRequest {
10631            atn,
10632            transition,
10633            expected_symbols,
10634            target,
10635            request,
10636            visiting,
10637            memo,
10638            expected,
10639        } = recovery;
10640        let RecognizeRequest {
10641            stop_state,
10642            index,
10643            rule_start_index,
10644            decision_start_index,
10645            init_action_rules,
10646            predicates,
10647            semantics,
10648            rule_args,
10649            member_actions,
10650            return_actions,
10651            local_int_arg,
10652            member_values,
10653            return_values,
10654            rule_alt_number,
10655            track_alt_numbers,
10656            consumed_eof,
10657            precedence,
10658            depth,
10659            ..
10660        } = request;
10661        let follow_symbols = state_expected_symbols(atn, transition.target());
10662        let Some((diagnostic, token_type, text)) = self.single_token_insertion(
10663            transition,
10664            index,
10665            atn.max_token_type(),
10666            &expected_symbols,
10667            &follow_symbols,
10668        ) else {
10669            return Vec::new();
10670        };
10671        self.recognize_state(
10672            atn,
10673            RecognizeRequest {
10674                state_number: target,
10675                stop_state,
10676                index,
10677                rule_start_index,
10678                decision_start_index,
10679                init_action_rules,
10680                predicates,
10681                semantics,
10682                rule_args,
10683                member_actions,
10684                return_actions,
10685                local_int_arg,
10686                member_values,
10687                return_values,
10688                rule_alt_number,
10689                track_alt_numbers,
10690                consumed_eof,
10691                committed_decision: false,
10692                precedence,
10693                depth: depth + 1,
10694                recovery_symbols: BTreeSet::new(),
10695                recovery_state: None,
10696            },
10697            visiting,
10698            memo,
10699            expected,
10700        )
10701        .into_iter()
10702        .map(|mut outcome| {
10703            outcome.diagnostics = self
10704                .recognition_arena
10705                .prepend_diagnostic(outcome.diagnostics, diagnostic.clone());
10706            let missing = self.arena_missing_token_node(token_type, index, text.clone());
10707            self.arena_prepend(&mut outcome.nodes, missing);
10708            outcome
10709        })
10710        .collect()
10711    }
10712
10713    /// Attempts to reach `stop_state` and carries semantic actions for the
10714    /// selected parser path.
10715    #[allow(clippy::too_many_lines)]
10716    fn recognize_state(
10717        &mut self,
10718        atn: &Atn,
10719        request: RecognizeRequest<'_>,
10720        visiting: &mut BTreeSet<RecognizeKey>,
10721        memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
10722        expected: &mut ExpectedTokens,
10723    ) -> Vec<RecognizeOutcome> {
10724        let request_template = request.clone();
10725        let RecognizeRequest {
10726            state_number,
10727            stop_state,
10728            index,
10729            rule_start_index,
10730            decision_start_index,
10731            init_action_rules,
10732            predicates,
10733            semantics,
10734            rule_args,
10735            member_actions,
10736            return_actions,
10737            local_int_arg,
10738            member_values,
10739            return_values,
10740            rule_alt_number,
10741            track_alt_numbers,
10742            consumed_eof,
10743            committed_decision,
10744            precedence,
10745            depth,
10746            recovery_symbols,
10747            recovery_state,
10748        } = request;
10749        if depth > RECOGNITION_DEPTH_LIMIT {
10750            return Vec::new();
10751        }
10752        if state_number == stop_state {
10753            return stop_outcome(
10754                index,
10755                consumed_eof,
10756                rule_alt_number,
10757                member_values,
10758                return_values,
10759            );
10760        }
10761        let key = RecognizeKey {
10762            state_number,
10763            stop_state,
10764            index,
10765            rule_start_index,
10766            decision_start_index,
10767            local_int_arg,
10768            member_values: member_values.clone(),
10769            return_values: return_values.clone(),
10770            rule_alt_number,
10771            track_alt_numbers,
10772            consumed_eof,
10773            committed_decision,
10774            precedence,
10775            recovery_symbols: recovery_symbols.clone(),
10776            recovery_state,
10777        };
10778        if let Some(outcomes) = memo.get(&key) {
10779            return outcomes.clone();
10780        }
10781
10782        let visit_key = key.clone();
10783        if !visiting.insert(visit_key.clone()) {
10784            return Vec::new();
10785        }
10786
10787        let Some(state) = atn.state(state_number) else {
10788            visiting.remove(&visit_key);
10789            return Vec::new();
10790        };
10791        let decision_override_generation = self.decision_override_generation;
10792        let transitions = state.transitions();
10793        let transition_count = transitions.len();
10794        let overridden_transition = if transition_count > 1
10795            && self.semantic_hooks.observes_parser_decisions()
10796        {
10797            atn.decision_to_state()
10798                .iter()
10799                .position(|candidate| candidate == state_number)
10800                .and_then(|decision| {
10801                    self.semantic_hooks
10802                        .parser_decision_override(decision, index, transition_count)
10803                })
10804                .and_then(|alternative| alternative.checked_sub(1))
10805                .filter(|alternative| *alternative < transition_count)
10806        } else {
10807            None
10808        };
10809        if overridden_transition.is_some() {
10810            self.decision_override_generation = self.decision_override_generation.wrapping_add(1);
10811        }
10812        let next_decision_start_index = if starts_prediction_decision(state, transition_count) {
10813            Some(index)
10814        } else {
10815            decision_start_index
10816        };
10817        let (epsilon_recovery_symbols, epsilon_recovery_state) =
10818            next_recovery_context(atn, state, &recovery_symbols, recovery_state);
10819        let mut outcomes = Vec::new();
10820        for (transition_index, transition) in transitions.iter().enumerate() {
10821            if overridden_transition.is_some_and(|forced| forced != transition_index) {
10822                continue;
10823            }
10824            let transition_committed =
10825                committed_decision || overridden_transition == Some(transition_index);
10826            let mut transition_request = request_template.clone();
10827            transition_request.committed_decision = transition_committed;
10828            let decision =
10829                transition_decision(atn, state, transition_count, transition_index, predicates);
10830            let next_alt_number = next_alt_number(
10831                state,
10832                transition_count,
10833                transition_index,
10834                rule_alt_number,
10835                track_alt_numbers,
10836            );
10837            let transition_data = transition.data();
10838            match &transition_data {
10839                Transition::Epsilon { target } | Transition::Action { target, .. } => {
10840                    let (action_rule_index, action_index) = match &transition_data {
10841                        Transition::Action {
10842                            rule_index,
10843                            action_index,
10844                            ..
10845                        } => (Some(*rule_index), *action_index),
10846                        _ => (None, None),
10847                    };
10848                    outcomes.extend(self.recognize_epsilon_or_action_step(
10849                        atn,
10850                        &transition_request,
10851                        EpsilonActionStep {
10852                            source_state: state_number,
10853                            target: *target,
10854                            action_rule_index,
10855                            action_index,
10856                            left_recursive_boundary: left_recursive_boundary(atn, state, *target),
10857                            decision,
10858                            decision_start_index: next_decision_start_index,
10859                            alt_number: next_alt_number,
10860                            recovery_symbols: epsilon_recovery_symbols.clone(),
10861                            recovery_state: epsilon_recovery_state,
10862                        },
10863                        RecognizeScratch {
10864                            visiting,
10865                            memo,
10866                            expected,
10867                        },
10868                    ));
10869                }
10870                Transition::Predicate {
10871                    target,
10872                    rule_index,
10873                    pred_index,
10874                    ..
10875                } => {
10876                    let predicate = PredicateEval {
10877                        index,
10878                        rule_index: *rule_index,
10879                        pred_index: *pred_index,
10880                        predicates,
10881                        semantics,
10882                        context: None,
10883                        local_int_arg,
10884                        member_values: &member_values,
10885                    };
10886                    if self.parser_predicate_matches(predicate) {
10887                        let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
10888                        outcomes.extend(
10889                            self.recognize_state(
10890                                atn,
10891                                RecognizeRequest {
10892                                    state_number: *target,
10893                                    stop_state,
10894                                    index,
10895                                    rule_start_index,
10896                                    decision_start_index: next_decision_start_index,
10897                                    init_action_rules,
10898                                    predicates,
10899                                    semantics,
10900                                    rule_args,
10901                                    member_actions,
10902                                    return_actions,
10903                                    local_int_arg,
10904                                    member_values: member_values.clone(),
10905                                    return_values: return_values.clone(),
10906                                    rule_alt_number: next_alt_number,
10907                                    track_alt_numbers,
10908                                    consumed_eof,
10909                                    committed_decision: transition_committed,
10910                                    precedence,
10911                                    depth: depth + 1,
10912                                    recovery_symbols: epsilon_recovery_symbols.clone(),
10913                                    recovery_state: epsilon_recovery_state,
10914                                },
10915                                visiting,
10916                                memo,
10917                                expected,
10918                            )
10919                            .into_iter()
10920                            .map(|mut outcome| {
10921                                prepend_decision(&mut outcome, decision);
10922                                if let Some(rule_index) = left_recursive_boundary {
10923                                    let boundary =
10924                                        self.arena_boundary_node(rule_index, next_alt_number);
10925                                    self.arena_prepend(&mut outcome.nodes, boundary);
10926                                }
10927                                outcome
10928                            }),
10929                        );
10930                    } else if let Some(message) = semantics
10931                        .and_then(|semantics| {
10932                            self.parser_semantic_ir_predicate_failure_message(
10933                                *rule_index,
10934                                *pred_index,
10935                                semantics,
10936                            )
10937                        })
10938                        .or_else(|| {
10939                            self.parser_predicate_failure_message(
10940                                *rule_index,
10941                                *pred_index,
10942                                predicates,
10943                            )
10944                        })
10945                    {
10946                        outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
10947                            rule_index: *rule_index,
10948                            index,
10949                            message,
10950                            member_values: member_values.clone(),
10951                            return_values: return_values.clone(),
10952                            rule_alt_number,
10953                        }));
10954                    } else {
10955                        record_predicate_no_viable(expected, next_decision_start_index, index);
10956                    }
10957                }
10958                Transition::Precedence {
10959                    target,
10960                    precedence: transition_precedence,
10961                } => {
10962                    if *transition_precedence >= precedence {
10963                        outcomes.extend(
10964                            self.recognize_state(
10965                                atn,
10966                                RecognizeRequest {
10967                                    state_number: *target,
10968                                    stop_state,
10969                                    index,
10970                                    rule_start_index,
10971                                    decision_start_index: next_decision_start_index,
10972                                    init_action_rules,
10973                                    predicates,
10974                                    semantics,
10975                                    rule_args,
10976                                    member_actions,
10977                                    return_actions,
10978                                    local_int_arg,
10979                                    member_values: member_values.clone(),
10980                                    return_values: return_values.clone(),
10981                                    rule_alt_number: next_alt_number,
10982                                    track_alt_numbers,
10983                                    consumed_eof,
10984                                    committed_decision: transition_committed,
10985                                    precedence,
10986                                    depth: depth + 1,
10987                                    recovery_symbols: epsilon_recovery_symbols.clone(),
10988                                    recovery_state: epsilon_recovery_state,
10989                                },
10990                                visiting,
10991                                memo,
10992                                expected,
10993                            )
10994                            .into_iter()
10995                            .map(|mut outcome| {
10996                                prepend_decision(&mut outcome, decision);
10997                                outcome
10998                            }),
10999                        );
11000                    }
11001                }
11002                Transition::Rule {
11003                    target,
11004                    rule_index,
11005                    follow_state,
11006                    precedence: rule_precedence,
11007                    ..
11008                } => {
11009                    let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index) else {
11010                        continue;
11011                    };
11012                    let child_local_int_arg =
11013                        rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
11014                    let expected_before_child = expected.clone();
11015                    let children = self.recognize_state(
11016                        atn,
11017                        RecognizeRequest {
11018                            state_number: *target,
11019                            stop_state: child_stop,
11020                            index,
11021                            rule_start_index: index,
11022                            decision_start_index: None,
11023                            init_action_rules,
11024                            predicates,
11025                            semantics,
11026                            rule_args,
11027                            member_actions,
11028                            return_actions,
11029                            local_int_arg: child_local_int_arg,
11030                            member_values: member_values.clone(),
11031                            return_values: BTreeMap::new(),
11032                            rule_alt_number: 0,
11033                            track_alt_numbers,
11034                            consumed_eof: false,
11035                            committed_decision: transition_committed,
11036                            precedence: *rule_precedence,
11037                            depth: depth + 1,
11038                            recovery_symbols: epsilon_recovery_symbols.clone(),
11039                            recovery_state: epsilon_recovery_state,
11040                        },
11041                        visiting,
11042                        memo,
11043                        expected,
11044                    );
11045                    let children = if children.is_empty() {
11046                        self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
11047                            atn,
11048                            rule_index: *rule_index,
11049                            start_index: index,
11050                            follow_state: *follow_state,
11051                            stop_state,
11052                            member_values: member_values.clone(),
11053                            expected,
11054                        })
11055                    } else {
11056                        children
11057                    };
11058                    let preserve_child_expected =
11059                        self.child_expected_reaches_clean_eof(&children, expected);
11060                    restore_expected(
11061                        &children,
11062                        index,
11063                        expected,
11064                        expected_before_child,
11065                        preserve_child_expected,
11066                    );
11067                    for child in children {
11068                        let child_stop_index =
11069                            self.rule_stop_token_index(child.index, child.consumed_eof);
11070                        let child_nodes = self
11071                            .recognition_arena
11072                            .fold_left_recursive_boundaries(child.nodes);
11073                        let child_node = self.arena_rule_node(ArenaRuleSpec {
11074                            rule_index: *rule_index,
11075                            invoking_state: invoking_state_number(state_number),
11076                            alt_number: child.alt_number,
11077                            start_index: index,
11078                            stop_index: child_stop_index,
11079                            return_values: child.return_values.clone(),
11080                            children: child_nodes,
11081                        });
11082                        outcomes.extend(
11083                            self.recognize_state(
11084                                atn,
11085                                RecognizeRequest {
11086                                    state_number: *follow_state,
11087                                    stop_state,
11088                                    index: child.index,
11089                                    rule_start_index,
11090                                    decision_start_index: next_decision_start_index,
11091                                    init_action_rules,
11092                                    predicates,
11093                                    semantics,
11094                                    rule_args,
11095                                    member_actions,
11096                                    return_actions,
11097                                    local_int_arg,
11098                                    member_values: child.member_values.clone(),
11099                                    return_values: return_values.clone(),
11100                                    rule_alt_number,
11101                                    track_alt_numbers,
11102                                    consumed_eof: consumed_eof || child.consumed_eof,
11103                                    committed_decision: transition_committed
11104                                        && child.index == index,
11105                                    precedence,
11106                                    depth: depth + 1,
11107                                    recovery_symbols: BTreeSet::new(),
11108                                    recovery_state: None,
11109                                },
11110                                visiting,
11111                                memo,
11112                                expected,
11113                            )
11114                            .into_iter()
11115                            .map(|mut outcome| {
11116                                outcome.consumed_eof |= child.consumed_eof;
11117                                outcome.diagnostics = self
11118                                    .recognition_arena
11119                                    .concat_diagnostics(child.diagnostics, outcome.diagnostics);
11120                                let mut decisions = child.decisions.clone();
11121                                decisions.append(&mut outcome.decisions);
11122                                outcome.decisions = decisions;
11123                                prepend_decision(&mut outcome, decision);
11124                                let mut actions = child.actions.clone();
11125                                if init_action_rules.contains(rule_index) {
11126                                    actions.insert(
11127                                        0,
11128                                        ParserAction::new_rule_init(
11129                                            *rule_index,
11130                                            index,
11131                                            Some(*follow_state),
11132                                        ),
11133                                    );
11134                                }
11135                                actions.append(&mut outcome.actions);
11136                                outcome.actions = actions;
11137                                self.arena_prepend(&mut outcome.nodes, child_node);
11138                                outcome
11139                            }),
11140                        );
11141                    }
11142                }
11143                Transition::Atom { target, .. }
11144                | Transition::Range { target, .. }
11145                | Transition::Set { target, .. }
11146                | Transition::NotSet { target, .. }
11147                | Transition::Wildcard { target, .. } => {
11148                    let symbol = self.token_type_at(index);
11149                    if transition_data.matches(symbol, 1, atn.max_token_type()) {
11150                        let next_index = self.consume_index(index, symbol);
11151                        outcomes.extend(
11152                            self.recognize_state(
11153                                atn,
11154                                RecognizeRequest {
11155                                    state_number: *target,
11156                                    stop_state,
11157                                    index: next_index,
11158                                    rule_start_index,
11159                                    decision_start_index: next_decision_start_index,
11160                                    init_action_rules,
11161                                    predicates,
11162                                    semantics,
11163                                    rule_args,
11164                                    member_actions,
11165                                    return_actions,
11166                                    local_int_arg,
11167                                    member_values: member_values.clone(),
11168                                    return_values: return_values.clone(),
11169                                    rule_alt_number: next_alt_number,
11170                                    track_alt_numbers,
11171                                    consumed_eof: consumed_eof || symbol == TOKEN_EOF,
11172                                    committed_decision: false,
11173                                    precedence,
11174                                    depth: depth + 1,
11175                                    recovery_symbols: BTreeSet::new(),
11176                                    recovery_state: None,
11177                                },
11178                                visiting,
11179                                memo,
11180                                expected,
11181                            )
11182                            .into_iter()
11183                            .map(|mut outcome| {
11184                                prepend_decision(&mut outcome, decision);
11185                                outcome.consumed_eof |= symbol == TOKEN_EOF;
11186                                let token = self.arena_token_node(index, false);
11187                                self.arena_prepend(&mut outcome.nodes, token);
11188                                outcome
11189                            }),
11190                        );
11191                    } else {
11192                        let expected_symbols =
11193                            recovery_expected_symbols(atn, state.state_number(), &recovery_symbols);
11194                        if expected_symbols.contains(&symbol) && !transition_committed {
11195                            continue;
11196                        }
11197                        expected.record_transition(index, transition, atn.max_token_type());
11198                        record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
11199                        let before_recovery = outcomes.len();
11200                        let recovery_request = transition_request.clone();
11201                        if transition_committed {
11202                            outcomes.extend(self.consuming_failure_fallback(
11203                                ConsumingFailureFallback {
11204                                    atn,
11205                                    target: *target,
11206                                    request: recovery_request,
11207                                    symbol,
11208                                    expected_symbols,
11209                                    decision_start_index: next_decision_start_index,
11210                                    decision,
11211                                },
11212                                visiting,
11213                                memo,
11214                                expected,
11215                            ));
11216                            break;
11217                        }
11218                        outcomes.extend(
11219                            self.single_token_deletion_recovery(RecoveryRequest {
11220                                atn,
11221                                transition,
11222                                expected_symbols: expected_symbols.clone(),
11223                                target: *target,
11224                                request: recovery_request.clone(),
11225                                visiting,
11226                                memo,
11227                                expected,
11228                            })
11229                            .into_iter()
11230                            .map(|mut outcome| {
11231                                prepend_decision(&mut outcome, decision);
11232                                outcome
11233                            }),
11234                        );
11235                        if !state_is_left_recursive_rule(atn, state) {
11236                            outcomes.extend(
11237                                self.single_token_insertion_recovery(RecoveryRequest {
11238                                    atn,
11239                                    transition,
11240                                    expected_symbols: expected_symbols.clone(),
11241                                    target: *target,
11242                                    request: recovery_request.clone(),
11243                                    visiting,
11244                                    memo,
11245                                    expected,
11246                                })
11247                                .into_iter()
11248                                .map(|mut outcome| {
11249                                    prepend_decision(&mut outcome, decision);
11250                                    outcome
11251                                }),
11252                            );
11253                        }
11254                        outcomes.extend(self.current_token_deletion_recovery(
11255                            CurrentTokenDeletionRequest {
11256                                atn,
11257                                expected_symbols: expected_symbols.clone(),
11258                                request: recovery_request.clone(),
11259                                visiting,
11260                                memo,
11261                                expected,
11262                            },
11263                        ));
11264                        if outcomes.len() == before_recovery {
11265                            outcomes.extend(self.consuming_failure_fallback(
11266                                ConsumingFailureFallback {
11267                                    atn,
11268                                    target: *target,
11269                                    request: recovery_request,
11270                                    symbol,
11271                                    expected_symbols,
11272                                    decision_start_index: next_decision_start_index,
11273                                    decision,
11274                                },
11275                                visiting,
11276                                memo,
11277                                expected,
11278                            ));
11279                        }
11280                    }
11281                }
11282            }
11283            if self.decision_override_generation != decision_override_generation {
11284                break;
11285            }
11286        }
11287
11288        visiting.remove(&visit_key);
11289        self.record_prediction_diagnostics(atn, state, index, &outcomes);
11290        if matches!(
11291            self.prediction_mode,
11292            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
11293        ) {
11294            discard_recovered_outcomes_if_clean_path_exists(&mut outcomes, &self.recognition_arena);
11295        }
11296        dedupe_outcomes(&mut outcomes, &self.recognition_arena);
11297        memo.insert(key, outcomes.clone());
11298        outcomes
11299    }
11300
11301    /// Follows an epsilon or semantic-action transition while preserving the
11302    /// path-local side effects that may later become generated action output.
11303    fn recognize_epsilon_or_action_step(
11304        &mut self,
11305        atn: &Atn,
11306        request: &RecognizeRequest<'_>,
11307        step: EpsilonActionStep,
11308        scratch: RecognizeScratch<'_>,
11309    ) -> Vec<RecognizeOutcome> {
11310        let RecognizeScratch {
11311            visiting,
11312            memo,
11313            expected,
11314        } = scratch;
11315        let action = step.action_rule_index.map(|rule_index| {
11316            let stop_index = self.rule_stop_token_index(request.index, request.consumed_eof);
11317            step.action_index.map_or_else(
11318                || {
11319                    ParserAction::new(
11320                        step.source_state,
11321                        rule_index,
11322                        request.rule_start_index,
11323                        stop_index,
11324                    )
11325                },
11326                |action_index| {
11327                    ParserAction::new_indexed(
11328                        step.source_state,
11329                        rule_index,
11330                        action_index,
11331                        request.rule_start_index,
11332                        stop_index,
11333                    )
11334                },
11335            )
11336        });
11337        let next_member_values = if action.is_some() {
11338            member_values_after_action(
11339                step.source_state,
11340                request.member_actions,
11341                request.semantics,
11342                &request.member_values,
11343            )
11344        } else {
11345            request.member_values.clone()
11346        };
11347        let next_return_values = action.map_or_else(
11348            || request.return_values.clone(),
11349            |action| {
11350                return_values_after_action(
11351                    step.source_state,
11352                    action.rule_index(),
11353                    request.return_actions,
11354                    request.semantics,
11355                    &request.return_values,
11356                )
11357            },
11358        );
11359
11360        self.recognize_state(
11361            atn,
11362            RecognizeRequest {
11363                state_number: step.target,
11364                stop_state: request.stop_state,
11365                index: request.index,
11366                rule_start_index: request.rule_start_index,
11367                decision_start_index: step.decision_start_index,
11368                init_action_rules: request.init_action_rules,
11369                predicates: request.predicates,
11370                semantics: request.semantics,
11371                rule_args: request.rule_args,
11372                member_actions: request.member_actions,
11373                return_actions: request.return_actions,
11374                local_int_arg: request.local_int_arg,
11375                member_values: next_member_values,
11376                return_values: next_return_values,
11377                rule_alt_number: if step.left_recursive_boundary.is_some() {
11378                    0
11379                } else {
11380                    step.alt_number
11381                },
11382                track_alt_numbers: request.track_alt_numbers,
11383                consumed_eof: request.consumed_eof,
11384                committed_decision: request.committed_decision,
11385                precedence: request.precedence,
11386                depth: request.depth + 1,
11387                recovery_symbols: step.recovery_symbols,
11388                recovery_state: step.recovery_state,
11389            },
11390            visiting,
11391            memo,
11392            expected,
11393        )
11394        .into_iter()
11395        .map(|mut outcome| {
11396            prepend_decision(&mut outcome, step.decision);
11397            if let Some(rule_index) = step.left_recursive_boundary {
11398                let boundary = self.arena_boundary_node(rule_index, step.alt_number);
11399                self.arena_prepend(&mut outcome.nodes, boundary);
11400            }
11401            if let Some(action) = action {
11402                outcome.actions.insert(0, action);
11403            }
11404            outcome
11405        })
11406        .collect()
11407    }
11408
11409    /// Reads the token type at an absolute token-stream index without moving
11410    /// the parser's stream cursor. The fast recognizer probes lookahead at
11411    /// every state visit, so avoiding the seek round-trip is a measurable
11412    /// hot-path win on long inputs.
11413    fn token_type_at(&mut self, index: usize) -> i32 {
11414        if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !self.input.is_filled() {
11415            self.input.fill();
11416        }
11417        self.input.token_type_at_index(index)
11418    }
11419
11420    /// Returns the cached `state_expected_symbols` set for an ATN state.
11421    ///
11422    /// The fast recognizer consults this set on every state visit through
11423    /// `next_recovery_context`; the underlying DFS is a pure function of the
11424    /// ATN, so caching the `Rc` lets clones reduce to a reference bump.
11425    ///
11426    /// Caching is layered through `intern_recovery_symbols` so two ATN states
11427    /// with the same expected-symbol set share one `Rc`. That invariant is
11428    /// what lets `FastRecognizeKey` hash on `recovery_symbols` by pointer
11429    /// without violating the `Hash`/`Eq` contract — `recovery_symbols` is
11430    /// always interned before it ends up in a key.
11431    fn cached_state_expected_symbols(
11432        &mut self,
11433        atn: &Atn,
11434        state_number: usize,
11435    ) -> Rc<BTreeSet<i32>> {
11436        if let Some(cached) = self.state_expected_cache.get(&state_number) {
11437            return Rc::clone(cached);
11438        }
11439        let symbols = state_expected_symbols(atn, state_number);
11440        let entry = self.intern_recovery_symbols(symbols);
11441        self.state_expected_cache
11442            .insert(state_number, Rc::clone(&entry));
11443        entry
11444    }
11445
11446    fn cached_state_expected_token_set(
11447        &mut self,
11448        atn: &Atn,
11449        state_number: usize,
11450    ) -> Rc<TokenBitSet> {
11451        if let Some(cached) = self.state_expected_token_cache.get(&state_number) {
11452            return Rc::clone(cached);
11453        }
11454        // Purely a function of the ATN, so back the per-parser cache with the
11455        // thread-shared one — fresh parser instances (one per parse in
11456        // generated usage) start warm instead of rewalking the ATN.
11457        let symbols = with_shared_atn_caches(atn, |cache| {
11458            if let Some(cached) = cache.state_expected_tokens.get(&state_number) {
11459                return Rc::clone(cached);
11460            }
11461            let symbols = Rc::new(state_expected_token_set(atn, state_number));
11462            cache
11463                .state_expected_tokens
11464                .insert(state_number, Rc::clone(&symbols));
11465            symbols
11466        });
11467        self.state_expected_token_cache
11468            .insert(state_number, Rc::clone(&symbols));
11469        symbols
11470    }
11471
11472    fn cached_state_can_reach_rule_stop(&mut self, atn: &Atn, state_number: usize) -> bool {
11473        if self.rule_stop_reach_cache.len() <= state_number {
11474            self.rule_stop_reach_cache
11475                .resize_with(atn.states().len().max(state_number + 1), || None);
11476        }
11477        if let Some(reaches) = self.rule_stop_reach_cache[state_number] {
11478            return reaches;
11479        }
11480        let reaches = with_shared_atn_caches(atn, |cache| {
11481            *cache
11482                .rule_stop_reach
11483                .entry(state_number)
11484                .or_insert_with(|| state_can_reach_rule_stop(atn, state_number))
11485        });
11486        self.rule_stop_reach_cache[state_number] = Some(reaches);
11487        reaches
11488    }
11489
11490    /// Returns the parser's empty `recovery_symbols` singleton so callers can
11491    /// share an `Rc` instead of allocating new `BTreeSet`s for the common case.
11492    fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
11493        Rc::clone(&self.empty_recovery_symbols)
11494    }
11495
11496    /// Returns the interned `Rc` form of a `recovery_symbols` set so the fast
11497    /// recognizer can hash and compare keys by pointer.
11498    ///
11499    /// Every `Rc<BTreeSet<i32>>` that flows into a `FastRecognizeKey` must
11500    /// come from this method or the empty singleton; otherwise two
11501    /// content-equal `Rc`s could end up with different `Rc::as_ptr` values,
11502    /// and the pointer-keyed hash on `FastRecognizeKey` would split equivalent
11503    /// recognition coordinates.
11504    fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
11505        if set.is_empty() {
11506            return Rc::clone(&self.empty_recovery_symbols);
11507        }
11508        let candidate = Rc::new(set);
11509        match self.recovery_symbols_intern.get(&candidate) {
11510            Some(existing) => Rc::clone(existing),
11511            None => {
11512                self.recovery_symbols_intern
11513                    .insert(Rc::clone(&candidate), Rc::clone(&candidate));
11514                candidate
11515            }
11516        }
11517    }
11518
11519    /// Returns the cached look-1 entry for a decision state, computing it on
11520    /// first use. Multi-alternative states are visited many times during
11521    /// recognition; sharing the entry through `Rc` keeps the prefilter to one
11522    /// hash lookup per visit.
11523    fn cached_decision_lookahead(
11524        &mut self,
11525        atn: &Atn,
11526        state: AtnState<'_>,
11527        rule_stop_state: usize,
11528    ) -> Rc<DecisionLookahead> {
11529        // Hit the parser-instance cache first. Decision lookahead is purely
11530        // a function of the ATN/state, so on a warm cache we skip the
11531        // thread-local + RefCell + HashMap-entry dance through
11532        // SHARED_ATN_CACHES — which on multi-trans-heavy grammars (C# does
11533        // ~58K multi-trans visits per parse) shows up as RefCell borrow and
11534        // hashmap-entry overhead in profiles.
11535        if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number()) {
11536            return Rc::clone(cached);
11537        }
11538        let entry = with_shared_atn_caches(atn, |cache| {
11539            if let Some(cached) = cache.decision_lookahead.get(&state.state_number()) {
11540                return Rc::clone(cached);
11541            }
11542            let mut entry = DecisionLookahead {
11543                transitions: Vec::with_capacity(state.transitions().len()),
11544            };
11545            for transition in &state.transitions() {
11546                entry.transitions.push(transition_first_set(
11547                    atn,
11548                    transition,
11549                    rule_stop_state,
11550                    &mut cache.first_set,
11551                ));
11552            }
11553            let entry = Rc::new(entry);
11554            cache
11555                .decision_lookahead
11556                .insert(state.state_number(), Rc::clone(&entry));
11557            entry
11558        });
11559        self.decision_lookahead_cache
11560            .insert(state.state_number(), Rc::clone(&entry));
11561        entry
11562    }
11563
11564    fn cached_rule_first_set(
11565        &mut self,
11566        atn: &Atn,
11567        target: usize,
11568        child_stop: usize,
11569    ) -> Rc<FirstSet> {
11570        if self.rule_first_set_cache.len() <= target {
11571            self.rule_first_set_cache
11572                .resize_with(atn.states().len().max(target + 1), || None);
11573        }
11574        if let Some(cached) = self
11575            .rule_first_set_cache
11576            .get(target)
11577            .and_then(Option::as_ref)
11578        {
11579            return Rc::clone(cached);
11580        }
11581        let first = with_shared_first_set_cache(atn, |cache| {
11582            rule_first_set(atn, target, child_stop, cache)
11583        });
11584        self.rule_first_set_cache[target] = Some(Rc::clone(&first));
11585        first
11586    }
11587
11588    fn state_can_reenter_without_consuming(&mut self, atn: &Atn, state_number: usize) -> bool {
11589        let atn_key = SharedAtnCacheKey::for_atn(atn);
11590        if self.empty_cycle_cache_atn != Some(atn_key) {
11591            self.empty_cycle_cache.clear();
11592            self.empty_cycle_cache_atn = Some(atn_key);
11593        }
11594        if self.empty_cycle_cache.len() <= state_number {
11595            self.empty_cycle_cache
11596                .resize_with(atn.state_count().max(state_number + 1), || None);
11597        }
11598        if let Some(cached) = self.empty_cycle_cache[state_number] {
11599            return cached;
11600        }
11601        let mut visited = FxHashSet::with_capacity_and_hasher(64, FxBuildHasher::default());
11602        let result = self.empty_path_reaches_state(atn, state_number, state_number, &mut visited);
11603        self.empty_cycle_cache[state_number] = Some(result);
11604        result
11605    }
11606
11607    fn empty_path_reaches_state(
11608        &mut self,
11609        atn: &Atn,
11610        state_number: usize,
11611        target_state: usize,
11612        visited: &mut FxHashSet<usize>,
11613    ) -> bool {
11614        enum Work {
11615            Visit(usize),
11616            RuleFollow {
11617                target: usize,
11618                rule_index: usize,
11619                follow_state: usize,
11620            },
11621        }
11622
11623        let mut work = vec![Work::Visit(state_number)];
11624        while let Some(item) = work.pop() {
11625            match item {
11626                Work::Visit(state_number) => {
11627                    if !visited.insert(state_number) {
11628                        continue;
11629                    }
11630                    let Some(state) = atn.state(state_number) else {
11631                        continue;
11632                    };
11633                    let transitions = state.transitions();
11634                    for transition_index in (0..transitions.len()).rev() {
11635                        let transition = transitions
11636                            .get(transition_index)
11637                            .expect("in-bounds parser transition");
11638                        let kind = transition.kind();
11639                        let target = transition.target();
11640                        match kind {
11641                            ParserTransitionKind::Atom
11642                            | ParserTransitionKind::Range
11643                            | ParserTransitionKind::Set
11644                            | ParserTransitionKind::NotSet
11645                            | ParserTransitionKind::Wildcard => {}
11646                            ParserTransitionKind::Rule => {
11647                                if target == target_state {
11648                                    return true;
11649                                }
11650                                work.push(Work::RuleFollow {
11651                                    target,
11652                                    rule_index: transition.arg0() as usize,
11653                                    follow_state: transition.arg1() as usize,
11654                                });
11655                                work.push(Work::Visit(target));
11656                            }
11657                            ParserTransitionKind::Epsilon
11658                            | ParserTransitionKind::Predicate
11659                            | ParserTransitionKind::Action
11660                            | ParserTransitionKind::Precedence => {
11661                                if target == target_state {
11662                                    return true;
11663                                }
11664                                work.push(Work::Visit(target));
11665                            }
11666                        }
11667                    }
11668                }
11669                Work::RuleFollow {
11670                    target,
11671                    rule_index,
11672                    follow_state,
11673                } => {
11674                    let Some(child_stop) = atn.rule_to_stop_state().get(rule_index) else {
11675                        continue;
11676                    };
11677                    if self.cached_rule_first_set(atn, target, child_stop).nullable {
11678                        if follow_state == target_state {
11679                            return true;
11680                        }
11681                        work.push(Work::Visit(follow_state));
11682                    }
11683                }
11684            }
11685        }
11686        false
11687    }
11688
11689    /// Decides whether the clean recognizer should use its full outcome memo
11690    /// table for this coordinate.
11691    fn clean_memo_enabled_for_key(&mut self, key: &FastRecognizeKey) -> bool {
11692        match self.clean_memo_mode {
11693            CleanMemoMode::Promote => true,
11694            CleanMemoMode::Probe => self.observe_clean_memo_probe(key),
11695            CleanMemoMode::Sparse => {
11696                self.clean_memo_sparse_samples += 1;
11697                if self.clean_memo_sparse_samples < CLEAN_MEMO_REPROBE_INTERVAL {
11698                    return false;
11699                }
11700                self.clean_memo_sparse_samples = 0;
11701                self.clean_memo_mode = CleanMemoMode::Probe;
11702                self.clean_memo_probe_samples = 0;
11703                self.clean_memo_probe_repeats = 0;
11704                self.clean_memo_probe_seen.clear();
11705                self.observe_clean_memo_probe(key)
11706            }
11707        }
11708    }
11709
11710    fn observe_clean_memo_probe(&mut self, key: &FastRecognizeKey) -> bool {
11711        self.clean_memo_probe_samples += 1;
11712        if !self.clean_memo_probe_seen.insert(key.clone()) {
11713            self.clean_memo_probe_repeats += 1;
11714        }
11715        if self.clean_memo_probe_repeats >= CLEAN_MEMO_REPEAT_LIMIT {
11716            self.clean_memo_mode = CleanMemoMode::Promote;
11717            self.clean_memo_probe_seen.clear();
11718            return true;
11719        }
11720        if self.clean_memo_probe_samples >= CLEAN_MEMO_PROBE_LIMIT {
11721            self.clean_memo_mode = CleanMemoMode::Sparse;
11722            self.clean_memo_sparse_samples = 0;
11723            self.clean_memo_probe_seen.clear();
11724            return false;
11725        }
11726        true
11727    }
11728
11729    /// Borrows the visible token at an absolute token-stream index.
11730    fn token_at(&self, index: usize) -> Option<TokenView<'_>> {
11731        self.input.get(index)
11732    }
11733
11734    /// Returns the compact token ID at an absolute token-stream index.
11735    fn token_id_at(&self, index: usize) -> Option<TokenId> {
11736        self.input.get_id(index)
11737    }
11738
11739    fn arena_token_node(&mut self, index: usize, error: bool) -> RecognizedNodeId {
11740        let token = self
11741            .token_id_at(index)
11742            .expect("recognized token index must exist in the token store");
11743        let node = if error {
11744            ArenaRecognizedNode::ErrorToken { token }
11745        } else {
11746            ArenaRecognizedNode::Token { token }
11747        };
11748        self.recognition_arena.push_node(node)
11749    }
11750
11751    fn arena_missing_token_node(
11752        &mut self,
11753        token_type: i32,
11754        at_index: usize,
11755        text: String,
11756    ) -> RecognizedNodeId {
11757        let extra = self
11758            .recognition_arena
11759            .push_extra(RecognitionExtra::MissingToken {
11760                token_type,
11761                at_index: u32::try_from(at_index).expect("missing-token stream index fits in u32"),
11762                text,
11763            });
11764        self.recognition_arena
11765            .push_node(ArenaRecognizedNode::MissingToken { extra })
11766    }
11767
11768    fn arena_rule_node(&mut self, spec: ArenaRuleSpec) -> RecognizedNodeId {
11769        let ArenaRuleSpec {
11770            rule_index,
11771            invoking_state,
11772            alt_number,
11773            start_index,
11774            stop_index,
11775            return_values,
11776            children,
11777        } = spec;
11778        let return_values = (!return_values.is_empty()).then(|| {
11779            self.recognition_arena
11780                .push_extra(RecognitionExtra::ReturnValues(return_values))
11781        });
11782        self.recognition_arena.push_node(ArenaRecognizedNode::Rule {
11783            rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
11784            invoking_state: i32::try_from(invoking_state).expect("invoking state fits in i32"),
11785            alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
11786            start_index: u32::try_from(start_index).expect("rule start index fits in u32"),
11787            stop_index: stop_index
11788                .map(|index| u32::try_from(index).expect("rule stop index fits in u32")),
11789            return_values,
11790            children,
11791        })
11792    }
11793
11794    fn arena_boundary_node(&mut self, rule_index: usize, alt_number: usize) -> RecognizedNodeId {
11795        self.recognition_arena
11796            .push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
11797                rule_index: u32::try_from(rule_index).expect("rule index fits in u32"),
11798                alt_number: u32::try_from(alt_number).expect("alternative number fits in u32"),
11799            })
11800    }
11801
11802    fn arena_prepend(&mut self, sequence: &mut NodeSeqId, node: RecognizedNodeId) {
11803        *sequence = self.recognition_arena.prepend(*sequence, node);
11804    }
11805
11806    // The perf-counters branch reads the process environment, so this cannot
11807    // become const even when Clippy analyzes the branch-free configuration.
11808    #[allow(clippy::missing_const_for_fn)]
11809    fn finish_recognition_arena(&mut self, root: NodeSeqId, diagnostics: DiagnosticSeqId) {
11810        self.last_recognition_arena_root = root;
11811        self.last_recognition_arena_diagnostics = diagnostics;
11812        #[cfg(feature = "perf-counters")]
11813        if std::env::var("ANTLR_PERF_DUMP").is_ok() {
11814            let stats = self.recognition_arena_stats();
11815            #[allow(clippy::print_stderr)]
11816            {
11817                eprintln!("perf recognition_nodes_total={}", stats.total_nodes);
11818                eprintln!("perf recognition_nodes_live={}", stats.live_nodes);
11819                eprintln!("perf recognition_nodes_dead={}", stats.dead_nodes);
11820                eprintln!("perf recognition_nodes_capacity={}", stats.node_capacity);
11821                eprintln!("perf recognition_links_total={}", stats.total_links);
11822                eprintln!("perf recognition_links_live={}", stats.live_links);
11823                eprintln!("perf recognition_links_dead={}", stats.dead_links);
11824                eprintln!("perf recognition_links_capacity={}", stats.link_capacity);
11825                eprintln!("perf recognition_extras_total={}", stats.total_extras);
11826                eprintln!("perf recognition_extras_live={}", stats.live_extras);
11827                eprintln!("perf recognition_extras_dead={}", stats.dead_extras);
11828                eprintln!("perf recognition_extras_capacity={}", stats.extra_capacity);
11829            }
11830        }
11831    }
11832
11833    fn reset_recognition_arena(&mut self) {
11834        self.recognition_arena.reset();
11835        self.last_recognition_arena_root = NodeSeqId::EMPTY;
11836        self.last_recognition_arena_diagnostics = DiagnosticSeqId::EMPTY;
11837    }
11838
11839    /// Normalizes the current token-stream cursor to the next parser-visible
11840    /// token before capturing a rule start boundary.
11841    fn current_visible_index(&mut self) -> usize {
11842        let index = self.input.index();
11843        self.input.seek(index);
11844        self.input.index()
11845    }
11846
11847    /// Reports whether a child rule reached EOF cleanly while also recording
11848    /// an EOF expectation from a longer path inside that child.
11849    fn child_expected_reaches_clean_eof(
11850        &mut self,
11851        children: &[RecognizeOutcome],
11852        expected: &ExpectedTokens,
11853    ) -> bool {
11854        let Some(index) = expected.index else {
11855            return false;
11856        };
11857        self.token_type_at(index) == TOKEN_EOF
11858            && children
11859                .iter()
11860                .any(|child| child.diagnostics.is_empty() && child.index == index)
11861    }
11862
11863    /// Finds the previous token visible to the parser before `index`.
11864    ///
11865    /// The token stream cursor skips hidden-channel tokens, so subtracting one
11866    /// from a visible-token index can point at whitespace. Parser intervals use
11867    /// this helper to stop at the previous visible token while preserving hidden
11868    /// text inside the rendered interval.
11869    fn previous_token_index(&self, index: usize) -> Option<usize> {
11870        self.input.previous_visible_token_index(index)
11871    }
11872
11873    /// Returns the token-stream index used as a rule stop boundary.
11874    ///
11875    /// EOF transitions keep the cursor on EOF, so a rule that consumed EOF must
11876    /// stop at `index` rather than at the previous visible token.
11877    fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
11878        if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
11879            Some(index)
11880        } else {
11881            self.previous_token_index(index)
11882        }
11883    }
11884
11885    /// Stop-token index for a rule's `@after` action, matching the boundary that
11886    /// `finish_rule` records on the rule context.
11887    ///
11888    /// A rule that matched EOF leaves the cursor parked on the EOF token
11889    /// (`CommonTokenStream::consume` does not advance past EOF), so the stop is
11890    /// the current index rather than the previous visible token. Without this,
11891    /// `$stop`/`$text` in an `@after` action on a rule like `r: a* EOF;` would
11892    /// report the token before EOF (or `None` for empty input), diverging from
11893    /// the rule context that `finish_rule` builds.
11894    ///
11895    /// NOTE: this infers `consumed_eof` from the cursor, which is wrong when a
11896    /// rule ends right before EOF without matching it (the cursor is parked on
11897    /// EOF, but the rule did not consume it). Prefer
11898    /// [`Self::after_action_stop_index_for_tree`], which reuses the stop token the
11899    /// rule context already recorded with the real flag. Kept for callers without
11900    /// the rule tree in hand.
11901    #[must_use]
11902    pub fn after_action_stop_index(&mut self, current_index: usize) -> Option<usize> {
11903        let consumed_eof = self.token_type_at(current_index) == TOKEN_EOF;
11904        self.rule_stop_token_index(current_index, consumed_eof)
11905    }
11906
11907    /// Stop-token index for a rule's `@after` action, taken from the stop token
11908    /// the rule context already recorded.
11909    ///
11910    /// `finish_rule` computes the rule stop with the real `consumed_eof` flag, so
11911    /// reading it back keeps `$stop`/`$text` in an `@after` action aligned with
11912    /// the rule context — even when the rule ends immediately before EOF without
11913    /// matching it (cursor parked on EOF, but `consumed_eof` is false). Falls back
11914    /// to the cursor-based inference only when the tree carries no rule stop.
11915    #[must_use]
11916    pub fn after_action_stop_index_for_tree(
11917        &mut self,
11918        tree: ParseTree,
11919        current_index: usize,
11920    ) -> Option<usize> {
11921        if let Some(stop) = self
11922            .node(tree)
11923            .as_rule()
11924            .and_then(crate::tree::RuleNodeView::stop_id)
11925        {
11926            return Some(stop.index());
11927        }
11928        self.after_action_stop_index(current_index)
11929    }
11930
11931    /// Start-token index for a rule's `@after` action, taken from the start token
11932    /// the rule context already recorded.
11933    ///
11934    /// `enter_rule` sets the rule context start to the first visible token (it
11935    /// skips leading hidden-channel tokens), so reading it back keeps `$start` /
11936    /// `$text` in an `@after` action aligned with the rule context — even when the
11937    /// rule begins after a hidden prefix (e.g. leading whitespace) that the raw
11938    /// pre-rule cursor still points at. Falls back to `fallback_index` only when
11939    /// the tree carries no rule start.
11940    #[must_use]
11941    pub fn after_action_start_index_for_tree(
11942        &self,
11943        tree: ParseTree,
11944        fallback_index: usize,
11945    ) -> usize {
11946        if let Some(start) = self
11947            .node(tree)
11948            .as_rule()
11949            .and_then(crate::tree::RuleNodeView::start_id)
11950        {
11951            return start.index();
11952        }
11953        fallback_index
11954    }
11955
11956    /// Returns the rule stop token for a selected parse path.
11957    ///
11958    /// EOF transitions do not advance the token-stream cursor, so an EOF match
11959    /// must use the current token rather than the previous visible token.
11960    fn rule_stop_token_id(&mut self, index: usize, consumed_eof: bool) -> Option<TokenId> {
11961        self.rule_stop_token_index(index, consumed_eof)
11962            .and_then(|token_index| self.token_id_at(token_index))
11963    }
11964
11965    /// Recovers from a semantic predicate with an ANTLR `<fail='...'>` option.
11966    ///
11967    /// Generated Java reports the failed-predicate message at the current
11968    /// lookahead, then consumes until rule recovery can resume. The metadata
11969    /// runtime models the same visible tree shape by keeping skipped tokens as
11970    /// error nodes and returning from the active rule at EOF.
11971    fn predicate_failure_recovery(
11972        &mut self,
11973        request: PredicateFailureRecovery<'_>,
11974    ) -> RecognizeOutcome {
11975        let PredicateFailureRecovery {
11976            rule_index,
11977            index,
11978            message,
11979            member_values,
11980            return_values,
11981            rule_alt_number,
11982        } = request;
11983        let rule_name = self
11984            .rule_names()
11985            .get(rule_index)
11986            .map_or_else(|| rule_index.to_string(), Clone::clone);
11987        let diagnostic = diagnostic_for_token(
11988            self.token_at(index).as_ref(),
11989            format!("rule {rule_name} {message}"),
11990        );
11991        let mut reversed_nodes = NodeSeqId::EMPTY;
11992        let mut next_index = index;
11993        loop {
11994            let symbol = self.token_type_at(next_index);
11995            if symbol == TOKEN_EOF {
11996                break;
11997            }
11998            let error = self.arena_token_node(next_index, true);
11999            self.arena_prepend(&mut reversed_nodes, error);
12000            let after = self.consume_index(next_index, symbol);
12001            if after == next_index {
12002                break;
12003            }
12004            next_index = after;
12005        }
12006        let nodes = self.recognition_arena.reverse_sequence(reversed_nodes);
12007        let diagnostics = self
12008            .recognition_arena
12009            .prepend_diagnostic(DiagnosticSeqId::EMPTY, diagnostic);
12010        RecognizeOutcome {
12011            index: next_index,
12012            consumed_eof: false,
12013            alt_number: rule_alt_number,
12014            member_values,
12015            return_values,
12016            diagnostics,
12017            decisions: Vec::new(),
12018            actions: Vec::new(),
12019            nodes,
12020        }
12021    }
12022
12023    /// Evaluates a user hook for a predicate coordinate that has no generated
12024    /// runtime table entry.
12025    fn parser_semantic_hook_result(
12026        &mut self,
12027        request: ParserSemanticHookRequest<'_>,
12028    ) -> Option<bool> {
12029        let ParserSemanticHookRequest {
12030            index,
12031            rule_index,
12032            pred_index,
12033            context,
12034            local_int_arg,
12035            member_values,
12036        } = request;
12037        let rule_name = self.rule_names().get(rule_index).cloned();
12038        self.input.seek(index);
12039        let input = &mut self.input;
12040        let semantic_hooks = &mut self.semantic_hooks;
12041        let mut ctx = ParserSemCtx {
12042            input,
12043            tree_storage: &self.tree,
12044            rule_index,
12045            coordinate_index: pred_index,
12046            rule_name,
12047            context,
12048            tree: None,
12049            local_int_arg,
12050            member_values,
12051            action: None,
12052        };
12053        semantic_hooks.sempred(&mut ctx, rule_index, pred_index)
12054    }
12055
12056    /// Re-inserts unknown-predicate coordinates recorded before a nested
12057    /// interpreted recognition, preserving order and skipping any the nested
12058    /// call already recorded, so a generated parent's fail-loud coordinates
12059    /// survive descending into an interpreted child.
12060    fn restore_prior_unknown_predicate_hits(&mut self, prior: Vec<(usize, usize)>) {
12061        if prior.is_empty() {
12062            return;
12063        }
12064        let mut merged = prior;
12065        for coordinate in std::mem::take(&mut self.unknown_predicate_hits) {
12066            if !merged.contains(&coordinate) {
12067                merged.push(coordinate);
12068            }
12069        }
12070        self.unknown_predicate_hits = merged;
12071    }
12072
12073    /// Re-inserts unhandled action coordinates recorded before a nested
12074    /// committed parse so only that child parse's misses affect its result.
12075    fn restore_prior_unhandled_action_hits(&mut self, prior: Vec<(usize, usize)>) {
12076        if prior.is_empty() {
12077            return;
12078        }
12079        let mut merged = prior;
12080        for coordinate in std::mem::take(&mut self.unhandled_action_hits) {
12081            if !merged.contains(&coordinate) {
12082                merged.push(coordinate);
12083            }
12084        }
12085        self.unhandled_action_hits = merged;
12086    }
12087
12088    /// Applies the active [`UnknownSemanticPolicy`] to a predicate coordinate
12089    /// that has no entry in the generated predicate table.
12090    ///
12091    /// Under [`UnknownSemanticPolicy::Error`] the coordinate is recorded and
12092    /// the guarded path is abandoned; the parse entry surfaces the recorded
12093    /// coordinates as [`AntlrError::Unsupported`] once recognition finishes,
12094    /// because a parse that consulted an unknown predicate is unreliable no
12095    /// matter which paths were ultimately selected.
12096    fn unknown_predicate_result(&mut self, rule_index: usize, pred_index: usize) -> bool {
12097        apply_unknown_predicate_policy(
12098            self.unknown_predicate_policy,
12099            rule_index,
12100            pred_index,
12101            &mut self.unknown_predicate_hits,
12102        )
12103    }
12104
12105    /// Builds the fail-loud error for unknown predicate coordinates recorded
12106    /// by the current parse, if any.
12107    fn unknown_semantic_error(&self) -> Option<AntlrError> {
12108        use std::fmt::Write as _;
12109        if self.unknown_predicate_hits.is_empty() && self.unhandled_action_hits.is_empty() {
12110            return None;
12111        }
12112        let mut message = String::new();
12113        for (rule_index, pred_index) in &self.unknown_predicate_hits {
12114            if !message.is_empty() {
12115                message.push_str("; ");
12116            }
12117            let _ = match self.rule_names().get(*rule_index) {
12118                Some(rule_name) => write!(
12119                    message,
12120                    "unsupported semantic predicate: rule={rule_name}({rule_index}) pred_index={pred_index}"
12121                ),
12122                None => write!(
12123                    message,
12124                    "unsupported semantic predicate: rule_index={rule_index} pred_index={pred_index}"
12125                ),
12126            };
12127        }
12128        for (rule_index, source_state) in &self.unhandled_action_hits {
12129            if !message.is_empty() {
12130                message.push_str("; ");
12131            }
12132            let _ = match self.rule_names().get(*rule_index) {
12133                Some(rule_name) => write!(
12134                    message,
12135                    "unhandled semantic action: rule={rule_name}({rule_index}) state={source_state}"
12136                ),
12137                None => write!(
12138                    message,
12139                    "unhandled semantic action: rule_index={rule_index} state={source_state}"
12140                ),
12141            };
12142        }
12143        Some(AntlrError::Unsupported(message))
12144    }
12145
12146    /// Evaluates one lowered predicate expression at the requested input
12147    /// position.
12148    ///
12149    /// This sits in the prediction hot loop, so the context borrows the
12150    /// speculative member state read-only and the rule name by reference —
12151    /// no per-evaluation allocation. Only the hook escape path materializes
12152    /// owned copies, and only when a hook is actually consulted.
12153    fn parser_semir_predicate_matches(
12154        &mut self,
12155        semantics: &ParserSemantics,
12156        predicate: &ParserSemanticPredicate,
12157        request: ParserSemanticHookRequest<'_>,
12158    ) -> bool {
12159        self.input.seek(request.index);
12160        let rule_name = self
12161            .data
12162            .rule_names()
12163            .get(request.rule_index)
12164            .map(String::as_str);
12165        let unknown_predicate_policy = self.unknown_predicate_policy;
12166        let mut ctx = ParserSemIrCtx {
12167            input: &mut self.input,
12168            tree_storage: &self.tree,
12169            semantic_hooks: &mut self.semantic_hooks,
12170            rule_index: request.rule_index,
12171            coordinate_index: request.pred_index,
12172            rule_name,
12173            context: request.context,
12174            local_int_arg: request.local_int_arg,
12175            member_values: request.member_values,
12176            invoked_predicates: &mut self.invoked_predicates,
12177            unknown_predicate_policy,
12178            unknown_predicate_hits: &mut self.unknown_predicate_hits,
12179        };
12180        semir::eval_pred(&semantics.ir, predicate.expr, &mut ctx)
12181    }
12182
12183    fn fast_parser_predicate_matches(
12184        &mut self,
12185        context: Option<FastPredicateContext<'_>>,
12186        transition: ParserTransition<'_>,
12187        index: usize,
12188    ) -> bool {
12189        let Some(context) = context else {
12190            return true;
12191        };
12192        let rule_index = transition.arg0() as usize;
12193        let pred_index = transition.arg1() as usize;
12194        let key = (index, rule_index, pred_index);
12195        if let Some(result) = self.fast_predicate_cache.get(&key) {
12196            return *result;
12197        }
12198        let result = self.parser_predicate_matches(PredicateEval {
12199            index,
12200            rule_index,
12201            pred_index,
12202            predicates: context.predicates,
12203            semantics: context.semantics,
12204            context: None,
12205            local_int_arg: None,
12206            member_values: context.member_values,
12207        });
12208        self.fast_predicate_cache.insert(key, result);
12209        result
12210    }
12211
12212    fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
12213        let PredicateEval {
12214            index,
12215            rule_index,
12216            pred_index,
12217            predicates,
12218            semantics,
12219            context,
12220            local_int_arg,
12221            member_values,
12222        } = eval;
12223        if let Some((semantics, predicate)) = semantics.and_then(|semantics| {
12224            semantics
12225                .predicates
12226                .iter()
12227                .find(|predicate| {
12228                    predicate.rule_index == rule_index && predicate.pred_index == pred_index
12229                })
12230                .map(|predicate| (semantics, predicate))
12231        }) {
12232            return self.parser_semir_predicate_matches(
12233                semantics,
12234                predicate,
12235                ParserSemanticHookRequest {
12236                    index,
12237                    rule_index,
12238                    pred_index,
12239                    context,
12240                    local_int_arg,
12241                    member_values,
12242                },
12243            );
12244        }
12245        let Some((_, _, predicate)) = predicates
12246            .iter()
12247            .find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
12248        else {
12249            if let Some(result) = self.parser_semantic_hook_result(ParserSemanticHookRequest {
12250                index,
12251                rule_index,
12252                pred_index,
12253                context,
12254                local_int_arg,
12255                member_values,
12256            }) {
12257                return result;
12258            }
12259            return self.unknown_predicate_result(rule_index, pred_index);
12260        };
12261        self.input.seek(index);
12262        match predicate {
12263            ParserPredicate::True => true,
12264            ParserPredicate::False => false,
12265            ParserPredicate::FalseWithMessage { .. } => false,
12266            ParserPredicate::Invoke { value } => {
12267                let key = (rule_index, pred_index);
12268                if !self.invoked_predicates.contains(&key) {
12269                    self.invoked_predicates.push(key);
12270                    use std::io::Write as _;
12271                    let mut stdout = std::io::stdout().lock();
12272                    let _ = writeln!(stdout, "eval={value}");
12273                }
12274                *value
12275            }
12276            ParserPredicate::LookaheadTextEquals { offset, text } => self
12277                .input
12278                .lt(*offset)
12279                .is_some_and(|token| Token::text(&token) == Some(*text)),
12280            ParserPredicate::LookaheadNotEquals { offset, token_type } => {
12281                self.la(*offset) != *token_type
12282            }
12283            ParserPredicate::TokenPairAdjacent => {
12284                let Some(first) = self.input.lt_id(-2).map(TokenId::index) else {
12285                    return false;
12286                };
12287                let Some(second) = self.input.lt_id(-1).map(TokenId::index) else {
12288                    return false;
12289                };
12290                first + 1 == second
12291            }
12292            ParserPredicate::ContextChildRuleTextNotEquals { rule_index, text } => context
12293                .and_then(|context| {
12294                    context
12295                        .child_rules(&self.tree, self.input.token_store(), *rule_index)
12296                        .next()
12297                        .map(crate::tree::RuleNodeView::text)
12298                })
12299                .is_none_or(|actual| actual != *text),
12300            ParserPredicate::LocalIntEquals { value } => {
12301                local_int_arg.is_none_or(|(_, actual)| actual == *value)
12302            }
12303            ParserPredicate::LocalIntLessOrEqual { value } => {
12304                local_int_arg.is_none_or(|(_, actual)| actual <= *value)
12305            }
12306            ParserPredicate::MemberModuloEquals {
12307                member,
12308                modulus,
12309                value,
12310                equals,
12311            } => {
12312                if *modulus == 0 {
12313                    return false;
12314                }
12315                let actual = member_values.scalar(*member).unwrap_or_default() % *modulus;
12316                (actual == *value) == *equals
12317            }
12318            ParserPredicate::MemberEquals {
12319                member,
12320                value,
12321                equals,
12322            } => {
12323                let actual = member_values.scalar(*member).unwrap_or_default();
12324                (actual == *value) == *equals
12325            }
12326        }
12327    }
12328
12329    /// Returns a generated fail-option message for a predicate coordinate.
12330    fn parser_predicate_failure_message(
12331        &self,
12332        rule_index: usize,
12333        pred_index: usize,
12334        predicates: &[(usize, usize, ParserPredicate)],
12335    ) -> Option<&'static str> {
12336        predicates
12337            .iter()
12338            .find_map(|(rule, pred, predicate)| match predicate {
12339                ParserPredicate::FalseWithMessage { message }
12340                    if *rule == rule_index && *pred == pred_index =>
12341                {
12342                    Some(*message)
12343                }
12344                _ => None,
12345            })
12346    }
12347
12348    /// Returns a generated fail-option message for a `SemIR` predicate
12349    /// coordinate.
12350    pub fn parser_semantic_ir_predicate_failure_message(
12351        &self,
12352        rule_index: usize,
12353        pred_index: usize,
12354        semantics: &ParserSemantics,
12355    ) -> Option<&'static str> {
12356        semantics
12357            .predicates
12358            .iter()
12359            .find(|predicate| {
12360                predicate.rule_index == rule_index && predicate.pred_index == pred_index
12361            })
12362            .and_then(|predicate| predicate.failure_message)
12363    }
12364
12365    /// Returns the token-stream index after consuming `symbol` at `index`.
12366    ///
12367    /// EOF is not advanced by ANTLR token streams, so EOF transitions keep the
12368    /// index stable and rely on `consumed_eof` to record that EOF was matched.
12369    /// The parser's stream cursor is left untouched: speculative recognition
12370    /// reads ahead by absolute index, so paying for `seek` on every visited
12371    /// state would dominate the hot path. Real consumption is committed by
12372    /// `parse_atn_rule` via `seek` once a viable outcome is selected.
12373    fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
12374        if symbol == TOKEN_EOF {
12375            return index;
12376        }
12377        self.input.next_visible_after(index)
12378    }
12379
12380    /// Builds ANTLR's no-viable-alternative diagnostic for an ambiguous
12381    /// decision that failed after consuming a shared prefix.
12382    fn no_viable_alternative(&self, start_index: usize, error_index: usize) -> ParserDiagnostic {
12383        let text = display_input_text(&self.input.text(start_index, error_index));
12384        diagnostic_for_token(
12385            self.token_at(error_index).as_ref(),
12386            format!("no viable alternative at input '{text}'"),
12387        )
12388    }
12389
12390    /// Selects the diagnostic for a failed consuming transition after all
12391    /// recovery repairs have been ruled out.
12392    fn recovery_failure_diagnostic(
12393        &self,
12394        index: usize,
12395        decision_start_index: Option<usize>,
12396        expected_symbols: &BTreeSet<i32>,
12397    ) -> ParserDiagnostic {
12398        if expected_symbols.len() > 1 {
12399            if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
12400                return self.no_viable_alternative(decision_start, index);
12401            }
12402        }
12403        diagnostic_for_token(
12404            self.token_at(index).as_ref(),
12405            format!(
12406                "mismatched input {} expecting {}",
12407                self.token_at(index)
12408                    .as_ref()
12409                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
12410                self.expected_symbols_display(expected_symbols)
12411            ),
12412        )
12413    }
12414
12415    /// Builds the EOF diagnostic used when ANTLR unwinds a failed nested rule
12416    /// instead of inserting missing tokens in the caller.
12417    fn eof_rule_recovery_diagnostic(
12418        &self,
12419        index: usize,
12420        expected_symbols: &BTreeSet<i32>,
12421        expected: &ExpectedTokens,
12422    ) -> ParserDiagnostic {
12423        let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
12424            &expected.symbols
12425        } else {
12426            expected_symbols
12427        };
12428        diagnostic_for_token(
12429            self.token_at(index).as_ref(),
12430            format!(
12431                "mismatched input {} expecting {}",
12432                self.token_at(index)
12433                    .as_ref()
12434                    .map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
12435                self.expected_symbols_display(symbols)
12436            ),
12437        )
12438    }
12439
12440    /// Returns token text for a buffered token interval used by generated
12441    /// `$text` actions.
12442    ///
12443    /// ANTLR treats EOF as a range boundary rather than printable input text,
12444    /// even when an action interval explicitly stops at the EOF token.
12445    pub fn text_interval(&self, start: usize, stop: Option<usize>) -> String {
12446        let Some(stop) = stop else {
12447            return String::new();
12448        };
12449        let stop = if self
12450            .token_at(stop)
12451            .is_some_and(|token| token.token_type() == TOKEN_EOF)
12452        {
12453            let Some(previous) = self.previous_token_index(stop) else {
12454                return String::new();
12455            };
12456            previous
12457        } else {
12458            stop
12459        };
12460        self.input.text(start, stop)
12461    }
12462
12463    /// Resets per-parse prediction diagnostics while keeping the parser-level
12464    /// reporting flag configured by generated harness code.
12465    fn clear_prediction_diagnostics(&mut self) {
12466        self.prediction_diagnostics.clear();
12467        self.reported_prediction_diagnostics.clear();
12468    }
12469
12470    /// Drops every per-parse cache that depends on ATN identity or pins
12471    /// recovery-symbol allocations.
12472    ///
12473    /// `BaseParser::parse_atn_rule` takes `&Atn` on each invocation, so the
12474    /// same parser instance can legally be driven against different grammars
12475    /// in sequence. The four caches reset here are keyed by raw ATN
12476    /// coordinates (state numbers, rule indexes) and would silently hand back
12477    /// entries from a previous ATN if reused — pruning lookahead against the
12478    /// wrong transitions or pinning recovery `Rc<BTreeSet<i32>>` allocations
12479    /// for the rest of the process. Clearing them on every parse entry keeps
12480    /// the perf wins (caches still amortize within one parse) without making
12481    /// long-lived parsers leak memory or surface stale ATN data:
12482    ///
12483    /// * `rule_first_set_cache` and `decision_lookahead_cache` are pure
12484    ///   functions of the ATN's state graph.
12485    /// * `state_expected_cache`, `state_expected_token_cache`,
12486    ///   `rule_stop_reach_cache`, and
12487    ///   `recovery_symbols_intern` together form
12488    ///   the identity invariant that lets `FastRecognizeKey` hash
12489    ///   `recovery_symbols` by pointer; they have to be cleared in lockstep
12490    ///   so a stale interned `Rc` cannot outlive its map entry.
12491    /// * `empty_cycle_cache` is grammar-static and carries its own ATN key, so
12492    ///   it is retained here and invalidated lazily when the ATN changes.
12493    fn reset_per_parse_caches(&mut self) {
12494        self.rule_first_set_cache.clear();
12495        self.decision_lookahead_cache.clear();
12496        self.ll1_decision_cache.clear();
12497        self.fast_predicate_cache.clear();
12498        self.rule_stop_reach_cache.clear();
12499        self.clean_memo_mode = CleanMemoMode::Probe;
12500        self.clean_memo_probe_seen.clear();
12501        self.clean_memo_probe_samples = 0;
12502        self.clean_memo_probe_repeats = 0;
12503        self.clean_memo_sparse_samples = 0;
12504        self.recovery_symbols_intern.clear();
12505        self.state_expected_cache.clear();
12506        self.state_expected_token_cache.clear();
12507    }
12508
12509    /// Buffers ANTLR-style diagnostic-listener messages for decision states
12510    /// where multiple clean alternatives survive full-context recognition.
12511    fn record_prediction_diagnostics(
12512        &mut self,
12513        atn: &Atn,
12514        state: AtnState<'_>,
12515        start_index: usize,
12516        outcomes: &[RecognizeOutcome],
12517    ) {
12518        if !self.report_diagnostic_errors || state.transitions().len() < 2 {
12519            return;
12520        }
12521        let Some(decision) = atn
12522            .decision_to_state()
12523            .iter()
12524            .position(|state_number| state_number == state.state_number())
12525        else {
12526            return;
12527        };
12528        let Some(rule_index) = state.rule_index() else {
12529            return;
12530        };
12531        let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
12532        for outcome in outcomes
12533            .iter()
12534            .filter(|outcome| outcome.diagnostics.is_empty())
12535        {
12536            let Some(alt) = outcome.decisions.first() else {
12537                continue;
12538            };
12539            alts_by_end
12540                .entry(outcome.index)
12541                .or_default()
12542                .insert(alt + 1);
12543        }
12544        let Some((&end_index, ambig_alts)) = alts_by_end
12545            .iter()
12546            .filter(|(_, alts)| alts.len() > 1)
12547            .max_by_key(|(end, _)| *end)
12548        else {
12549            return;
12550        };
12551        let rule_name = self
12552            .rule_names()
12553            .get(rule_index)
12554            .map_or_else(|| "<unknown>".to_owned(), Clone::clone);
12555        let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
12556        let input = display_input_text(&self.input.text(start_index, stop_index));
12557        let alts = ambig_alts
12558            .iter()
12559            .map(usize::to_string)
12560            .collect::<Vec<_>>()
12561            .join(", ");
12562        let key = (decision, start_index, format!("{alts}:{input}"));
12563        if !self.reported_prediction_diagnostics.insert(key) {
12564            return;
12565        }
12566        let start_diagnostic = diagnostic_for_token(
12567            self.token_at(start_index),
12568            format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
12569        );
12570        let stop_diagnostic = diagnostic_for_token(
12571            self.token_at(stop_index),
12572            format!(
12573                "reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
12574            ),
12575        );
12576        self.prediction_diagnostics.push(start_diagnostic);
12577        self.prediction_diagnostics.push(stop_diagnostic);
12578    }
12579
12580    /// Formats the tokens expected from an ATN state using ANTLR display names.
12581    pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
12582        expected_symbols_display(
12583            &state_expected_symbols(atn, state_number),
12584            self.vocabulary(),
12585        )
12586    }
12587
12588    /// Expected-token set at the parser's current ATN state — ANTLR's
12589    /// `getExpectedTokens()`. Generated recognizers expose this as
12590    /// `self.expected_tokens()` for embedded test actions
12591    /// (`self.expected_tokens().to_token_string(self.vocabulary())`).
12592    pub fn expected_tokens_current(&self, atn: &Atn) -> ExpectedTokenSet {
12593        let state = usize::try_from(self.data().state()).unwrap_or(0);
12594        ExpectedTokenSet {
12595            symbols: state_expected_symbols(atn, state),
12596        }
12597    }
12598
12599    /// Enables the bail error strategy: the first syntax error aborts the
12600    /// parse instead of recovering.
12601    pub const fn set_bail_on_error(&mut self, bail: bool) {
12602        self.bail_on_error = bail;
12603    }
12604
12605    /// Whether the bail error strategy is active.
12606    #[must_use]
12607    pub const fn bail_on_error(&self) -> bool {
12608        self.bail_on_error
12609    }
12610
12611    /// Names of the rules on the live invocation stack, current rule first —
12612    /// ANTLR's `getRuleInvocationStack()`.
12613    pub fn rule_invocation_stack(&self) -> Vec<String> {
12614        self.rule_context_stack
12615            .iter()
12616            .rev()
12617            .map(|frame| {
12618                self.data()
12619                    .rule_names()
12620                    .get(frame.rule_index)
12621                    .cloned()
12622                    .unwrap_or_else(|| format!("<{}>", frame.rule_index))
12623            })
12624            .collect()
12625    }
12626
12627    /// Invoking-state chain for the active rule context, current rule first.
12628    ///
12629    /// The root frame is excluded, matching Java's `RuleContext.toString()`.
12630    pub fn active_invocation_states(&self) -> Vec<isize> {
12631        self.rule_context_stack
12632            .iter()
12633            .skip(1)
12634            .rev()
12635            .map(|frame| frame.invoking_state)
12636            .collect()
12637    }
12638
12639    /// Formats a buffered token in ANTLR's diagnostic token display form.
12640    pub fn token_display_at(&self, index: usize) -> Option<String> {
12641        self.token_at(index).map(|token| format!("{token}"))
12642    }
12643}
12644
12645impl<'atn, S, H> DirectAdaptiveParser<'atn, '_, S, H>
12646where
12647    S: TokenSource,
12648    H: SemanticHooks,
12649{
12650    fn parse_rule(
12651        &mut self,
12652        rule_index: usize,
12653        invoking_state: isize,
12654        precedence: i32,
12655    ) -> DirectAdaptiveParseResult<ParseTree> {
12656        let start_state = self.atn.rule_to_start_state().get(rule_index).ok_or(
12657            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::MissingAtn),
12658        )?;
12659        let stop_state = self
12660            .atn
12661            .rule_to_stop_state()
12662            .get(rule_index)
12663            .filter(|state| *state != usize::MAX)
12664            .ok_or(DirectAdaptiveParseControl::Fallback(
12665                DirectAdaptiveFallback::MissingAtn,
12666            ))?;
12667        let start_index = self.parser.current_visible_index();
12668        let mut context = ParserRuleContext::new(rule_index, invoking_state);
12669        if let Some(token) = self.parser.token_id_at(start_index) {
12670            self.parser.set_context_start(&mut context, token);
12671        }
12672        let mut state_number = start_state;
12673        let mut consumed_eof = false;
12674        while state_number != stop_state {
12675            self.step()?;
12676            let (transition, boundary) = self.next_transition(state_number, precedence)?;
12677            if boundary.is_some() {
12678                return Err(DirectAdaptiveParseControl::Fallback(
12679                    DirectAdaptiveFallback::LeftRecursiveBoundary,
12680                ));
12681            }
12682            match transition.data() {
12683                Transition::Epsilon { target } => {
12684                    state_number = target;
12685                }
12686                Transition::Precedence {
12687                    target,
12688                    precedence: transition_precedence,
12689                } => {
12690                    if transition_precedence < precedence {
12691                        return Err(DirectAdaptiveParseControl::Fallback(
12692                            DirectAdaptiveFallback::Precedence,
12693                        ));
12694                    }
12695                    state_number = target;
12696                }
12697                Transition::Rule {
12698                    rule_index,
12699                    follow_state,
12700                    precedence: rule_precedence,
12701                    ..
12702                } => {
12703                    let child = self.parse_rule(
12704                        rule_index,
12705                        invoking_state_number(state_number),
12706                        rule_precedence,
12707                    )?;
12708                    if self.parser.build_parse_trees {
12709                        self.parser.tree.add_child(&mut context, child);
12710                    }
12711                    state_number = follow_state;
12712                }
12713                Transition::Atom { .. }
12714                | Transition::Range { .. }
12715                | Transition::Set { .. }
12716                | Transition::NotSet { .. }
12717                | Transition::Wildcard { .. } => {
12718                    let (matched_eof, child) = self.consume_transition(transition)?;
12719                    consumed_eof |= matched_eof;
12720                    if let Some(child) = child {
12721                        self.parser.tree.add_child(&mut context, child);
12722                    }
12723                    state_number = transition.target();
12724                }
12725                Transition::Predicate { .. } => {
12726                    return Err(DirectAdaptiveParseControl::Fallback(
12727                        DirectAdaptiveFallback::Predicate,
12728                    ));
12729                }
12730                Transition::Action { .. } => {
12731                    return Err(DirectAdaptiveParseControl::Fallback(
12732                        DirectAdaptiveFallback::Action,
12733                    ));
12734                }
12735            }
12736        }
12737
12738        let stop_index = self
12739            .parser
12740            .rule_stop_token_index(self.parser.input.index(), consumed_eof);
12741        if let Some(token) = stop_index.and_then(|index| self.parser.token_id_at(index)) {
12742            self.parser.set_context_stop(&mut context, token);
12743        }
12744        Ok(self.parser.rule_node(context))
12745    }
12746
12747    const fn step(&mut self) -> DirectAdaptiveParseResult<()> {
12748        self.steps += 1;
12749        if self.steps > ADAPTIVE_DIRECT_STEP_LIMIT {
12750            return Err(DirectAdaptiveParseControl::Fallback(
12751                DirectAdaptiveFallback::StepLimit,
12752            ));
12753        }
12754        Ok(())
12755    }
12756
12757    fn next_transition(
12758        &mut self,
12759        state_number: usize,
12760        precedence: i32,
12761    ) -> DirectAdaptiveParseResult<(ParserTransition<'atn>, Option<usize>)> {
12762        let state = self
12763            .atn
12764            .state(state_number)
12765            .ok_or(DirectAdaptiveParseControl::Fallback(
12766                DirectAdaptiveFallback::MissingAtn,
12767            ))?;
12768        if state.is_rule_stop() {
12769            return Err(DirectAdaptiveParseControl::Fallback(
12770                DirectAdaptiveFallback::RuleStop,
12771            ));
12772        }
12773        let transition_index =
12774            self.transition_index(state_number, state.transitions().len(), precedence)?;
12775        let transition = state.transitions().get(transition_index).ok_or(
12776            DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::NoTransition),
12777        )?;
12778        let boundary = match &transition.data() {
12779            Transition::Epsilon { target } | Transition::Precedence { target, .. } => {
12780                left_recursive_boundary(self.atn, state, *target)
12781            }
12782            _ => None,
12783        };
12784        Ok((transition, boundary))
12785    }
12786
12787    fn transition_index(
12788        &mut self,
12789        state_number: usize,
12790        transition_count: usize,
12791        precedence: i32,
12792    ) -> DirectAdaptiveParseResult<usize> {
12793        match transition_count {
12794            0 => Err(DirectAdaptiveParseControl::Fallback(
12795                DirectAdaptiveFallback::NoTransition,
12796            )),
12797            1 => Ok(0),
12798            _ => {
12799                if let Some(alt) = self.ll1_transition_index(state_number, transition_count)? {
12800                    return Ok(alt);
12801                }
12802                let decision = self
12803                    .decision_by_state
12804                    .get(state_number)
12805                    .and_then(|decision| *decision)
12806                    .ok_or(DirectAdaptiveParseControl::Fallback(
12807                        DirectAdaptiveFallback::UnknownDecision,
12808                    ))?;
12809                let prediction = self
12810                    .simulator
12811                    .adaptive_predict_stream_info_with_precedence(
12812                        decision,
12813                        direct_precedence(precedence),
12814                        &mut self.parser.input,
12815                    )
12816                    .map_err(|_| {
12817                        DirectAdaptiveParseControl::Fallback(DirectAdaptiveFallback::Prediction)
12818                    })?;
12819                if prediction.has_semantic_context {
12820                    return Err(DirectAdaptiveParseControl::Fallback(
12821                        DirectAdaptiveFallback::SemanticContext,
12822                    ));
12823                }
12824                prediction
12825                    .alt
12826                    .checked_sub(1)
12827                    .filter(|index| *index < transition_count)
12828                    .ok_or(DirectAdaptiveParseControl::Fallback(
12829                        DirectAdaptiveFallback::InvalidAlt,
12830                    ))
12831            }
12832        }
12833    }
12834
12835    fn ll1_transition_index(
12836        &mut self,
12837        state_number: usize,
12838        transition_count: usize,
12839    ) -> DirectAdaptiveParseResult<Option<usize>> {
12840        let state = self
12841            .atn
12842            .state(state_number)
12843            .ok_or(DirectAdaptiveParseControl::Fallback(
12844                DirectAdaptiveFallback::MissingAtn,
12845            ))?;
12846        if state.precedence_rule_decision() {
12847            return Ok(None);
12848        }
12849        let Some(rule_stop) = state
12850            .rule_index()
12851            .and_then(|rule_index| self.atn.rule_to_stop_state().get(rule_index))
12852        else {
12853            return Ok(None);
12854        };
12855        let symbol = self.parser.input.la_token(1);
12856        let entry = self
12857            .parser
12858            .cached_decision_lookahead(self.atn, state, rule_stop);
12859        Ok(
12860            ll1_greedy_alt(&entry, symbol, state.non_greedy())
12861                .filter(|alt| *alt < transition_count),
12862        )
12863    }
12864
12865    fn consume_transition(
12866        &mut self,
12867        transition: ParserTransition<'_>,
12868    ) -> DirectAdaptiveParseResult<(bool, Option<ParseTree>)> {
12869        let symbol = self.parser.input.la_token(1);
12870        if !transition.matches(symbol, 1, self.atn.max_token_type()) {
12871            return Err(DirectAdaptiveParseControl::Fallback(
12872                DirectAdaptiveFallback::TokenMismatch,
12873            ));
12874        }
12875        let token = self
12876            .parser
12877            .input
12878            .lt_id(1)
12879            .ok_or(DirectAdaptiveParseControl::Fallback(
12880                DirectAdaptiveFallback::TokenMismatch,
12881            ))?;
12882        let matched_eof = symbol == TOKEN_EOF;
12883        if !matched_eof {
12884            self.parser.consume();
12885        }
12886        let child = self
12887            .parser
12888            .build_parse_trees
12889            .then(|| self.parser.terminal_tree(token));
12890        Ok((matched_eof, child))
12891    }
12892}
12893
12894impl<S, H> CommittedAtnParser<'_, '_, '_, S, H>
12895where
12896    S: TokenSource,
12897    H: SemanticHooks,
12898{
12899    fn parse_rule(
12900        &mut self,
12901        rule_index: usize,
12902        precedence: i32,
12903        inherited_local_int_arg: Option<(usize, i64)>,
12904        init_expected_state: Option<usize>,
12905    ) -> Result<CommittedRuleOutcome, AntlrError> {
12906        let start_state = self
12907            .atn
12908            .rule_to_start_state()
12909            .get(rule_index)
12910            .ok_or_else(|| {
12911                AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
12912            })?;
12913        let stop_state = self
12914            .atn
12915            .rule_to_stop_state()
12916            .get(rule_index)
12917            .filter(|state| *state != usize::MAX)
12918            .ok_or_else(|| {
12919                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
12920            })?;
12921        let left_recursive = self
12922            .atn
12923            .state(start_state)
12924            .is_some_and(AtnState::left_recursive_rule);
12925        if let Some(error) = self.parser.rule_depth_cap_violation() {
12926            return Err(error);
12927        }
12928        if let Some(error) = self.parser.parse_listener_enter_rule(rule_index) {
12929            return Err(error);
12930        }
12931        let mut context = if left_recursive {
12932            self.parser.enter_recursion_rule(
12933                invoking_state_number(start_state),
12934                rule_index,
12935                precedence,
12936            )
12937        } else {
12938            self.parser
12939                .enter_rule(invoking_state_number(start_state), rule_index)
12940        };
12941        let rule_start_index = self.parser.current_visible_index();
12942        let local_int_arg =
12943            usize::try_from(context.invoking_state())
12944                .ok()
12945                .and_then(|source_state| {
12946                    rule_local_int_arg(
12947                        self.options.rule_args,
12948                        source_state,
12949                        rule_index,
12950                        inherited_local_int_arg,
12951                    )
12952                });
12953        if self.options.init_action_rules.contains(&rule_index) {
12954            let action = ParserAction::new_rule_init(
12955                rule_index,
12956                rule_start_index,
12957                init_expected_state.or(Some(start_state)),
12958            );
12959            if !self
12960                .parser
12961                .parser_rule_init_hook_with_context(action, &context, local_int_arg)
12962            {
12963                self.deferred_actions.push(action);
12964            }
12965        }
12966        let mut consumed_eof = false;
12967        let result = self.walk_rule(
12968            rule_index,
12969            start_state,
12970            stop_state,
12971            precedence,
12972            rule_start_index,
12973            local_int_arg,
12974            left_recursive,
12975            &mut context,
12976            &mut consumed_eof,
12977        );
12978
12979        let result = match result {
12980            Ok(()) => Ok(if left_recursive {
12981                self.parser.finish_recursion_rule(context, consumed_eof)
12982            } else {
12983                self.parser.finish_rule(context, consumed_eof)
12984            }),
12985            Err(error) if self.parser.bail_on_error() => {
12986                if left_recursive {
12987                    self.parser.unroll_recursion_context();
12988                } else {
12989                    self.parser.exit_rule();
12990                }
12991                Err(error)
12992            }
12993            Err(error) => {
12994                self.parser
12995                    .recover_generated_rule(&mut context, self.atn, error);
12996                Ok(if left_recursive {
12997                    self.parser.finish_recursion_rule(context, consumed_eof)
12998                } else {
12999                    self.parser.finish_rule(context, consumed_eof)
13000                })
13001            }
13002        };
13003        self.parser.parse_listener_exit_rule(rule_index);
13004        result.map(|tree| CommittedRuleOutcome { tree, consumed_eof })
13005    }
13006
13007    #[allow(clippy::too_many_arguments)]
13008    fn walk_rule(
13009        &mut self,
13010        rule_index: usize,
13011        mut state_number: usize,
13012        stop_state: usize,
13013        precedence: i32,
13014        rule_start_index: usize,
13015        local_int_arg: Option<(usize, i64)>,
13016        left_recursive: bool,
13017        context: &mut ParserRuleContext,
13018        consumed_eof: &mut bool,
13019    ) -> Result<(), AntlrError> {
13020        let mut entered_loops = BTreeSet::new();
13021        let mut visited_coordinates = FxHashSet::default();
13022        let mut guarded_input_index = self.parser.input.index();
13023        while state_number != stop_state {
13024            let input_index = self.parser.input.index();
13025            if input_index != guarded_input_index {
13026                visited_coordinates.clear();
13027                guarded_input_index = input_index;
13028            }
13029            if !visited_coordinates.insert((state_number, input_index)) {
13030                return Err(AntlrError::Unsupported(format!(
13031                    "committed parser encountered a non-consuming ATN cycle at state \
13032                         {state_number}"
13033                )));
13034            }
13035            let state = self.atn.state(state_number).ok_or_else(|| {
13036                AntlrError::Unsupported(format!("missing parser ATN state {state_number}"))
13037            })?;
13038            if state.is_rule_stop() {
13039                return Err(AntlrError::Unsupported(format!(
13040                    "rule {rule_index} reached unexpected stop state {state_number}"
13041                )));
13042            }
13043            let transition_index = {
13044                let mut decision_context = CommittedDecisionContext {
13045                    precedence,
13046                    local_int_arg,
13047                    context,
13048                    entered_loops: &mut entered_loops,
13049                };
13050                self.transition_index(state, &mut decision_context)?
13051            };
13052            let transition = state.transitions().get(transition_index).ok_or_else(|| {
13053                AntlrError::Unsupported(format!(
13054                    "missing transition {transition_index} from parser ATN state {state_number}"
13055                ))
13056            })?;
13057
13058            let next_alt = next_alt_number(
13059                state,
13060                state.transitions().len(),
13061                transition_index,
13062                context.alt_number(),
13063                self.options.track_alt_numbers,
13064            );
13065            if self.options.track_alt_numbers && context.alt_number() == 0 && next_alt != 0 {
13066                context.set_alt_number(next_alt);
13067            }
13068            let next_context_alt = next_alt_number(
13069                state,
13070                state.transitions().len(),
13071                transition_index,
13072                context.context_alt_number(),
13073                self.options.track_context_alt_numbers,
13074            );
13075            if self.options.track_context_alt_numbers
13076                && context.context_alt_number() == 0
13077                && next_context_alt != 0
13078            {
13079                context.set_context_alt_number(next_context_alt);
13080            }
13081
13082            if left_recursive
13083                && left_recursive_boundary(self.atn, state, transition.target()).is_some()
13084            {
13085                if let Some(error) = self.parser.rule_depth_cap_violation() {
13086                    return Err(error);
13087                }
13088                self.parser.parse_listener_exit_rule(rule_index);
13089                self.parser.push_new_recursion_context_with_previous(
13090                    invoking_state_number(
13091                        self.atn
13092                            .rule_to_start_state()
13093                            .get(rule_index)
13094                            .unwrap_or(state_number),
13095                    ),
13096                    rule_index,
13097                    context,
13098                );
13099                if let Some(error) = self.parser.parse_listener_enter_rule(rule_index) {
13100                    return Err(error);
13101                }
13102            }
13103            state_number = self.apply_transition(
13104                state_number,
13105                transition,
13106                precedence,
13107                rule_start_index,
13108                local_int_arg,
13109                context,
13110                consumed_eof,
13111            )?;
13112        }
13113        Ok(())
13114    }
13115
13116    fn transition_index(
13117        &mut self,
13118        state: AtnState<'_>,
13119        decision_context: &mut CommittedDecisionContext<'_>,
13120    ) -> Result<usize, AntlrError> {
13121        let transition_count = state.transitions().len();
13122        if transition_count == 1 {
13123            return Ok(0);
13124        }
13125        let Some(decision) = self
13126            .decision_by_state
13127            .get(state.state_number())
13128            .copied()
13129            .flatten()
13130        else {
13131            return Err(AntlrError::Unsupported(format!(
13132                "parser ATN state {} has {transition_count} transitions but is not a decision",
13133                state.state_number()
13134            )));
13135        };
13136
13137        let decision_start = self.parser.input.index();
13138        let overridden_transition = if self.parser.semantic_hooks.observes_parser_decisions() {
13139            self.parser
13140                .semantic_hooks
13141                .parser_decision_override(decision, decision_start, transition_count)
13142                .and_then(|alternative| alternative.checked_sub(1))
13143                .filter(|alternative| *alternative < transition_count)
13144        } else {
13145            None
13146        };
13147        if let Some(selected) = overridden_transition {
13148            self.update_loop_selection(state, selected, decision_context);
13149            return Ok(selected);
13150        }
13151
13152        if !state.precedence_rule_decision() {
13153            let loop_back = match state.kind() {
13154                AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack => true,
13155                AtnStateKind::StarLoopEntry => decision_context
13156                    .entered_loops
13157                    .contains(&state.state_number()),
13158                _ => false,
13159            };
13160            let children = self.parser.sync_decision(
13161                self.atn,
13162                state.state_number(),
13163                !decision_context.context.has_matched_child(),
13164                loop_back,
13165            )?;
13166            for child in children {
13167                self.parser.add_parse_child(decision_context.context, child);
13168            }
13169        }
13170
13171        let prediction_precedence = if state.precedence_rule_decision() {
13172            usize::try_from(decision_context.precedence.max(0)).unwrap_or_default()
13173        } else {
13174            0
13175        };
13176        let prediction_context = {
13177            let return_states = self
13178                .parser
13179                .prediction_context_return_states(self.atn)
13180                .collect::<Vec<_>>();
13181            self.simulator
13182                .intern_prediction_context(self.parser.rule_context_version(), return_states)
13183        };
13184        self.simulator.set_exact_ambig_detection(
13185            self.parser.prediction_mode() == PredictionMode::LlExactAmbigDetection,
13186        );
13187        let prediction_mode = self.parser.prediction_mode();
13188        let prediction = match self.simulator.adaptive_predict_stream_info_sll_probe(
13189            decision,
13190            prediction_precedence,
13191            &mut self.parser.input,
13192        ) {
13193            Ok(prediction)
13194                if prediction.requires_full_context && prediction_mode != PredictionMode::Sll =>
13195            {
13196                self.simulator.adaptive_predict_stream_info_with_context(
13197                    decision,
13198                    prediction_precedence,
13199                    &mut self.parser.input,
13200                    prediction_context,
13201                )
13202            }
13203            prediction => prediction,
13204        };
13205        let mut prediction = match prediction {
13206            Ok(prediction) => prediction,
13207            Err(ParserAtnSimulatorError::NoViableAlt { index, .. })
13208                if state.precedence_rule_decision() =>
13209            {
13210                let enter_alt = state.transitions().iter().position(|transition| {
13211                    self.atn
13212                        .state(transition.target())
13213                        .is_some_and(|target| target.kind() != AtnStateKind::LoopEnd)
13214                });
13215                let exit_alt = state.transitions().iter().position(|transition| {
13216                    self.atn
13217                        .state(transition.target())
13218                        .is_some_and(|target| target.kind() == AtnStateKind::LoopEnd)
13219                });
13220                let selected = if self.parser.left_recursive_loop_enter_matches(
13221                    self.atn,
13222                    state.state_number(),
13223                    decision_context.precedence,
13224                ) {
13225                    enter_alt
13226                } else {
13227                    exit_alt
13228                };
13229                let Some(selected) = selected else {
13230                    return Err(self
13231                        .parser
13232                        .no_viable_alternative_error_at(decision_start, index));
13233                };
13234                ParserAtnPrediction {
13235                    alt: selected + 1,
13236                    requires_full_context: true,
13237                    has_semantic_context: true,
13238                    diagnostic: None,
13239                }
13240            }
13241            Err(ParserAtnSimulatorError::NoViableAlt { index, .. }) => {
13242                return Err(self
13243                    .parser
13244                    .no_viable_alternative_error_at(decision_start, index));
13245            }
13246            Err(ParserAtnSimulatorError::PredictionRequiresMoreLookahead) => {
13247                return Err(self.parser.no_viable_alternative_error(decision_start));
13248            }
13249            Err(error) => {
13250                return Err(AntlrError::Unsupported(format!(
13251                    "committed parser prediction failed at decision {decision}: {error:?}"
13252                )));
13253            }
13254        };
13255        let mut selected = prediction
13256            .alt
13257            .checked_sub(1)
13258            .filter(|index| *index < transition_count)
13259            .ok_or_else(|| self.parser.no_viable_alternative_error(decision_start))?;
13260
13261        let semantic_candidates = self.simulator.prediction_semantic_candidates();
13262        if !semantic_candidates.is_empty() {
13263            let predicted_alt = prediction.alt;
13264            let mut semantic_results = BTreeMap::new();
13265            let selected_alt = selected + 1;
13266            let selected_matches = self.semantic_alternative_matches(
13267                selected_alt,
13268                decision_context,
13269                &semantic_candidates,
13270            );
13271            semantic_results.insert(selected_alt, selected_matches);
13272            if !selected_matches {
13273                let alternatives = semantic_candidates
13274                    .iter()
13275                    .map(|candidate| candidate.alt)
13276                    .filter(|alternative| *alternative != 0 && *alternative <= transition_count)
13277                    .collect::<BTreeSet<_>>();
13278                selected = alternatives
13279                    .into_iter()
13280                    .find(|alternative| {
13281                        let matches = self.semantic_alternative_matches(
13282                            *alternative,
13283                            decision_context,
13284                            &semantic_candidates,
13285                        );
13286                        semantic_results.insert(*alternative, matches);
13287                        matches
13288                    })
13289                    .and_then(|alternative| alternative.checked_sub(1))
13290                    .ok_or_else(|| self.parser.no_viable_alternative_error(decision_start))?;
13291            }
13292            if self.parser.report_diagnostic_errors
13293                && let Some(diagnostic) = prediction.diagnostic.as_ref()
13294            {
13295                for alternative in diagnostic.conflicting_alts.clone() {
13296                    if semantic_results.contains_key(&alternative)
13297                        || !semantic_candidates
13298                            .iter()
13299                            .any(|candidate| candidate.alt == alternative)
13300                    {
13301                        continue;
13302                    }
13303                    let matches = self.semantic_alternative_matches(
13304                        alternative,
13305                        decision_context,
13306                        &semantic_candidates,
13307                    );
13308                    semantic_results.insert(alternative, matches);
13309                }
13310            }
13311            Self::filter_prediction_diagnostic(
13312                &mut prediction,
13313                predicted_alt,
13314                selected + 1,
13315                &semantic_results,
13316            );
13317        }
13318        self.parser.record_generated_prediction_diagnostic(
13319            self.atn,
13320            state.state_number(),
13321            &prediction,
13322        );
13323
13324        self.update_loop_selection(state, selected, decision_context);
13325        Ok(selected)
13326    }
13327
13328    fn semantic_alternative_matches(
13329        &mut self,
13330        alternative: usize,
13331        decision_context: &CommittedDecisionContext<'_>,
13332        candidates: &[ParserSemanticCandidate],
13333    ) -> bool {
13334        candidates
13335            .iter()
13336            .filter(|candidate| candidate.alt == alternative)
13337            .any(|candidate| {
13338                self.semantic_context_matches(&candidate.context, decision_context, candidate)
13339            })
13340    }
13341
13342    fn filter_prediction_diagnostic(
13343        prediction: &mut ParserAtnPrediction,
13344        predicted_alt: usize,
13345        selected_alt: usize,
13346        semantic_results: &BTreeMap<usize, bool>,
13347    ) {
13348        prediction.alt = selected_alt;
13349        if selected_alt != predicted_alt {
13350            prediction.diagnostic = None;
13351            return;
13352        }
13353        if let Some(diagnostic) = prediction.diagnostic.as_mut() {
13354            diagnostic
13355                .conflicting_alts
13356                .retain(|alternative| semantic_results.get(alternative).copied().unwrap_or(true));
13357            if diagnostic.conflicting_alts.len() < 2 {
13358                prediction.diagnostic = None;
13359            }
13360        }
13361    }
13362
13363    fn semantic_context_matches(
13364        &mut self,
13365        semantic_context: &SemanticContext,
13366        decision_context: &CommittedDecisionContext<'_>,
13367        candidate: &ParserSemanticCandidate,
13368    ) -> bool {
13369        match semantic_context {
13370            SemanticContext::None => true,
13371            SemanticContext::Predicate {
13372                rule_index,
13373                pred_index,
13374                ..
13375            } => {
13376                let mut matched_provenance = false;
13377                for predicate_call in candidate
13378                    .predicate_calls
13379                    .iter()
13380                    .filter(|call| call.rule_index == *rule_index && call.pred_index == *pred_index)
13381                {
13382                    matched_provenance = true;
13383                    let mut local_int_arg = decision_context.local_int_arg;
13384                    for rule_call in &predicate_call.rule_calls {
13385                        local_int_arg = rule_local_int_arg(
13386                            self.options.rule_args,
13387                            rule_call.source_state,
13388                            rule_call.rule_index,
13389                            local_int_arg,
13390                        );
13391                    }
13392                    if !self.semantic_predicate_matches(
13393                        *rule_index,
13394                        *pred_index,
13395                        decision_context,
13396                        local_int_arg,
13397                    ) {
13398                        return false;
13399                    }
13400                }
13401                if matched_provenance {
13402                    true
13403                } else {
13404                    self.semantic_predicate_matches(
13405                        *rule_index,
13406                        *pred_index,
13407                        decision_context,
13408                        decision_context.local_int_arg,
13409                    )
13410                }
13411            }
13412            SemanticContext::Precedence { precedence } => {
13413                *precedence >= decision_context.precedence
13414            }
13415            SemanticContext::And(children) => {
13416                for child in children {
13417                    if !self.semantic_context_matches(child, decision_context, candidate) {
13418                        return false;
13419                    }
13420                }
13421                true
13422            }
13423            SemanticContext::Or(children) => {
13424                for child in children {
13425                    if self.semantic_context_matches(child, decision_context, candidate) {
13426                        return true;
13427                    }
13428                }
13429                false
13430            }
13431        }
13432    }
13433
13434    fn semantic_predicate_matches(
13435        &mut self,
13436        rule_index: usize,
13437        pred_index: usize,
13438        decision_context: &CommittedDecisionContext<'_>,
13439        local_int_arg: Option<(usize, i64)>,
13440    ) -> bool {
13441        let member_values = self.parser.int_members.clone();
13442        self.parser.parser_predicate_matches(PredicateEval {
13443            index: self.parser.input.index(),
13444            rule_index,
13445            pred_index,
13446            predicates: self.options.predicates,
13447            semantics: self.options.semantics,
13448            context: Some(&*decision_context.context),
13449            local_int_arg,
13450            member_values: &member_values,
13451        })
13452    }
13453
13454    fn update_loop_selection(
13455        &self,
13456        state: AtnState<'_>,
13457        selected: usize,
13458        decision_context: &mut CommittedDecisionContext<'_>,
13459    ) {
13460        if state.kind() == AtnStateKind::StarLoopEntry {
13461            let enters = self
13462                .atn
13463                .state(
13464                    state
13465                        .transitions()
13466                        .get(selected)
13467                        .expect("selected transition is in bounds")
13468                        .target(),
13469                )
13470                .is_some_and(|target| target.kind() != AtnStateKind::LoopEnd);
13471            if enters {
13472                decision_context.entered_loops.insert(state.state_number());
13473            } else {
13474                decision_context.entered_loops.remove(&state.state_number());
13475            }
13476        }
13477    }
13478
13479    #[allow(clippy::too_many_arguments)]
13480    fn apply_transition(
13481        &mut self,
13482        source_state: usize,
13483        transition: ParserTransition<'_>,
13484        precedence: i32,
13485        rule_start_index: usize,
13486        local_int_arg: Option<(usize, i64)>,
13487        context: &mut ParserRuleContext,
13488        consumed_eof: &mut bool,
13489    ) -> Result<usize, AntlrError> {
13490        self.parser.set_state(invoking_state_number(source_state));
13491        match transition.data() {
13492            Transition::Epsilon { target } => Ok(target),
13493            Transition::Atom { target, label } => {
13494                let matched = self
13495                    .parser
13496                    .match_token_recovering(label, target, self.atn)?;
13497                *consumed_eof |= matched.consumed_eof();
13498                for child in matched.into_child_iter() {
13499                    self.parser.add_parse_child(context, child);
13500                }
13501                Ok(target)
13502            }
13503            Transition::Range {
13504                target,
13505                start,
13506                stop,
13507            } => {
13508                let matched =
13509                    self.parser
13510                        .match_set_recovering(&[(start, stop)], target, self.atn)?;
13511                *consumed_eof |= matched.consumed_eof();
13512                for child in matched.into_child_iter() {
13513                    self.parser.add_parse_child(context, child);
13514                }
13515                Ok(target)
13516            }
13517            Transition::Set { target, set } => {
13518                let matched = self
13519                    .parser
13520                    .match_token_set_recovering(set, target, self.atn)?;
13521                *consumed_eof |= matched.consumed_eof();
13522                for child in matched.into_child_iter() {
13523                    self.parser.add_parse_child(context, child);
13524                }
13525                Ok(target)
13526            }
13527            Transition::NotSet { target, set } => {
13528                let matched = self.parser.match_not_token_set_recovering(
13529                    set,
13530                    1,
13531                    self.atn.max_token_type(),
13532                    target,
13533                    self.atn,
13534                )?;
13535                *consumed_eof |= matched.consumed_eof();
13536                for child in matched.into_child_iter() {
13537                    self.parser.add_parse_child(context, child);
13538                }
13539                Ok(target)
13540            }
13541            Transition::Wildcard { target } => {
13542                let matched = self.parser.match_not_set_recovering(
13543                    &[],
13544                    1,
13545                    self.atn.max_token_type(),
13546                    target,
13547                    self.atn,
13548                )?;
13549                *consumed_eof |= matched.consumed_eof();
13550                for child in matched.into_child_iter() {
13551                    self.parser.add_parse_child(context, child);
13552                }
13553                Ok(target)
13554            }
13555            Transition::Rule {
13556                rule_index,
13557                follow_state,
13558                precedence: rule_precedence,
13559                ..
13560            } => {
13561                let marker = self
13562                    .parser
13563                    .push_invoking_state(invoking_state_number(source_state));
13564                let child = if self.parser.generated_rule_stack_check_due() {
13565                    grow_generated_rule_stack(|| {
13566                        self.parse_rule(
13567                            rule_index,
13568                            rule_precedence,
13569                            local_int_arg,
13570                            Some(follow_state),
13571                        )
13572                    })
13573                } else {
13574                    self.parse_rule(
13575                        rule_index,
13576                        rule_precedence,
13577                        local_int_arg,
13578                        Some(follow_state),
13579                    )
13580                };
13581                self.parser.discard_invoking_state(marker);
13582                let child = child?;
13583                *consumed_eof |= child.consumed_eof;
13584                self.parser.add_parse_child(context, child.tree);
13585                Ok(follow_state)
13586            }
13587            Transition::Predicate {
13588                target,
13589                rule_index,
13590                pred_index,
13591                ..
13592            } => {
13593                let member_values = self.parser.int_members.clone();
13594                if self.parser.parser_predicate_matches(PredicateEval {
13595                    index: self.parser.input.index(),
13596                    rule_index,
13597                    pred_index,
13598                    predicates: self.options.predicates,
13599                    semantics: self.options.semantics,
13600                    context: Some(context),
13601                    local_int_arg,
13602                    member_values: &member_values,
13603                }) {
13604                    return Ok(target);
13605                }
13606                if let Some(message) = self
13607                    .options
13608                    .semantics
13609                    .and_then(|semantics| {
13610                        self.parser.parser_semantic_ir_predicate_failure_message(
13611                            rule_index, pred_index, semantics,
13612                        )
13613                    })
13614                    .or_else(|| {
13615                        self.parser.parser_predicate_failure_message(
13616                            rule_index,
13617                            pred_index,
13618                            self.options.predicates,
13619                        )
13620                    })
13621                {
13622                    return Err(self
13623                        .parser
13624                        .failed_predicate_option_error(rule_index, message));
13625                }
13626                Err(self.parser.failed_predicate_error("semantic predicate"))
13627            }
13628            Transition::Action {
13629                target, rule_index, ..
13630            } => {
13631                self.apply_translated_actions(source_state, rule_index, context);
13632                if let Some(action_index) = self.action_index(source_state) {
13633                    let action = self.parser.parser_action_at_current_indexed(
13634                        source_state,
13635                        rule_index,
13636                        action_index,
13637                        rule_start_index,
13638                        *consumed_eof,
13639                    );
13640                    let _ = self.parser.parser_action_hook_inner(
13641                        action,
13642                        Some(context),
13643                        None,
13644                        local_int_arg,
13645                        true,
13646                    );
13647                }
13648                Ok(target)
13649            }
13650            Transition::Precedence {
13651                target,
13652                precedence: transition_precedence,
13653            } => {
13654                if transition_precedence >= precedence {
13655                    Ok(target)
13656                } else {
13657                    Err(self
13658                        .parser
13659                        .failed_predicate_error(format!("precpred(_ctx, {transition_precedence})")))
13660                }
13661            }
13662        }
13663    }
13664
13665    fn apply_translated_actions(
13666        &mut self,
13667        source_state: usize,
13668        rule_index: usize,
13669        context: &mut ParserRuleContext,
13670    ) {
13671        apply_member_actions(
13672            source_state,
13673            self.options.member_actions,
13674            self.options.semantics,
13675            &mut self.parser.int_members,
13676        );
13677        let return_values = return_values_after_action(
13678            source_state,
13679            rule_index,
13680            self.options.return_actions,
13681            self.options.semantics,
13682            &BTreeMap::new(),
13683        );
13684        for (name, value) in return_values {
13685            context.set_int_return(name, value);
13686        }
13687    }
13688
13689    fn action_index(&self, source_state: usize) -> Option<usize> {
13690        self.action_index_by_state.get(&source_state).copied()
13691    }
13692}
13693
13694/// Detects the loop edge where ANTLR would call `pushNewRecursionContext` for a
13695/// transformed left-recursive rule.
13696fn left_recursive_boundary(atn: &Atn, state: AtnState<'_>, target: usize) -> Option<usize> {
13697    if !state.precedence_rule_decision() {
13698        return None;
13699    }
13700    let target_state = atn.state(target)?;
13701    if target_state.kind() == AtnStateKind::LoopEnd {
13702        return None;
13703    }
13704    state.rule_index()
13705}
13706
13707/// Selects the first outer alternative observed for a rule path.
13708///
13709/// ANTLR's alt-numbered tree contexts store the rule alternative chosen at the
13710/// outer decision. The metadata recognizer only needs this when a generated
13711/// grammar opts into that target template; otherwise the value remains `0` and
13712/// parse-tree rendering is unchanged.
13713fn next_alt_number(
13714    state: AtnState<'_>,
13715    transition_count: usize,
13716    transition_index: usize,
13717    current_alt_number: usize,
13718    track_alt_numbers: bool,
13719) -> usize {
13720    if !track_alt_numbers || current_alt_number != 0 || transition_count <= 1 {
13721        return current_alt_number;
13722    }
13723    if matches!(
13724        state.kind(),
13725        AtnStateKind::Basic
13726            | AtnStateKind::BlockStart
13727            | AtnStateKind::PlusBlockStart
13728            | AtnStateKind::StarBlockStart
13729            | AtnStateKind::StarLoopEntry
13730    ) && !state.precedence_rule_decision()
13731    {
13732        return transition_index + 1;
13733    }
13734    current_alt_number
13735}
13736
13737/// Converts an ATN state number into the signed invoking-state slot used by
13738/// ANTLR parse-tree contexts, saturating only for impossible platform widths.
13739fn invoking_state_number(state_number: usize) -> isize {
13740    isize::try_from(state_number).unwrap_or(isize::MAX)
13741}
13742
13743const fn packed_i32(value: u32) -> i32 {
13744    i32::from_le_bytes(value.to_le_bytes())
13745}
13746
13747fn direct_precedence(precedence: i32) -> usize {
13748    usize::try_from(precedence.max(0)).unwrap_or_default()
13749}
13750
13751fn token_input_display(token: &impl Token) -> String {
13752    format!("'{}'", token.text().unwrap_or("<EOF>"))
13753}
13754
13755fn display_input_text(text: &str) -> String {
13756    let mut out = String::new();
13757    for ch in text.chars() {
13758        match ch {
13759            '\n' => out.push_str("\\n"),
13760            '\r' => out.push_str("\\r"),
13761            '\t' => out.push_str("\\t"),
13762            other => out.push(other),
13763        }
13764    }
13765    out
13766}
13767
13768fn diagnostic_for_token<T: Token>(token: Option<T>, message: String) -> ParserDiagnostic {
13769    let (line, column, offending) = token.map_or((0, 0, None), |token| {
13770        (token.line(), token.column(), Some(token.token_id()))
13771    });
13772    ParserDiagnostic {
13773        line,
13774        column,
13775        message,
13776        offending,
13777    }
13778}
13779
13780fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
13781    expected_symbols_display_iter(symbols.iter().copied(), vocabulary)
13782}
13783
13784fn expected_symbols_display_iter(
13785    symbols: impl IntoIterator<Item = i32>,
13786    vocabulary: &Vocabulary,
13787) -> String {
13788    let items = symbols
13789        .into_iter()
13790        .map(|symbol| expected_symbol_display(symbol, vocabulary))
13791        .collect::<Vec<_>>();
13792    if let [single] = items.as_slice() {
13793        return single.clone();
13794    }
13795    format!("{{{}}}", items.join(", "))
13796}
13797
13798fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
13799    if symbol == TOKEN_EOF {
13800        return "<EOF>".to_owned();
13801    }
13802    vocabulary.display_name(symbol)
13803}
13804
13805fn caller_follow_token_info_for_stream<S: TokenSource>(
13806    input: &mut CommonTokenStream<S>,
13807    index: usize,
13808) -> (i32, bool, bool) {
13809    // Generated callers own statement separators; leave them available when
13810    // an interpreted child rule can either stop before or consume one.
13811    if index >= FAST_RECOGNIZER_DEFERRED_FILL_AT && !input.is_filled() {
13812        input.fill();
13813    }
13814    let token_type = input.token_type_at_index(index);
13815    let visible_channel = input.channel();
13816    let token = input.get(index);
13817    let is_boundary = token
13818        .as_ref()
13819        .and_then(Token::text)
13820        .is_some_and(is_caller_follow_boundary_text);
13821    let is_boundary_gap = token.as_ref().is_some_and(|token| {
13822        token.channel() != visible_channel
13823            || is_caller_follow_boundary_gap_text(token.text_or_empty())
13824    });
13825    (token_type, is_boundary, is_boundary_gap)
13826}
13827
13828fn is_caller_follow_boundary_text(text: &str) -> bool {
13829    text.chars().any(|ch| ch == ';' || ch == '\n')
13830        && text.chars().all(|ch| ch.is_whitespace() || ch == ';')
13831}
13832
13833fn is_caller_follow_boundary_gap_text(text: &str) -> bool {
13834    text.chars().all(|ch| ch.is_whitespace() || ch == ';')
13835}
13836
13837/// Returns whether `state` belongs to an ANTLR-transformed left-recursive rule.
13838/// Inline insertion in those precedence loops can synthesize a missing operand
13839/// before an operator and then block the legitimate loop-exit path.
13840fn state_is_left_recursive_rule(atn: &Atn, state: AtnState<'_>) -> bool {
13841    let Some(rule_index) = state.rule_index() else {
13842        return false;
13843    };
13844    atn.rule_to_start_state()
13845        .get(rule_index)
13846        .and_then(|state_number| atn.state(state_number))
13847        .is_some_and(AtnState::left_recursive_rule)
13848}
13849
13850/// Picks the better of two `parse_atn_rule` passes (with and without the
13851/// FIRST-set prefilter). A clean outcome (no diagnostics) always wins over a
13852/// recovered one; among recovered outcomes the second pass is preferred
13853/// because the no-prefilter walk reaches ANTLR-style recovery inside child
13854/// rules. If both passes failed, the second pass's expected-token snapshot
13855/// is returned so the caller renders the same diagnostic ANTLR would.
13856fn select_better_top_outcome(
13857    first: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
13858    second: Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens>,
13859    arena: &RecognitionArena,
13860) -> Result<(FastRecognizeOutcome, ExpectedTokens, usize), ExpectedTokens> {
13861    match (first, second) {
13862        (Ok(first), Ok(second)) => {
13863            if arena.diagnostics(first.0.diagnostics).next().is_none() {
13864                Ok(first)
13865            } else {
13866                Ok(second)
13867            }
13868        }
13869        (Ok(first), Err(_)) => Ok(first),
13870        (Err(_), Ok(second)) => Ok(second),
13871        (Err(_), Err(second_expected)) => Err(second_expected),
13872    }
13873}
13874
13875/// Chooses the outermost parse result that consumed the most input.
13876///
13877/// The recognizer intentionally keeps shorter endpoints available while walking
13878/// nested rule transitions so callers can satisfy following tokens such as
13879/// `expr 'and' expr`. Only the public rule entry commits to one endpoint.
13880fn select_best_fast_outcome(
13881    outcomes: impl Iterator<Item = FastRecognizeOutcome>,
13882    prediction_mode: PredictionMode,
13883    caller_follow: Option<&TokenBitSet>,
13884    mut token_info_at: impl FnMut(usize) -> (i32, bool, bool),
13885    arena: &RecognitionArena,
13886) -> Option<FastRecognizeOutcome> {
13887    let mut best = None;
13888    let mut best_caller_follow = None;
13889    for outcome in outcomes {
13890        if matches!(
13891            prediction_mode,
13892            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection
13893        ) && outcome.diagnostics.is_empty()
13894            && let Some(follow) = caller_follow
13895        {
13896            let (token_type, is_boundary, _) = token_info_at(outcome.index);
13897            if is_boundary && follow.contains(token_type) {
13898                let replace =
13899                    best_caller_follow
13900                        .as_ref()
13901                        .is_none_or(|existing: &FastRecognizeOutcome| {
13902                            (outcome.index, outcome.consumed_eof)
13903                                < (existing.index, existing.consumed_eof)
13904                        });
13905                if replace {
13906                    best_caller_follow = Some(outcome);
13907                }
13908            }
13909        }
13910        let Some(existing) = best else {
13911            best = Some(outcome);
13912            continue;
13913        };
13914        let outcome_position = (outcome.index, outcome.consumed_eof);
13915        let best_position = (existing.index, existing.consumed_eof);
13916        let better = match prediction_mode {
13917            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => outcome_is_better(
13918                outcome_position,
13919                outcome.diagnostics,
13920                best_position,
13921                existing.diagnostics,
13922                arena,
13923            ),
13924            PredictionMode::Sll => outcome.index > existing.index,
13925        };
13926        best = Some(if better { outcome } else { existing });
13927    }
13928    let should_use_caller_follow =
13929        best_caller_follow
13930            .as_ref()
13931            .zip(best.as_ref())
13932            .is_some_and(|(candidate, selected)| {
13933                if !selected.diagnostics.is_empty() {
13934                    return true;
13935                }
13936                candidate.index < selected.index
13937                    && (candidate.index..selected.index).all(|index| token_info_at(index).2)
13938            });
13939    if should_use_caller_follow {
13940        best_caller_follow
13941    } else {
13942        best
13943    }
13944}
13945
13946fn select_best_outcome(
13947    outcomes: impl Iterator<Item = RecognizeOutcome>,
13948    prediction_mode: PredictionMode,
13949    arena: &RecognitionArena,
13950) -> Option<RecognizeOutcome> {
13951    let outcomes = outcomes.collect::<Vec<_>>();
13952    let prefer_first_tie = outcomes
13953        .iter()
13954        .any(|outcome| arena.sequence_needs_stable_tie(outcome.nodes));
13955    outcomes.into_iter().reduce(|best, outcome| {
13956        let outcome_position = (outcome.index, outcome.consumed_eof);
13957        let best_position = (best.index, best.consumed_eof);
13958        let better = match prediction_mode {
13959            PredictionMode::Ll | PredictionMode::LlExactAmbigDetection => {
13960                outcome_is_better(
13961                    outcome_position,
13962                    outcome.diagnostics,
13963                    best_position,
13964                    best.diagnostics,
13965                    arena,
13966                ) || (outcome_position == best_position
13967                    && arena.diagnostics_len(outcome.diagnostics)
13968                        == arena.diagnostics_len(best.diagnostics)
13969                    && arena.diagnostics_recovery_rank(outcome.diagnostics)
13970                        == arena.diagnostics_recovery_rank(best.diagnostics)
13971                    && (outcome.decisions < best.decisions
13972                        || (!prefer_first_tie
13973                            && outcome.decisions == best.decisions
13974                            && outcome.actions > best.actions)))
13975            }
13976            PredictionMode::Sll => {
13977                outcome_position > best_position
13978                    || (outcome_position == best_position
13979                        && !prefer_first_tie
13980                        && (outcome.decisions < best.decisions
13981                            || (outcome.decisions == best.decisions
13982                                && outcome_is_better(
13983                                    outcome_position,
13984                                    outcome.diagnostics,
13985                                    best_position,
13986                                    best.diagnostics,
13987                                    arena,
13988                                ))))
13989            }
13990        };
13991        if better {
13992            return outcome;
13993        }
13994        best
13995    })
13996}
13997
13998/// Records the serialized transition order at parser decision states.
13999///
14000/// When two clean paths consume the same input, ANTLR's adaptive prediction
14001/// chooses by alternative order. Keeping this compact trace lets the metadata
14002/// recognizer distinguish greedy and non-greedy optional blocks without a full
14003/// prediction simulator.
14004fn transition_decision(
14005    atn: &Atn,
14006    state: AtnState<'_>,
14007    transition_count: usize,
14008    transition_index: usize,
14009    predicates: &[(usize, usize, ParserPredicate)],
14010) -> Option<usize> {
14011    if transition_count <= 1 || decision_reaches_unsupported_predicate(atn, state, predicates) {
14012        return None;
14013    }
14014    Some(transition_index)
14015}
14016
14017/// Reports whether a state should reset the active no-viable decision start.
14018///
14019/// Loop entry/back states are continuations of the surrounding adaptive
14020/// prediction; resetting at those states would turn LL-star failures back into
14021/// ordinary mismatches.
14022fn starts_prediction_decision(state: AtnState<'_>, transition_count: usize) -> bool {
14023    transition_count > 1
14024        && !matches!(
14025            state.kind(),
14026            AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
14027        )
14028}
14029
14030/// Marks a farthest expected-token set as no-viable when multiple alternatives
14031/// failed after the active decision had already consumed input.
14032fn record_no_viable_if_ambiguous(
14033    expected: &mut ExpectedTokens,
14034    decision_start_index: Option<usize>,
14035    index: usize,
14036) {
14037    if expected.index == Some(index) && expected.symbols.len() > 1 {
14038        if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
14039            expected.record_no_viable(decision_start, index);
14040        }
14041    }
14042}
14043
14044/// Records a no-viable decision caused by a failed semantic predicate before
14045/// any consuming transition can contribute an expected-token set.
14046const fn record_predicate_no_viable(
14047    expected: &mut ExpectedTokens,
14048    decision_start_index: Option<usize>,
14049    index: usize,
14050) {
14051    if let Some(decision_start) = decision_start_index {
14052        expected.record_no_viable(decision_start, index);
14053    }
14054}
14055
14056/// Returns the active decision start only when the error is past that start.
14057const fn no_viable_decision_start(
14058    decision_start_index: Option<usize>,
14059    index: usize,
14060) -> Option<usize> {
14061    match decision_start_index {
14062        Some(start) if index > start => Some(start),
14063        _ => None,
14064    }
14065}
14066
14067/// Restores expected-token bookkeeping when a child rule found a clean
14068/// consuming path; failures in longer child alternatives should not pollute the
14069/// caller's final expectation set.
14070fn restore_expected(
14071    children: &[RecognizeOutcome],
14072    child_start_index: usize,
14073    expected: &mut ExpectedTokens,
14074    snapshot: ExpectedTokens,
14075    preserve_child_expected: bool,
14076) {
14077    if preserve_child_expected {
14078        return;
14079    }
14080    if children
14081        .iter()
14082        .any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
14083    {
14084        *expected = snapshot;
14085    }
14086}
14087
14088/// Reports whether a decision can reach a predicate the generator did not
14089/// translate. Static alternative order is unsafe for those context predicates.
14090fn decision_reaches_unsupported_predicate(
14091    atn: &Atn,
14092    state: AtnState<'_>,
14093    predicates: &[(usize, usize, ParserPredicate)],
14094) -> bool {
14095    state.transitions().iter().any(|transition| {
14096        transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
14097    })
14098}
14099
14100/// Walks epsilon-like edges from one transition to find unsupported predicates.
14101fn transition_reaches_unsupported_predicate(
14102    atn: &Atn,
14103    transition: ParserTransition<'_>,
14104    predicates: &[(usize, usize, ParserPredicate)],
14105    visited: &mut BTreeSet<usize>,
14106) -> bool {
14107    match &transition.data() {
14108        Transition::Predicate {
14109            rule_index,
14110            pred_index,
14111            ..
14112        } => !predicates
14113            .iter()
14114            .any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
14115        Transition::Epsilon { target }
14116        | Transition::Action { target, .. }
14117        | Transition::Rule { target, .. } => {
14118            state_reaches_unsupported_predicate(atn, *target, predicates, visited)
14119        }
14120        Transition::Precedence { .. }
14121        | Transition::Atom { .. }
14122        | Transition::Range { .. }
14123        | Transition::Set { .. }
14124        | Transition::NotSet { .. }
14125        | Transition::Wildcard { .. } => false,
14126    }
14127}
14128
14129/// Finds an unsupported predicate reachable before a consuming transition.
14130fn state_reaches_unsupported_predicate(
14131    atn: &Atn,
14132    state_number: usize,
14133    predicates: &[(usize, usize, ParserPredicate)],
14134    visited: &mut BTreeSet<usize>,
14135) -> bool {
14136    if !visited.insert(state_number) {
14137        return false;
14138    }
14139    let Some(state) = atn.state(state_number) else {
14140        return false;
14141    };
14142    state.transitions().iter().any(|transition| {
14143        transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
14144    })
14145}
14146
14147/// Adds a decision step to the front of an already-recognized suffix path.
14148fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
14149    if let Some(decision) = decision {
14150        outcome.decisions.insert(0, decision);
14151    }
14152}
14153
14154fn outcome_is_better(
14155    outcome_position: (usize, bool),
14156    outcome_diagnostics: DiagnosticSeqId,
14157    best_position: (usize, bool),
14158    best_diagnostics: DiagnosticSeqId,
14159    arena: &RecognitionArena,
14160) -> bool {
14161    let outcome_len = arena.diagnostics_len(outcome_diagnostics);
14162    let best_len = arena.diagnostics_len(best_diagnostics);
14163    outcome_position > best_position
14164        || (outcome_position == best_position
14165            && (outcome_len < best_len
14166                || (outcome_len == best_len
14167                    && arena.diagnostics_recovery_rank(outcome_diagnostics)
14168                        < arena.diagnostics_recovery_rank(best_diagnostics))))
14169}
14170
14171fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
14172    if outcomes
14173        .iter()
14174        .any(|outcome| outcome.diagnostics.is_empty())
14175    {
14176        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
14177    }
14178}
14179
14180fn discard_recovered_outcomes_if_clean_path_exists(
14181    outcomes: &mut Vec<RecognizeOutcome>,
14182    arena: &RecognitionArena,
14183) {
14184    if outcomes
14185        .iter()
14186        .any(|outcome| outcome_has_rule_failure_diagnostic(outcome, arena))
14187    {
14188        return;
14189    }
14190    if outcomes
14191        .iter()
14192        .any(|outcome| outcome.diagnostics.is_empty())
14193    {
14194        outcomes.retain(|outcome| outcome.diagnostics.is_empty());
14195    }
14196}
14197
14198/// Reports whether a recovered outcome came from an explicit predicate
14199/// fail-option and therefore should compete with shorter clean loop exits.
14200fn outcome_has_rule_failure_diagnostic(
14201    outcome: &RecognizeOutcome,
14202    arena: &RecognitionArena,
14203) -> bool {
14204    arena
14205        .diagnostics(outcome.diagnostics)
14206        .any(|diagnostic| diagnostic.message.starts_with("rule "))
14207}
14208
14209/// Removes equivalent endpoints before memoizing a state result while
14210/// preserving ATN transition-discovery order.
14211///
14212/// Outcomes are compared on observable recognition state — the input index,
14213/// EOF consumption, and diagnostics — without descending into the parse-tree
14214/// fragment carried by `nodes`. Two paths reaching the same point with
14215/// different node trees would otherwise prevent memoization from collapsing
14216/// equivalent suffixes and explode the speculative-path cache.
14217///
14218/// The first occurrence per recognition key wins, which matches ANTLR's
14219/// greedy alternative selection: serialized ATNs put greedy `*`/`+` loop-back
14220/// transitions before loop-exit, so the first-discovered outcome carries the
14221/// greedy parse-tree fragment.
14222fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>, arena: &RecognitionArena) {
14223    if outcomes.len() < 2 {
14224        return;
14225    }
14226    let mut seen = FxHashSet::with_capacity_and_hasher(outcomes.len(), FxBuildHasher::default());
14227    outcomes.retain(|outcome| {
14228        seen.insert((
14229            outcome.index,
14230            outcome.consumed_eof,
14231            arena.diagnostics_len(outcome.diagnostics),
14232            arena.diagnostics_recovery_rank(outcome.diagnostics),
14233        ))
14234    });
14235}
14236
14237const FAST_OUTCOME_INLINE_KEYS: usize = 8;
14238const FAST_OUTCOME_BITS_PER_WORD: usize = 64;
14239const MAX_FAST_OUTCOME_DENSE_BYTES: usize = 64 * 1024;
14240const MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS: usize = 65_536;
14241
14242#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14243enum FastOutcomeDedupStrategy {
14244    Inline,
14245    Dense,
14246    Sparse,
14247}
14248
14249impl FastOutcomeDedupScratch {
14250    fn prepare_dense(&mut self, word_count: usize) {
14251        while let Some(word_index) = self.touched_dense_words.pop() {
14252            self.dense_words[usize::try_from(word_index).expect("u32 fits in usize")] = 0;
14253        }
14254        if self.dense_words.len() < word_count {
14255            self.dense_words.resize(word_count, 0);
14256        }
14257    }
14258}
14259
14260fn clean_fast_outcome_dense_layout(outcomes: &[FastRecognizeOutcome]) -> Option<(usize, usize)> {
14261    let first_index = outcomes.first()?.index;
14262    let (min_index, max_index) = outcomes[1..].iter().fold(
14263        (first_index, first_index),
14264        |(min_index, max_index), outcome| {
14265            (min_index.min(outcome.index), max_index.max(outcome.index))
14266        },
14267    );
14268    let index_span = max_index.checked_sub(min_index)?.checked_add(1)?;
14269    let bit_count = index_span.checked_mul(2)?;
14270    let word_count =
14271        bit_count.checked_add(FAST_OUTCOME_BITS_PER_WORD - 1)? / FAST_OUTCOME_BITS_PER_WORD;
14272    let dense_bytes = word_count.checked_mul(size_of::<u64>())?;
14273    let sparse_key_bytes = outcomes.len().checked_mul(size_of::<(usize, bool)>())?;
14274    (dense_bytes <= MAX_FAST_OUTCOME_DENSE_BYTES && dense_bytes <= sparse_key_bytes)
14275        .then_some((min_index, word_count))
14276}
14277
14278#[cfg(feature = "perf-counters")]
14279fn record_clean_fast_outcome_dedup(
14280    strategy: FastOutcomeDedupStrategy,
14281    input_len: usize,
14282    output_len: usize,
14283    dense_words: usize,
14284) {
14285    let counter = match strategy {
14286        FastOutcomeDedupStrategy::Inline => &perf_counters::OUTCOME_DEDUPE_INLINE,
14287        FastOutcomeDedupStrategy::Dense => &perf_counters::OUTCOME_DEDUPE_DENSE,
14288        FastOutcomeDedupStrategy::Sparse => &perf_counters::OUTCOME_DEDUPE_SPARSE,
14289    };
14290    perf_counters::inc(
14291        &perf_counters::OUTCOME_DEDUPE_INPUTS,
14292        u64::try_from(input_len).unwrap_or(u64::MAX),
14293    );
14294    perf_counters::inc(
14295        &perf_counters::OUTCOME_DEDUPE_REMOVED,
14296        u64::try_from(input_len - output_len).unwrap_or(u64::MAX),
14297    );
14298    perf_counters::inc(counter, 1);
14299    perf_counters::inc(
14300        &perf_counters::OUTCOME_DEDUPE_DENSE_WORDS,
14301        u64::try_from(dense_words).unwrap_or(u64::MAX),
14302    );
14303}
14304
14305/// Removes duplicate clean endpoints while preserving transition-discovery
14306/// order. Tiny lists stay on the stack; larger compact ranges use a direct
14307/// bitmap, and only wide sparse ranges pay for hashing.
14308fn dedupe_clean_fast_outcomes(
14309    outcomes: &mut Vec<FastRecognizeOutcome>,
14310    scratch: &mut FastOutcomeDedupScratch,
14311) -> FastOutcomeDedupStrategy {
14312    #[cfg(feature = "perf-counters")]
14313    let input_len = outcomes.len();
14314    if outcomes.len() <= FAST_OUTCOME_INLINE_KEYS {
14315        let mut inline_keys = [(0, false); FAST_OUTCOME_INLINE_KEYS];
14316        let mut inline_len = 0_usize;
14317        outcomes.retain(|outcome| {
14318            let key = (outcome.index, outcome.consumed_eof);
14319            if inline_keys[..inline_len].contains(&key) {
14320                return false;
14321            }
14322            inline_keys[inline_len] = key;
14323            inline_len += 1;
14324            true
14325        });
14326        #[cfg(feature = "perf-counters")]
14327        record_clean_fast_outcome_dedup(
14328            FastOutcomeDedupStrategy::Inline,
14329            input_len,
14330            outcomes.len(),
14331            0,
14332        );
14333        return FastOutcomeDedupStrategy::Inline;
14334    }
14335
14336    if let Some((base_index, word_count)) = clean_fast_outcome_dense_layout(outcomes) {
14337        scratch.prepare_dense(word_count);
14338        outcomes.retain(|outcome| {
14339            let bit_index = (outcome.index - base_index) * 2 + usize::from(outcome.consumed_eof);
14340            let word_index = bit_index / FAST_OUTCOME_BITS_PER_WORD;
14341            let bit = 1_u64 << (bit_index % FAST_OUTCOME_BITS_PER_WORD);
14342            let word = &mut scratch.dense_words[word_index];
14343            if *word & bit != 0 {
14344                return false;
14345            }
14346            if *word == 0 {
14347                scratch
14348                    .touched_dense_words
14349                    .push(u32::try_from(word_index).expect("dense outcome bitmap is capped"));
14350            }
14351            *word |= bit;
14352            true
14353        });
14354        #[cfg(feature = "perf-counters")]
14355        record_clean_fast_outcome_dedup(
14356            FastOutcomeDedupStrategy::Dense,
14357            input_len,
14358            outcomes.len(),
14359            word_count,
14360        );
14361        return FastOutcomeDedupStrategy::Dense;
14362    }
14363
14364    scratch.sparse_keys.clear();
14365    scratch.sparse_keys.reserve(outcomes.len());
14366    outcomes.retain(|outcome| {
14367        scratch
14368            .sparse_keys
14369            .insert((outcome.index, outcome.consumed_eof))
14370    });
14371    #[cfg(feature = "perf-counters")]
14372    record_clean_fast_outcome_dedup(
14373        FastOutcomeDedupStrategy::Sparse,
14374        input_len,
14375        outcomes.len(),
14376        0,
14377    );
14378    if scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS {
14379        scratch.sparse_keys = FxHashSet::default();
14380    }
14381    FastOutcomeDedupStrategy::Sparse
14382}
14383
14384/// Sorts and removes equivalent endpoints, including action traces and the
14385/// arena-backed node sequence's structural contents.
14386fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>, arena: &RecognitionArena) {
14387    outcomes.sort_unstable_by(|left, right| compare_recognize_outcomes(left, right, arena));
14388    outcomes
14389        .dedup_by(|left, right| compare_recognize_outcomes(left, right, arena) == Ordering::Equal);
14390}
14391
14392fn compare_recognize_outcomes(
14393    left: &RecognizeOutcome,
14394    right: &RecognizeOutcome,
14395    arena: &RecognitionArena,
14396) -> Ordering {
14397    left.index
14398        .cmp(&right.index)
14399        .then_with(|| left.consumed_eof.cmp(&right.consumed_eof))
14400        .then_with(|| left.alt_number.cmp(&right.alt_number))
14401        .then_with(|| left.member_values.cmp(&right.member_values))
14402        .then_with(|| left.return_values.cmp(&right.return_values))
14403        .then_with(|| arena.compare_diagnostics(left.diagnostics, right.diagnostics))
14404        .then_with(|| left.decisions.cmp(&right.decisions))
14405        .then_with(|| left.actions.cmp(&right.actions))
14406        .then_with(|| arena.compare_sequences(left.nodes, right.nodes))
14407}
14408
14409impl<S, H> Recognizer for BaseParser<S, H>
14410where
14411    S: TokenSource,
14412    H: SemanticHooks,
14413{
14414    fn data(&self) -> &RecognizerData {
14415        &self.data
14416    }
14417
14418    fn data_mut(&mut self) -> &mut RecognizerData {
14419        &mut self.data
14420    }
14421}
14422
14423impl<S, H> Parser for BaseParser<S, H>
14424where
14425    S: TokenSource,
14426    H: SemanticHooks,
14427{
14428    fn build_parse_trees(&self) -> bool {
14429        self.build_parse_trees
14430    }
14431
14432    fn set_build_parse_trees(&mut self, build: bool) {
14433        self.build_parse_trees = build;
14434    }
14435
14436    fn number_of_syntax_errors(&self) -> usize {
14437        Self::number_of_syntax_errors(self)
14438    }
14439
14440    fn report_diagnostic_errors(&self) -> bool {
14441        self.report_diagnostic_errors
14442    }
14443
14444    fn set_report_diagnostic_errors(&mut self, report: bool) {
14445        self.report_diagnostic_errors = report;
14446    }
14447
14448    fn prediction_mode(&self) -> PredictionMode {
14449        self.prediction_mode
14450    }
14451
14452    fn set_prediction_mode(&mut self, mode: PredictionMode) {
14453        self.prediction_mode = mode;
14454    }
14455
14456    fn max_rule_depth(&self) -> Option<usize> {
14457        self.max_rule_depth
14458    }
14459
14460    fn set_max_rule_depth(&mut self, depth: Option<usize>) {
14461        self.max_rule_depth = depth;
14462    }
14463
14464    fn add_parse_listener(&mut self, listener: Box<dyn ParseListener>) {
14465        self.parse_listeners.push(ParseListenerSlot(listener));
14466    }
14467
14468    fn remove_parse_listeners(&mut self) -> Vec<Box<dyn ParseListener>> {
14469        Self::remove_parse_listeners(self)
14470    }
14471}
14472
14473#[cfg(test)]
14474#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
14475mod tests {
14476    use super::*;
14477    use crate::atn::parser::{
14478        ParserAtnPredictionDiagnostic, ParserAtnPredictionDiagnosticKind, ParserAtnSimulator,
14479    };
14480    use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
14481    use crate::token::{HIDDEN_CHANNEL, Token, TokenId, TokenSink, TokenSpec, TokenStoreError};
14482    use crate::token_stream::CommonTokenStream;
14483    use crate::tree::{NodeKind, ParseTreeStats};
14484    use crate::vocabulary::Vocabulary;
14485    use std::cell::RefCell;
14486    use std::mem::size_of;
14487    use std::rc::Rc;
14488    use std::sync::{Arc, Mutex};
14489
14490    #[test]
14491    fn fx_hasher_write_matches_typed_methods_for_full_words() {
14492        // PR #5 review (Greptile P2): future key types whose `Hash` impl funnels
14493        // bytes through `Hasher::write` (e.g. `String`, `[u8; 8]`, slice-typed
14494        // fields) must hash the same as the typed methods, otherwise an
14495        // `FxHashMap` keyed on such a type silently disagrees with itself
14496        // depending on which entry point the caller used. Verify the
14497        // little-endian word equivalence this PR established.
14498        let value: u64 = 0x0102_0304_0506_0708;
14499        let mut typed = FxHasher::default();
14500        typed.write_u64(value);
14501        let mut bytewise = FxHasher::default();
14502        bytewise.write(&value.to_le_bytes());
14503        assert_eq!(typed.finish(), bytewise.finish());
14504    }
14505
14506    #[derive(Clone, Debug)]
14507    struct TestToken {
14508        spec: TokenSpec,
14509        id: TokenId,
14510        source_name: String,
14511    }
14512
14513    impl TestToken {
14514        fn new(token_type: i32) -> Self {
14515            Self {
14516                spec: TokenSpec::explicit(token_type, ""),
14517                id: TokenId::try_from(0).expect("zero token ID"),
14518                source_name: String::new(),
14519            }
14520        }
14521
14522        fn eof(source_name: &str, index: usize, line: usize, column: usize) -> Self {
14523            Self {
14524                spec: TokenSpec::eof(index, index, line, column),
14525                id: TokenId::try_from(0).expect("zero token ID"),
14526                source_name: source_name.to_owned(),
14527            }
14528        }
14529
14530        fn with_text(mut self, text: impl Into<String>) -> Self {
14531            self.spec.text = Some(text.into());
14532            self
14533        }
14534
14535        const fn with_channel(mut self, channel: i32) -> Self {
14536            self.spec.channel = channel;
14537            self
14538        }
14539
14540        fn with_span(mut self, start: usize, stop: usize) -> Self {
14541            self.spec = self.spec.with_span(start, stop);
14542            self
14543        }
14544
14545        fn with_byte_span(mut self, start: usize, stop: usize) -> Self {
14546            self.spec = self.spec.with_byte_span(start, stop);
14547            self
14548        }
14549
14550        const fn with_position(mut self, line: usize, column: usize) -> Self {
14551            self.spec.line = line;
14552            self.spec.column = column;
14553            self
14554        }
14555
14556        fn set_token_index(&mut self, index: isize) {
14557            self.id = TokenId::try_from(index.max(0).cast_unsigned()).expect("test token index");
14558        }
14559    }
14560
14561    impl Token for TestToken {
14562        fn token_id(&self) -> TokenId {
14563            self.id
14564        }
14565
14566        fn token_type(&self) -> i32 {
14567            self.spec.token_type
14568        }
14569
14570        fn channel(&self) -> i32 {
14571            self.spec.channel
14572        }
14573
14574        fn start(&self) -> usize {
14575            self.spec.start
14576        }
14577
14578        fn stop(&self) -> usize {
14579            self.spec.stop
14580        }
14581
14582        fn line(&self) -> usize {
14583            self.spec.line
14584        }
14585
14586        fn column(&self) -> usize {
14587            self.spec.column
14588        }
14589
14590        fn text(&self) -> Option<&str> {
14591            self.spec.text.as_deref()
14592        }
14593
14594        fn source_name(&self) -> &str {
14595            &self.source_name
14596        }
14597
14598        fn start_byte(&self) -> Option<usize> {
14599            (self.spec.start_byte != usize::MAX).then_some(self.spec.start_byte)
14600        }
14601
14602        fn stop_byte(&self) -> Option<usize> {
14603            (self.spec.stop_byte != usize::MAX).then_some(self.spec.stop_byte)
14604        }
14605    }
14606
14607    #[derive(Debug)]
14608    struct Source {
14609        tokens: Vec<TestToken>,
14610        index: usize,
14611    }
14612
14613    impl TokenSource for Source {
14614        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
14615            let token = self
14616                .tokens
14617                .get(self.index)
14618                .cloned()
14619                .unwrap_or_else(|| TestToken::eof("parser-test", self.index, 1, self.index));
14620            self.index += 1;
14621            sink.push(token.spec)
14622        }
14623
14624        fn line(&self) -> usize {
14625            1
14626        }
14627
14628        fn column(&self) -> usize {
14629            self.index
14630        }
14631
14632        fn source_name(&self) -> &'static str {
14633            "parser-test"
14634        }
14635    }
14636
14637    #[derive(Clone, Debug, Eq, PartialEq)]
14638    struct RecordedDiagnostic {
14639        grammar_file_name: String,
14640        offending_text: Option<String>,
14641        line: usize,
14642        column: usize,
14643        span: Option<std::ops::Range<usize>>,
14644        message: String,
14645        error: Option<AntlrError>,
14646    }
14647
14648    #[derive(Clone, Debug)]
14649    struct RecordingErrorListener {
14650        diagnostics: Arc<Mutex<Vec<RecordedDiagnostic>>>,
14651    }
14652
14653    impl<R> crate::ErrorListener<R> for RecordingErrorListener
14654    where
14655        R: Recognizer + ?Sized,
14656    {
14657        fn syntax_error(&mut self, recognizer: &R, event: &SyntaxErrorEvent<'_>) {
14658            self.diagnostics
14659                .lock()
14660                .expect("recorded diagnostics lock")
14661                .push(RecordedDiagnostic {
14662                    grammar_file_name: recognizer.grammar_file_name().to_owned(),
14663                    offending_text: event
14664                        .offending
14665                        .and_then(|token| token.text().map(str::to_owned)),
14666                    line: event.line,
14667                    column: event.column,
14668                    span: event.span.clone(),
14669                    message: event.message.to_owned(),
14670                    error: event.error.cloned(),
14671                });
14672        }
14673    }
14674
14675    #[derive(Debug)]
14676    struct ReportingSource {
14677        source: Source,
14678        diagnostics: Rc<RefCell<Vec<TokenSourceError>>>,
14679    }
14680
14681    impl TokenSource for ReportingSource {
14682        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
14683            self.source.next_token(sink)
14684        }
14685
14686        fn line(&self) -> usize {
14687            self.source.line()
14688        }
14689
14690        fn column(&self) -> usize {
14691            self.source.column()
14692        }
14693
14694        fn source_name(&self) -> &str {
14695            self.source.source_name()
14696        }
14697
14698        fn report_error(&self, error: &TokenSourceError) -> bool {
14699            self.diagnostics.borrow_mut().push(error.clone());
14700            true
14701        }
14702    }
14703
14704    fn mini_parser_data() -> RecognizerData {
14705        RecognizerData::new(
14706            "Mini.g4",
14707            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
14708        )
14709        .with_rule_names(["s"])
14710    }
14711
14712    fn mini_parser(tokens: Vec<TestToken>) -> BaseParser<Source> {
14713        let data = mini_parser_data();
14714        BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
14715    }
14716
14717    fn mini_parser_with_hooks<H>(tokens: Vec<TestToken>, hooks: H) -> BaseParser<Source, H>
14718    where
14719        H: SemanticHooks,
14720    {
14721        BaseParser::with_semantic_hooks(
14722            CommonTokenStream::new(Source { tokens, index: 0 }),
14723            mini_parser_data(),
14724            hooks,
14725        )
14726    }
14727
14728    #[test]
14729    fn parser_dispatches_recovery_diagnostics_through_registered_listeners() {
14730        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
14731        parser.remove_error_listeners();
14732        let diagnostics = Arc::new(Mutex::new(Vec::new()));
14733        parser.add_error_listener(RecordingErrorListener {
14734            diagnostics: Arc::clone(&diagnostics),
14735        });
14736        let parser_diagnostics = [ParserDiagnostic {
14737            line: 1,
14738            column: 2,
14739            message: "missing 'x' at 'y'".to_owned(),
14740            offending: None,
14741        }];
14742        let token_errors = [
14743            TokenSourceError::new(1, 1, "token recognition error at: '@'").with_span(1..2),
14744            TokenSourceError::new(1, 3, "token recognition error at: '#'").with_span(3..4),
14745        ];
14746
14747        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
14748
14749        // The interleaved token/parser diagnostic stream (ordering, columns, messages) is one
14750        // reviewable snapshot instead of three hand-written RecordedDiagnostic literals.
14751        insta::assert_debug_snapshot!(
14752            "parser_dispatches_recovery_diagnostics_through_registered_listeners",
14753            *diagnostics.lock().expect("recorded diagnostics lock")
14754        );
14755
14756        parser.remove_error_listeners();
14757        parser.dispatch_generated_diagnostics(&parser_diagnostics, &token_errors);
14758        assert_eq!(
14759            diagnostics.lock().expect("recorded diagnostics lock").len(),
14760            3
14761        );
14762    }
14763
14764    #[test]
14765    fn recovery_diagnostics_expose_the_offending_token_to_listeners() {
14766        let mut parser = mini_parser(vec![
14767            TestToken::new(7)
14768                .with_text("oops")
14769                .with_span(0, 3)
14770                .with_byte_span(0, 4)
14771                .with_position(1, 2),
14772            TestToken::eof("parser-test", 4, 1, 6),
14773        ]);
14774        parser.remove_error_listeners();
14775        let diagnostics = Arc::new(Mutex::new(Vec::new()));
14776        parser.add_error_listener(RecordingErrorListener {
14777            diagnostics: Arc::clone(&diagnostics),
14778        });
14779        let offending = parser.input.lt_id(1);
14780        assert!(offending.is_some(), "current token should be buffered");
14781        let parser_diagnostics = [ParserDiagnostic {
14782            line: 1,
14783            column: 2,
14784            message: "extraneous input 'oops'".to_owned(),
14785            offending,
14786        }];
14787
14788        parser.dispatch_generated_diagnostics(&parser_diagnostics, &[]);
14789
14790        // Listeners receive a resolvable view of the offending token — the
14791        // ANTLR offendingSymbol contract downstream span-building error
14792        // reporters (miette-style byte-offset underlines) rely on.
14793        let recorded = diagnostics
14794            .lock()
14795            .expect("recorded diagnostics lock")
14796            .clone();
14797        insta::assert_debug_snapshot!(
14798            "recovery_diagnostics_expose_the_offending_token_to_listeners",
14799            recorded
14800        );
14801    }
14802
14803    #[test]
14804    fn recovery_diagnostics_preserve_unknown_custom_token_span() {
14805        let mut parser = mini_parser(vec![
14806            TestToken::new(7)
14807                .with_text("oops")
14808                .with_span(0, 3)
14809                .with_position(1, 2),
14810            TestToken::eof("parser-test", 4, 1, 6),
14811        ]);
14812        parser.remove_error_listeners();
14813        let diagnostics = Arc::new(Mutex::new(Vec::new()));
14814        parser.add_error_listener(RecordingErrorListener {
14815            diagnostics: Arc::clone(&diagnostics),
14816        });
14817        let offending = parser.input.lt_id(1);
14818        assert!(offending.is_some(), "current token should be buffered");
14819
14820        parser.dispatch_parser_diagnostic(&ParserDiagnostic {
14821            line: 1,
14822            column: 2,
14823            message: "extraneous input 'oops'".to_owned(),
14824            offending,
14825        });
14826
14827        let span = {
14828            let diagnostics = diagnostics.lock().expect("recorded diagnostics lock");
14829            assert_eq!(diagnostics.len(), 1);
14830            diagnostics[0].span.clone()
14831        };
14832        assert_eq!(span, None);
14833    }
14834
14835    #[test]
14836    fn parser_leaves_token_errors_to_source_owned_listeners() {
14837        let source_diagnostics = Rc::new(RefCell::new(Vec::new()));
14838        let source = ReportingSource {
14839            source: Source {
14840                tokens: vec![TestToken::eof("parser-test", 0, 1, 0)],
14841                index: 0,
14842            },
14843            diagnostics: Rc::clone(&source_diagnostics),
14844        };
14845        let mut parser = BaseParser::new(CommonTokenStream::new(source), mini_parser_data());
14846        parser.remove_error_listeners();
14847        let parser_diagnostics = Arc::new(Mutex::new(Vec::new()));
14848        parser.add_error_listener(RecordingErrorListener {
14849            diagnostics: Arc::clone(&parser_diagnostics),
14850        });
14851        let source_error = TokenSourceError::new(2, 4, "token recognition error at: '$'");
14852
14853        parser.dispatch_token_source_errors(std::slice::from_ref(&source_error));
14854
14855        assert_eq!(*source_diagnostics.borrow(), [source_error]);
14856        assert!(
14857            parser_diagnostics
14858                .lock()
14859                .expect("recorded diagnostics lock")
14860                .is_empty()
14861        );
14862    }
14863
14864    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
14865        builder.finish().expect("valid packed parser ATN")
14866    }
14867
14868    fn nested_rule_chain_atn(depth: usize) -> Atn {
14869        nested_rule_graph_atn(depth, false, false)
14870    }
14871
14872    fn nested_rule_graph_atn(depth: usize, branching: bool, consuming_follows: bool) -> Atn {
14873        assert!(depth > 0);
14874        let mut atn = ParserAtnBuilder::new(2);
14875        let mut starts = Vec::with_capacity(depth);
14876        let mut stops = Vec::with_capacity(depth);
14877        let mut follows = Vec::with_capacity(depth.saturating_sub(1));
14878        for rule_index in 0..depth {
14879            starts.push(
14880                atn.add_state(AtnStateKind::RuleStart, Some(rule_index))
14881                    .expect("rule start")
14882                    .index(),
14883            );
14884        }
14885        for rule_index in 0..depth {
14886            stops.push(
14887                atn.add_state(AtnStateKind::RuleStop, Some(rule_index))
14888                    .expect("rule stop")
14889                    .index(),
14890            );
14891        }
14892        if consuming_follows {
14893            for rule_index in 0..depth - 1 {
14894                follows.push(
14895                    atn.add_state(AtnStateKind::Basic, Some(rule_index))
14896                        .expect("rule follow")
14897                        .index(),
14898                );
14899            }
14900        }
14901        atn.set_rule_to_start_state(starts.clone())
14902            .expect("rule start states");
14903        atn.set_rule_to_stop_state(stops.clone())
14904            .expect("rule stop states");
14905        for rule_index in 0..depth - 1 {
14906            let follow_state = if consuming_follows {
14907                follows[rule_index]
14908            } else {
14909                stops[rule_index]
14910            };
14911            atn.add_transition(
14912                starts[rule_index],
14913                ParserTransitionSpec::Rule {
14914                    target: starts[rule_index + 1],
14915                    rule_index: rule_index + 1,
14916                    follow_state,
14917                    precedence: 0,
14918                },
14919            )
14920            .expect("nested rule transition");
14921            if branching {
14922                atn.add_transition(
14923                    starts[rule_index],
14924                    ParserTransitionSpec::Atom {
14925                        target: stops[rule_index],
14926                        label: 2,
14927                    },
14928                )
14929                .expect("dead branch transition");
14930            }
14931            if consuming_follows {
14932                atn.add_transition(
14933                    follow_state,
14934                    ParserTransitionSpec::Atom {
14935                        target: stops[rule_index],
14936                        label: 1,
14937                    },
14938                )
14939                .expect("consuming follow transition");
14940            }
14941        }
14942        let token_set = atn.add_interval_set([(1, 1)]).expect("token set");
14943        atn.add_transition(
14944            starts[depth - 1],
14945            ParserTransitionSpec::Set {
14946                target: stops[depth - 1],
14947                set: token_set,
14948            },
14949        )
14950        .expect("terminal set transition");
14951        if branching {
14952            atn.add_transition(
14953                starts[depth - 1],
14954                ParserTransitionSpec::Atom {
14955                    target: stops[depth - 1],
14956                    label: 2,
14957                },
14958            )
14959            .expect("dead leaf branch transition");
14960        }
14961        finish_atn(atn)
14962    }
14963
14964    fn ordinary_star_loop_atn() -> Atn {
14965        let mut atn = ParserAtnBuilder::new(2);
14966        for (state_number, kind, rule_index) in [
14967            (0, AtnStateKind::RuleStart, 0),
14968            (1, AtnStateKind::StarLoopEntry, 0),
14969            (2, AtnStateKind::Basic, 0),
14970            (3, AtnStateKind::StarLoopBack, 0),
14971            (4, AtnStateKind::LoopEnd, 0),
14972            (5, AtnStateKind::Basic, 0),
14973            (6, AtnStateKind::RuleStop, 0),
14974            (7, AtnStateKind::RuleStart, 1),
14975            (8, AtnStateKind::Basic, 1),
14976            (9, AtnStateKind::RuleStop, 1),
14977        ] {
14978            assert_eq!(
14979                atn.add_state(kind, Some(rule_index))
14980                    .expect("state")
14981                    .index(),
14982                state_number
14983            );
14984        }
14985        atn.set_rule_to_start_state(vec![0, 7])
14986            .expect("rule start states");
14987        atn.set_rule_to_stop_state(vec![6, 9])
14988            .expect("rule stop states");
14989        atn.add_decision_state(1).expect("decision state");
14990        atn.set_loop_back_state(4, 3).expect("loop back state");
14991        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
14992            .expect("transition");
14993        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
14994            .expect("transition");
14995        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
14996            .expect("transition");
14997        atn.add_transition(
14998            2,
14999            ParserTransitionSpec::Rule {
15000                target: 7,
15001                rule_index: 1,
15002                follow_state: 3,
15003                precedence: 0,
15004            },
15005        )
15006        .expect("transition");
15007        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 1 })
15008            .expect("transition");
15009        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
15010            .expect("transition");
15011        atn.add_transition(
15012            5,
15013            ParserTransitionSpec::Atom {
15014                target: 6,
15015                label: TOKEN_EOF,
15016            },
15017        )
15018        .expect("transition");
15019        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
15020            .expect("transition");
15021        atn.add_transition(
15022            8,
15023            ParserTransitionSpec::Atom {
15024                target: 9,
15025                label: 1,
15026            },
15027        )
15028        .expect("transition");
15029        finish_atn(atn)
15030    }
15031
15032    /// ATN for `s : (X | X X)* EOF`.
15033    fn ambiguous_ordinary_star_loop_atn() -> Atn {
15034        let mut atn = ParserAtnBuilder::new(1);
15035        for (state_number, kind) in [
15036            (0, AtnStateKind::RuleStart),
15037            (1, AtnStateKind::StarLoopEntry),
15038            (2, AtnStateKind::StarBlockStart),
15039            (3, AtnStateKind::Basic),
15040            (4, AtnStateKind::BlockEnd),
15041            (5, AtnStateKind::StarLoopBack),
15042            (6, AtnStateKind::LoopEnd),
15043            (7, AtnStateKind::Basic),
15044            (8, AtnStateKind::RuleStop),
15045        ] {
15046            assert_eq!(
15047                atn.add_state(kind, Some(0)).expect("state").index(),
15048                state_number
15049            );
15050        }
15051        atn.set_rule_to_start_state(vec![0])
15052            .expect("rule start states");
15053        atn.set_rule_to_stop_state(vec![8])
15054            .expect("rule stop states");
15055        atn.set_end_state(2, 4).expect("block end state");
15056        atn.set_loop_back_state(6, 5).expect("loop back state");
15057        atn.add_decision_state(1).expect("decision state");
15058        atn.add_decision_state(2).expect("decision state");
15059        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15060            .expect("transition");
15061        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15062            .expect("transition");
15063        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
15064            .expect("transition");
15065        atn.add_transition(
15066            2,
15067            ParserTransitionSpec::Atom {
15068                target: 4,
15069                label: 1,
15070            },
15071        )
15072        .expect("transition");
15073        atn.add_transition(
15074            2,
15075            ParserTransitionSpec::Atom {
15076                target: 3,
15077                label: 1,
15078            },
15079        )
15080        .expect("transition");
15081        atn.add_transition(
15082            3,
15083            ParserTransitionSpec::Atom {
15084                target: 4,
15085                label: 1,
15086            },
15087        )
15088        .expect("transition");
15089        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
15090            .expect("transition");
15091        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 1 })
15092            .expect("transition");
15093        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
15094            .expect("transition");
15095        atn.add_transition(
15096            7,
15097            ParserTransitionSpec::Atom {
15098                target: 8,
15099                label: TOKEN_EOF,
15100            },
15101        )
15102        .expect("transition");
15103        finish_atn(atn)
15104    }
15105
15106    fn ordinary_plus_loop_atn() -> Atn {
15107        let mut atn = ParserAtnBuilder::new(2);
15108        for (state_number, kind, rule_index) in [
15109            (0, AtnStateKind::RuleStart, 0),
15110            (1, AtnStateKind::Basic, 0),
15111            (2, AtnStateKind::PlusLoopBack, 0),
15112            (3, AtnStateKind::LoopEnd, 0),
15113            (4, AtnStateKind::Basic, 0),
15114            (5, AtnStateKind::RuleStop, 0),
15115            (6, AtnStateKind::RuleStart, 1),
15116            (7, AtnStateKind::Basic, 1),
15117            (8, AtnStateKind::RuleStop, 1),
15118        ] {
15119            assert_eq!(
15120                atn.add_state(kind, Some(rule_index))
15121                    .expect("state")
15122                    .index(),
15123                state_number
15124            );
15125        }
15126        atn.set_rule_to_start_state(vec![0, 6])
15127            .expect("rule start states");
15128        atn.set_rule_to_stop_state(vec![5, 8])
15129            .expect("rule stop states");
15130        atn.add_decision_state(2).expect("decision state");
15131        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15132            .expect("transition");
15133        atn.add_transition(
15134            1,
15135            ParserTransitionSpec::Rule {
15136                target: 6,
15137                rule_index: 1,
15138                follow_state: 2,
15139                precedence: 0,
15140            },
15141        )
15142        .expect("transition");
15143        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 1 })
15144            .expect("transition");
15145        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
15146            .expect("transition");
15147        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
15148            .expect("transition");
15149        atn.add_transition(
15150            4,
15151            ParserTransitionSpec::Atom {
15152                target: 5,
15153                label: TOKEN_EOF,
15154            },
15155        )
15156        .expect("transition");
15157        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
15158            .expect("transition");
15159        atn.add_transition(
15160            7,
15161            ParserTransitionSpec::Atom {
15162                target: 8,
15163                label: 1,
15164            },
15165        )
15166        .expect("transition");
15167        finish_atn(atn)
15168    }
15169
15170    fn repeated_x_tokens(count: usize) -> Vec<TestToken> {
15171        let mut tokens = (0..count)
15172            .map(|_| TestToken::new(1).with_text("x"))
15173            .collect::<Vec<_>>();
15174        tokens.push(TestToken::eof("parser-test", count, 1, count));
15175        tokens
15176    }
15177
15178    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
15179        let mut atn = ParserAtnBuilder::new(2);
15180        assert_eq!(
15181            atn.add_state(AtnStateKind::RuleStart, Some(0))
15182                .expect("state")
15183                .index(),
15184            0
15185        );
15186        assert_eq!(
15187            atn.add_state(AtnStateKind::Basic, Some(0))
15188                .expect("state")
15189                .index(),
15190            1
15191        );
15192        assert_eq!(
15193            atn.add_state(AtnStateKind::Basic, Some(0))
15194                .expect("state")
15195                .index(),
15196            2
15197        );
15198        assert_eq!(
15199            atn.add_state(AtnStateKind::RuleStart, Some(1))
15200                .expect("state")
15201                .index(),
15202            3
15203        );
15204        atn.set_left_recursive_rule(3)
15205            .expect("left-recursive rule start");
15206        assert_eq!(
15207            atn.add_state(AtnStateKind::StarLoopEntry, Some(1))
15208                .expect("state")
15209                .index(),
15210            4
15211        );
15212        atn.set_precedence_rule_decision(4)
15213            .expect("precedence decision");
15214        assert_eq!(
15215            atn.add_state(AtnStateKind::Basic, Some(1))
15216                .expect("state")
15217                .index(),
15218            5
15219        );
15220        assert_eq!(
15221            atn.add_state(AtnStateKind::Basic, Some(1))
15222                .expect("state")
15223                .index(),
15224            6
15225        );
15226        assert_eq!(
15227            atn.add_state(AtnStateKind::LoopEnd, Some(1))
15228                .expect("state")
15229                .index(),
15230            7
15231        );
15232        assert_eq!(
15233            atn.add_state(AtnStateKind::RuleStop, Some(1))
15234                .expect("state")
15235                .index(),
15236            8
15237        );
15238        assert_eq!(
15239            atn.add_state(AtnStateKind::RuleStop, Some(0))
15240                .expect("state")
15241                .index(),
15242            9
15243        );
15244        atn.set_rule_to_start_state(vec![0, 3])
15245            .expect("rule start states");
15246        atn.set_rule_to_stop_state(vec![9, 8])
15247            .expect("rule stop states");
15248        atn.add_transition(
15249            1,
15250            ParserTransitionSpec::Rule {
15251                target: 3,
15252                rule_index: 1,
15253                follow_state: 2,
15254                precedence: 0,
15255            },
15256        )
15257        .expect("transition");
15258        atn.add_transition(
15259            2,
15260            ParserTransitionSpec::Atom {
15261                target: 9,
15262                label: caller_symbol,
15263            },
15264        )
15265        .expect("transition");
15266        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
15267            .expect("transition");
15268        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 7 })
15269            .expect("transition");
15270        atn.add_transition(
15271            5,
15272            ParserTransitionSpec::Precedence {
15273                target: 6,
15274                precedence: 1,
15275            },
15276        )
15277        .expect("transition");
15278        atn.add_transition(
15279            6,
15280            ParserTransitionSpec::Atom {
15281                target: 4,
15282                label: 1,
15283            },
15284        )
15285        .expect("transition");
15286        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
15287            .expect("transition");
15288        finish_atn(atn)
15289    }
15290
15291    fn labeled_left_recursive_operator_atn() -> Atn {
15292        let mut atn = ParserAtnBuilder::new(4);
15293        for (state, kind) in [
15294            (0, AtnStateKind::RuleStart),
15295            (1, AtnStateKind::BlockStart),
15296            (2, AtnStateKind::StarLoopEntry),
15297            (3, AtnStateKind::StarBlockStart),
15298            (4, AtnStateKind::Basic),
15299            (5, AtnStateKind::Basic),
15300            (6, AtnStateKind::Basic),
15301            (7, AtnStateKind::StarLoopBack),
15302            (8, AtnStateKind::LoopEnd),
15303            (9, AtnStateKind::RuleStop),
15304        ] {
15305            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
15306        }
15307        atn.set_left_recursive_rule(0)
15308            .expect("left-recursive rule start");
15309        atn.set_precedence_rule_decision(2)
15310            .expect("precedence decision");
15311        atn.set_loop_back_state(8, 7).expect("loop-back state");
15312        atn.set_rule_to_start_state(vec![0])
15313            .expect("rule start states");
15314        atn.set_rule_to_stop_state(vec![9])
15315            .expect("rule stop states");
15316        for state in [1, 2, 3] {
15317            atn.add_decision_state(state).expect("decision state");
15318        }
15319        for (source, target) in [(0, 1), (2, 3), (2, 8), (7, 2), (8, 9)] {
15320            atn.add_transition(source, ParserTransitionSpec::Epsilon { target })
15321                .expect("epsilon transition");
15322        }
15323        for (source, target, label) in [(1, 2, 1), (1, 2, 2), (4, 6, 4), (5, 6, 3), (6, 7, 1)] {
15324            atn.add_transition(source, ParserTransitionSpec::Atom { target, label })
15325                .expect("token transition");
15326        }
15327        for (target, precedence) in [(4, 2), (5, 1)] {
15328            atn.add_transition(3, ParserTransitionSpec::Precedence { target, precedence })
15329                .expect("operator precedence");
15330        }
15331        finish_atn(atn)
15332    }
15333
15334    fn parser_inside_left_recursive_callee(symbol: i32) -> BaseParser<Source> {
15335        let mut parser = mini_parser(vec![
15336            TestToken::new(symbol).with_text("lookahead"),
15337            TestToken::eof("parser-test", 1, 1, 1),
15338        ]);
15339        parser.rule_context_stack = vec![
15340            RuleContextFrame {
15341                rule_index: 0,
15342                invoking_state: -1,
15343            },
15344            RuleContextFrame {
15345                rule_index: 1,
15346                invoking_state: 1,
15347            },
15348        ];
15349        parser
15350    }
15351
15352    fn left_recursive_loop_with_shared_gt_prefix_atn() -> Atn {
15353        // StarLoopEntry with two operator alts that share leading token 1 (`>`):
15354        //   prec 2: token 1, token 1  (shift `>>`)
15355        //   prec 1: token 1           (relational `>`)
15356        let mut atn = ParserAtnBuilder::new(1);
15357        for (state, kind, rule) in [
15358            (0, AtnStateKind::RuleStart, 0),
15359            (1, AtnStateKind::StarLoopEntry, 0),
15360            (2, AtnStateKind::Basic, 0), // ops hub
15361            (3, AtnStateKind::Basic, 0), // shift prec
15362            (4, AtnStateKind::Basic, 0), // shift first >
15363            (5, AtnStateKind::Basic, 0), // shift second >
15364            (6, AtnStateKind::Basic, 0), // rel prec
15365            (7, AtnStateKind::Basic, 0), // rel >
15366            (8, AtnStateKind::LoopEnd, 0),
15367            (9, AtnStateKind::RuleStop, 0),
15368        ] {
15369            assert_eq!(
15370                atn.add_state(kind, Some(rule)).expect("state").index(),
15371                state
15372            );
15373            if state == 0 {
15374                atn.set_left_recursive_rule(state)
15375                    .expect("left-recursive rule start");
15376            } else if state == 1 {
15377                atn.set_precedence_rule_decision(state)
15378                    .expect("precedence decision");
15379            }
15380        }
15381        atn.set_rule_to_start_state(vec![0])
15382            .expect("rule start states");
15383        atn.set_rule_to_stop_state(vec![9])
15384            .expect("rule stop states");
15385        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15386            .expect("ops");
15387        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
15388            .expect("exit");
15389        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
15390            .expect("to shift");
15391        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
15392            .expect("to rel");
15393        atn.add_transition(
15394            3,
15395            ParserTransitionSpec::Precedence {
15396                target: 4,
15397                precedence: 2,
15398            },
15399        )
15400        .expect("shift prec");
15401        atn.add_transition(
15402            4,
15403            ParserTransitionSpec::Atom {
15404                target: 5,
15405                label: 1,
15406            },
15407        )
15408        .expect("shift first >");
15409        atn.add_transition(
15410            5,
15411            ParserTransitionSpec::Atom {
15412                target: 1,
15413                label: 1,
15414            },
15415        )
15416        .expect("shift second >");
15417        atn.add_transition(
15418            6,
15419            ParserTransitionSpec::Precedence {
15420                target: 7,
15421                precedence: 1,
15422            },
15423        )
15424        .expect("rel prec");
15425        atn.add_transition(
15426            7,
15427            ParserTransitionSpec::Atom {
15428                target: 1,
15429                label: 1,
15430            },
15431        )
15432        .expect("rel >");
15433        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
15434            .expect("loop end");
15435        finish_atn(atn)
15436    }
15437
15438    fn left_recursive_loop_with_rule_wrapped_gt_prefix_atn() -> Atn {
15439        let mut atn = ParserAtnBuilder::new(2);
15440        for (state, kind, rule) in [
15441            (0, AtnStateKind::RuleStart, 0),
15442            (1, AtnStateKind::StarLoopEntry, 0),
15443            (2, AtnStateKind::Basic, 0),
15444            (3, AtnStateKind::Basic, 0),
15445            (4, AtnStateKind::Basic, 0),
15446            (5, AtnStateKind::Basic, 0),
15447            (6, AtnStateKind::Basic, 0),
15448            (7, AtnStateKind::Basic, 0),
15449            (8, AtnStateKind::LoopEnd, 0),
15450            (9, AtnStateKind::RuleStop, 0),
15451            (10, AtnStateKind::RuleStart, 1),
15452            (11, AtnStateKind::Basic, 1),
15453            (12, AtnStateKind::RuleStop, 1),
15454        ] {
15455            assert_eq!(
15456                atn.add_state(kind, Some(rule)).expect("state").index(),
15457                state
15458            );
15459            if state == 0 {
15460                atn.set_left_recursive_rule(state)
15461                    .expect("left-recursive rule start");
15462            } else if state == 1 {
15463                atn.set_precedence_rule_decision(state)
15464                    .expect("precedence decision");
15465            }
15466        }
15467        atn.set_rule_to_start_state(vec![0, 10])
15468            .expect("rule start states");
15469        atn.set_rule_to_stop_state(vec![9, 12])
15470            .expect("rule stop states");
15471        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15472            .expect("ops");
15473        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 8 })
15474            .expect("exit");
15475        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
15476            .expect("to shift");
15477        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
15478            .expect("to relational");
15479        atn.add_transition(
15480            3,
15481            ParserTransitionSpec::Precedence {
15482                target: 4,
15483                precedence: 2,
15484            },
15485        )
15486        .expect("shift precedence");
15487        atn.add_transition(
15488            4,
15489            ParserTransitionSpec::Rule {
15490                target: 10,
15491                rule_index: 1,
15492                follow_state: 5,
15493                precedence: 0,
15494            },
15495        )
15496        .expect("first shift token helper");
15497        atn.add_transition(
15498            5,
15499            ParserTransitionSpec::Atom {
15500                target: 1,
15501                label: 1,
15502            },
15503        )
15504        .expect("second shift token");
15505        atn.add_transition(
15506            6,
15507            ParserTransitionSpec::Precedence {
15508                target: 7,
15509                precedence: 1,
15510            },
15511        )
15512        .expect("relational precedence");
15513        atn.add_transition(
15514            7,
15515            ParserTransitionSpec::Atom {
15516                target: 1,
15517                label: 1,
15518            },
15519        )
15520        .expect("relational token");
15521        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
15522            .expect("loop end");
15523        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
15524            .expect("helper entry");
15525        atn.add_transition(
15526            11,
15527            ParserTransitionSpec::Atom {
15528                target: 12,
15529                label: 1,
15530            },
15531        )
15532        .expect("first shift token");
15533        finish_atn(atn)
15534    }
15535
15536    fn left_recursive_loop_with_predicate_and_multi_token_prefix_atn() -> Atn {
15537        let mut atn = ParserAtnBuilder::new(1);
15538        for (state, kind) in [
15539            (0, AtnStateKind::RuleStart),
15540            (1, AtnStateKind::StarLoopEntry),
15541            (2, AtnStateKind::Basic),
15542            (3, AtnStateKind::Basic),
15543            (4, AtnStateKind::Basic),
15544            (5, AtnStateKind::Basic),
15545            (6, AtnStateKind::Basic),
15546            (7, AtnStateKind::Basic),
15547            (8, AtnStateKind::Basic),
15548            (9, AtnStateKind::LoopEnd),
15549            (10, AtnStateKind::RuleStop),
15550        ] {
15551            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
15552            if state == 0 {
15553                atn.set_left_recursive_rule(state)
15554                    .expect("left-recursive rule start");
15555            } else if state == 1 {
15556                atn.set_precedence_rule_decision(state)
15557                    .expect("precedence decision");
15558            }
15559        }
15560        atn.set_rule_to_start_state(vec![0])
15561            .expect("rule start states");
15562        atn.set_rule_to_stop_state(vec![10])
15563            .expect("rule stop states");
15564        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15565            .expect("ops");
15566        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 9 })
15567            .expect("exit");
15568        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
15569            .expect("to multi-token operator");
15570        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 6 })
15571            .expect("to predicate operator");
15572        atn.add_transition(
15573            3,
15574            ParserTransitionSpec::Precedence {
15575                target: 4,
15576                precedence: 2,
15577            },
15578        )
15579        .expect("multi-token precedence");
15580        atn.add_transition(
15581            4,
15582            ParserTransitionSpec::Atom {
15583                target: 5,
15584                label: 1,
15585            },
15586        )
15587        .expect("multi-token first");
15588        atn.add_transition(
15589            5,
15590            ParserTransitionSpec::Atom {
15591                target: 1,
15592                label: 1,
15593            },
15594        )
15595        .expect("multi-token second");
15596        atn.add_transition(
15597            6,
15598            ParserTransitionSpec::Precedence {
15599                target: 7,
15600                precedence: 2,
15601            },
15602        )
15603        .expect("predicate precedence");
15604        atn.add_transition(
15605            7,
15606            ParserTransitionSpec::Predicate {
15607                target: 8,
15608                rule_index: 0,
15609                pred_index: 0,
15610                context_dependent: false,
15611            },
15612        )
15613        .expect("operator predicate");
15614        atn.add_transition(
15615            8,
15616            ParserTransitionSpec::Atom {
15617                target: 1,
15618                label: 1,
15619            },
15620        )
15621        .expect("predicate single token");
15622        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
15623            .expect("loop end");
15624        finish_atn(atn)
15625    }
15626
15627    fn left_recursive_loop_with_nullable_operator_prefix_atn() -> Atn {
15628        let mut atn = ParserAtnBuilder::new(2);
15629        for (state, kind, rule) in [
15630            (0, AtnStateKind::RuleStart, 0),
15631            (1, AtnStateKind::StarLoopEntry, 0),
15632            (2, AtnStateKind::Basic, 0),
15633            (3, AtnStateKind::Basic, 0),
15634            (4, AtnStateKind::Basic, 0),
15635            (5, AtnStateKind::LoopEnd, 0),
15636            (6, AtnStateKind::RuleStop, 0),
15637            (7, AtnStateKind::RuleStart, 1),
15638            (8, AtnStateKind::RuleStop, 1),
15639            (9, AtnStateKind::Basic, 1),
15640        ] {
15641            assert_eq!(
15642                atn.add_state(kind, Some(rule)).expect("state").index(),
15643                state
15644            );
15645            if state == 0 {
15646                atn.set_left_recursive_rule(state)
15647                    .expect("left-recursive rule start");
15648            } else if state == 1 {
15649                atn.set_precedence_rule_decision(state)
15650                    .expect("precedence decision");
15651            }
15652        }
15653        atn.set_rule_to_start_state(vec![0, 7])
15654            .expect("rule start states");
15655        atn.set_rule_to_stop_state(vec![6, 8])
15656            .expect("rule stop states");
15657        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15658            .expect("transition");
15659        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
15660            .expect("transition");
15661        atn.add_transition(
15662            2,
15663            ParserTransitionSpec::Precedence {
15664                target: 3,
15665                precedence: 3,
15666            },
15667        )
15668        .expect("transition");
15669        atn.add_transition(
15670            3,
15671            ParserTransitionSpec::Rule {
15672                target: 7,
15673                rule_index: 1,
15674                follow_state: 4,
15675                precedence: 0,
15676            },
15677        )
15678        .expect("transition");
15679        atn.add_transition(
15680            4,
15681            ParserTransitionSpec::Atom {
15682                target: 1,
15683                label: 1,
15684            },
15685        )
15686        .expect("transition");
15687        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
15688            .expect("transition");
15689        atn.add_transition(
15690            7,
15691            ParserTransitionSpec::Precedence {
15692                target: 9,
15693                precedence: 1,
15694            },
15695        )
15696        .expect("transition");
15697        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 8 })
15698            .expect("transition");
15699        finish_atn(atn)
15700    }
15701
15702    fn left_recursive_loop_with_predicate_guarded_operator_atn() -> Atn {
15703        let mut atn = ParserAtnBuilder::new(2);
15704        for (state, kind) in [
15705            (0, AtnStateKind::RuleStart),
15706            (1, AtnStateKind::StarLoopEntry),
15707            (2, AtnStateKind::Basic),
15708            (3, AtnStateKind::Basic),
15709            (4, AtnStateKind::Basic),
15710            (5, AtnStateKind::LoopEnd),
15711            (6, AtnStateKind::RuleStop),
15712        ] {
15713            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
15714            if state == 0 {
15715                atn.set_left_recursive_rule(state)
15716                    .expect("left-recursive rule start");
15717            } else if state == 1 {
15718                atn.set_precedence_rule_decision(state)
15719                    .expect("precedence decision");
15720            }
15721        }
15722        atn.set_rule_to_start_state(vec![0])
15723            .expect("rule start states");
15724        atn.set_rule_to_stop_state(vec![6])
15725            .expect("rule stop states");
15726        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
15727            .expect("transition");
15728        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
15729            .expect("transition");
15730        atn.add_transition(
15731            2,
15732            ParserTransitionSpec::Precedence {
15733                target: 3,
15734                precedence: 1,
15735            },
15736        )
15737        .expect("transition");
15738        atn.add_transition(
15739            3,
15740            ParserTransitionSpec::Predicate {
15741                target: 4,
15742                rule_index: 0,
15743                pred_index: 0,
15744                context_dependent: false,
15745            },
15746        )
15747        .expect("transition");
15748        atn.add_transition(
15749            4,
15750            ParserTransitionSpec::Atom {
15751                target: 1,
15752                label: 1,
15753            },
15754        )
15755        .expect("transition");
15756        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
15757            .expect("transition");
15758        finish_atn(atn)
15759    }
15760
15761    fn left_recursive_loop_with_nullable_follow_call_atn(caller_symbol: i32) -> Atn {
15762        let mut atn = ParserAtnBuilder::new(2);
15763        for (state, kind, rule) in [
15764            (0, AtnStateKind::RuleStart, 0),
15765            (1, AtnStateKind::Basic, 0),
15766            (2, AtnStateKind::Basic, 0),
15767            (3, AtnStateKind::Basic, 0),
15768            (4, AtnStateKind::RuleStop, 0),
15769            (5, AtnStateKind::RuleStart, 1),
15770            (6, AtnStateKind::StarLoopEntry, 1),
15771            (7, AtnStateKind::Basic, 1),
15772            (8, AtnStateKind::Basic, 1),
15773            (9, AtnStateKind::LoopEnd, 1),
15774            (10, AtnStateKind::RuleStop, 1),
15775            (11, AtnStateKind::RuleStart, 2),
15776            (12, AtnStateKind::RuleStop, 2),
15777        ] {
15778            assert_eq!(
15779                atn.add_state(kind, Some(rule)).expect("state").index(),
15780                state
15781            );
15782            if state == 5 {
15783                atn.set_left_recursive_rule(state)
15784                    .expect("left-recursive rule start");
15785            } else if state == 6 {
15786                atn.set_precedence_rule_decision(state)
15787                    .expect("precedence decision");
15788            }
15789        }
15790        atn.set_rule_to_start_state(vec![0, 5, 11])
15791            .expect("rule start states");
15792        atn.set_rule_to_stop_state(vec![4, 10, 12])
15793            .expect("rule stop states");
15794        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15795            .expect("transition");
15796        atn.add_transition(
15797            1,
15798            ParserTransitionSpec::Rule {
15799                target: 5,
15800                rule_index: 1,
15801                follow_state: 2,
15802                precedence: 0,
15803            },
15804        )
15805        .expect("transition");
15806        atn.add_transition(
15807            2,
15808            ParserTransitionSpec::Rule {
15809                target: 11,
15810                rule_index: 2,
15811                follow_state: 3,
15812                precedence: 0,
15813            },
15814        )
15815        .expect("transition");
15816        atn.add_transition(
15817            3,
15818            ParserTransitionSpec::Atom {
15819                target: 4,
15820                label: caller_symbol,
15821            },
15822        )
15823        .expect("transition");
15824        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
15825            .expect("transition");
15826        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 9 })
15827            .expect("transition");
15828        atn.add_transition(
15829            7,
15830            ParserTransitionSpec::Precedence {
15831                target: 8,
15832                precedence: 1,
15833            },
15834        )
15835        .expect("transition");
15836        atn.add_transition(
15837            8,
15838            ParserTransitionSpec::Atom {
15839                target: 6,
15840                label: 1,
15841            },
15842        )
15843        .expect("transition");
15844        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
15845            .expect("transition");
15846        atn.add_transition(11, ParserTransitionSpec::Epsilon { target: 12 })
15847            .expect("transition");
15848        finish_atn(atn)
15849    }
15850
15851    fn left_recursive_loop_with_nullable_parent_return_atn(caller_symbol: i32) -> Atn {
15852        let mut atn = ParserAtnBuilder::new(2);
15853        for (state, kind, rule) in [
15854            (0, AtnStateKind::RuleStart, 0),
15855            (1, AtnStateKind::Basic, 0),
15856            (2, AtnStateKind::Basic, 0),
15857            (3, AtnStateKind::RuleStop, 0),
15858            (4, AtnStateKind::RuleStart, 1),
15859            (5, AtnStateKind::Basic, 1),
15860            (6, AtnStateKind::Basic, 1),
15861            (7, AtnStateKind::RuleStop, 1),
15862            (8, AtnStateKind::RuleStart, 2),
15863            (9, AtnStateKind::StarLoopEntry, 2),
15864            (10, AtnStateKind::Basic, 2),
15865            (11, AtnStateKind::Basic, 2),
15866            (12, AtnStateKind::LoopEnd, 2),
15867            (13, AtnStateKind::RuleStop, 2),
15868        ] {
15869            assert_eq!(
15870                atn.add_state(kind, Some(rule)).expect("state").index(),
15871                state
15872            );
15873            if state == 8 {
15874                atn.set_left_recursive_rule(state)
15875                    .expect("left-recursive rule start");
15876            } else if state == 9 {
15877                atn.set_precedence_rule_decision(state)
15878                    .expect("precedence decision");
15879            }
15880        }
15881        atn.set_rule_to_start_state(vec![0, 4, 8])
15882            .expect("rule start states");
15883        atn.set_rule_to_stop_state(vec![3, 7, 13])
15884            .expect("rule stop states");
15885        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15886            .expect("transition");
15887        atn.add_transition(
15888            1,
15889            ParserTransitionSpec::Rule {
15890                target: 4,
15891                rule_index: 1,
15892                follow_state: 2,
15893                precedence: 0,
15894            },
15895        )
15896        .expect("transition");
15897        atn.add_transition(
15898            2,
15899            ParserTransitionSpec::Atom {
15900                target: 3,
15901                label: caller_symbol,
15902            },
15903        )
15904        .expect("transition");
15905        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
15906            .expect("transition");
15907        atn.add_transition(
15908            5,
15909            ParserTransitionSpec::Rule {
15910                target: 8,
15911                rule_index: 2,
15912                follow_state: 6,
15913                precedence: 0,
15914            },
15915        )
15916        .expect("transition");
15917        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
15918            .expect("transition");
15919        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
15920            .expect("transition");
15921        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 12 })
15922            .expect("transition");
15923        atn.add_transition(
15924            10,
15925            ParserTransitionSpec::Precedence {
15926                target: 11,
15927                precedence: 1,
15928            },
15929        )
15930        .expect("transition");
15931        atn.add_transition(
15932            11,
15933            ParserTransitionSpec::Atom {
15934                target: 9,
15935                label: 1,
15936            },
15937        )
15938        .expect("transition");
15939        atn.add_transition(12, ParserTransitionSpec::Epsilon { target: 13 })
15940            .expect("transition");
15941        finish_atn(atn)
15942    }
15943
15944    fn left_recursive_loop_with_recursive_operand_return_atn(caller_symbol: i32) -> Atn {
15945        let mut atn = ParserAtnBuilder::new(2);
15946        for (state, kind, rule) in [
15947            (0, AtnStateKind::RuleStart, 0),
15948            (1, AtnStateKind::Basic, 0),
15949            (2, AtnStateKind::Basic, 0),
15950            (3, AtnStateKind::RuleStop, 0),
15951            (4, AtnStateKind::RuleStart, 1),
15952            (5, AtnStateKind::StarLoopEntry, 1),
15953            (6, AtnStateKind::Basic, 1),
15954            (7, AtnStateKind::Basic, 1),
15955            (8, AtnStateKind::Basic, 1),
15956            (9, AtnStateKind::Basic, 1),
15957            (10, AtnStateKind::LoopEnd, 1),
15958            (11, AtnStateKind::RuleStop, 1),
15959        ] {
15960            assert_eq!(
15961                atn.add_state(kind, Some(rule)).expect("state").index(),
15962                state
15963            );
15964            if state == 4 {
15965                atn.set_left_recursive_rule(state)
15966                    .expect("left-recursive rule start");
15967            } else if state == 5 {
15968                atn.set_precedence_rule_decision(state)
15969                    .expect("precedence decision");
15970            }
15971        }
15972        atn.set_rule_to_start_state(vec![0, 4])
15973            .expect("rule start states");
15974        atn.set_rule_to_stop_state(vec![3, 11])
15975            .expect("rule stop states");
15976        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
15977            .expect("transition");
15978        atn.add_transition(
15979            1,
15980            ParserTransitionSpec::Rule {
15981                target: 4,
15982                rule_index: 1,
15983                follow_state: 2,
15984                precedence: 0,
15985            },
15986        )
15987        .expect("transition");
15988        atn.add_transition(
15989            2,
15990            ParserTransitionSpec::Atom {
15991                target: 3,
15992                label: caller_symbol,
15993            },
15994        )
15995        .expect("transition");
15996        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
15997            .expect("transition");
15998        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
15999            .expect("transition");
16000        atn.add_transition(
16001            6,
16002            ParserTransitionSpec::Precedence {
16003                target: 7,
16004                precedence: 1,
16005            },
16006        )
16007        .expect("transition");
16008        atn.add_transition(
16009            7,
16010            ParserTransitionSpec::Atom {
16011                target: 8,
16012                label: 1,
16013            },
16014        )
16015        .expect("transition");
16016        atn.add_transition(
16017            8,
16018            ParserTransitionSpec::Rule {
16019                target: 4,
16020                rule_index: 1,
16021                follow_state: 9,
16022                precedence: 2,
16023            },
16024        )
16025        .expect("transition");
16026        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 5 })
16027            .expect("transition");
16028        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
16029            .expect("transition");
16030        finish_atn(atn)
16031    }
16032
16033    #[test]
16034    fn left_recursive_loop_defers_overlapping_caller_lookahead() {
16035        let overlapping_atn = left_recursive_loop_with_caller_follow_atn(1);
16036        let unambiguous_atn = left_recursive_loop_with_caller_follow_atn(2);
16037
16038        let mut overlapping = parser_inside_left_recursive_callee(1);
16039        assert_eq!(
16040            overlapping.left_recursive_loop_enter_prediction(&overlapping_atn, 4, 0),
16041            None
16042        );
16043
16044        let mut unambiguous_enter = parser_inside_left_recursive_callee(1);
16045        assert_eq!(
16046            unambiguous_enter.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
16047            Some(true)
16048        );
16049
16050        let mut unambiguous_exit = parser_inside_left_recursive_callee(2);
16051        assert_eq!(
16052            unambiguous_exit.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
16053            Some(false)
16054        );
16055
16056        assert_eq!(
16057            overlapping.left_recursive_loop_enter_prediction(&unambiguous_atn, 4, 0),
16058            Some(true),
16059            "overlap results must not leak across ATNs"
16060        );
16061    }
16062
16063    #[test]
16064    fn left_recursive_loop_enters_after_nullable_operator_prefix() {
16065        let atn = left_recursive_loop_with_nullable_operator_prefix_atn();
16066        let mut parser = mini_parser(vec![
16067            TestToken::new(1).with_text("operator"),
16068            TestToken::eof("parser-test", 1, 1, 1),
16069        ]);
16070        parser.rule_context_stack = vec![RuleContextFrame {
16071            rule_index: 0,
16072            invoking_state: -1,
16073        }];
16074
16075        assert_eq!(
16076            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
16077            Some(true)
16078        );
16079        assert_eq!(
16080            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
16081            Some(true),
16082            "cached operator lookahead must preserve the nullable prefix return path"
16083        );
16084        assert_eq!(
16085            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
16086            Some(true),
16087            "the nullable child must use its rule-call precedence, not the caller precedence"
16088        );
16089    }
16090
16091    #[test]
16092    fn left_recursive_loop_defers_multi_token_prefix_that_shadows_lower_single_token() {
16093        // Models Java `>` (relational, prec 1, one token) vs `>>` (shift, prec 2,
16094        // two tokens). At prec 2 only shift is viable; one-token lookahead on `>`
16095        // must defer so StarLoopEntry adaptive predict can exit when the second
16096        // `>` is absent (as in `a < b > c`).
16097        let atn = left_recursive_loop_with_shared_gt_prefix_atn();
16098        let mut parser = mini_parser(vec![
16099            TestToken::new(1).with_text(">"),
16100            TestToken::new(2).with_text("id"),
16101            TestToken::eof("parser-test", 1, 1, 1),
16102        ]);
16103        parser.rule_context_stack = vec![RuleContextFrame {
16104            rule_index: 0,
16105            invoking_state: -1,
16106        }];
16107
16108        assert_eq!(
16109            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
16110            Some(true),
16111            "at low precedence relational `>` is a single-token operator"
16112        );
16113        assert_eq!(
16114            parser.left_recursive_loop_enter_prediction(&atn, 1, 1),
16115            Some(true),
16116            "relational remains single-token at its own precedence"
16117        );
16118        assert_eq!(
16119            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
16120            None,
16121            "at shift precedence, bare `>` must not force enter"
16122        );
16123    }
16124
16125    #[test]
16126    fn left_recursive_loop_preserves_rule_wrapped_operator_continuation() {
16127        let atn = left_recursive_loop_with_rule_wrapped_gt_prefix_atn();
16128        let mut parser = mini_parser(vec![
16129            TestToken::new(1).with_text(">"),
16130            TestToken::new(2).with_text("id"),
16131            TestToken::eof("parser-test", 1, 1, 1),
16132        ]);
16133        parser.rule_context_stack = vec![RuleContextFrame {
16134            rule_index: 0,
16135            invoking_state: -1,
16136        }];
16137
16138        assert_eq!(
16139            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
16140            Some(true),
16141            "the direct relational alternative remains a one-token operator"
16142        );
16143        assert_eq!(
16144            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
16145            None,
16146            "a token matched in the helper rule must return to the second shift token"
16147        );
16148    }
16149
16150    #[test]
16151    fn left_recursive_loop_preserves_predicate_and_multi_token_reachability() {
16152        let atn = left_recursive_loop_with_predicate_and_multi_token_prefix_atn();
16153        let mut parser = mini_parser(vec![
16154            TestToken::new(1).with_text(">"),
16155            TestToken::new(2).with_text("id"),
16156            TestToken::eof("parser-test", 1, 1, 1),
16157        ]);
16158        parser.rule_context_stack = vec![RuleContextFrame {
16159            rule_index: 0,
16160            invoking_state: -1,
16161        }];
16162
16163        assert_eq!(
16164            parser.left_recursive_loop_enter_prediction(&atn, 1, 2),
16165            None,
16166            "a predicate-gated single-token path must not be hidden by a multi-token path"
16167        );
16168    }
16169
16170    #[test]
16171    fn left_recursive_loop_defers_predicate_guarded_operator() {
16172        let atn = left_recursive_loop_with_predicate_guarded_operator_atn();
16173        let mut parser = mini_parser_with_hooks(
16174            vec![
16175                TestToken::new(1).with_text("operator"),
16176                TestToken::eof("parser-test", 1, 1, 1),
16177            ],
16178            RejectingPredicateHooks::default(),
16179        );
16180        parser.rule_context_stack = vec![RuleContextFrame {
16181            rule_index: 0,
16182            invoking_state: -1,
16183        }];
16184
16185        assert_eq!(
16186            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
16187            None,
16188            "a false predicate must be evaluated before entering the operator alternative"
16189        );
16190        assert_eq!(
16191            parser.left_recursive_loop_enter_prediction(&atn, 1, 0),
16192            None,
16193            "cached predicate-dependent lookahead must keep deferring"
16194        );
16195    }
16196
16197    #[test]
16198    fn left_recursive_loop_defers_through_nullable_caller_rule_call() {
16199        let atn = left_recursive_loop_with_nullable_follow_call_atn(1);
16200        let mut parser = parser_inside_left_recursive_callee(1);
16201
16202        assert_eq!(
16203            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
16204            None
16205        );
16206        assert_eq!(
16207            parser.left_recursive_loop_enter_prediction(&atn, 6, 0),
16208            None,
16209            "the cached overlap must preserve the nullable child return path"
16210        );
16211    }
16212
16213    #[test]
16214    fn left_recursive_loop_defers_through_nullable_parent_return() {
16215        let atn = left_recursive_loop_with_nullable_parent_return_atn(1);
16216        let mut parser = mini_parser(vec![
16217            TestToken::new(1).with_text("lookahead"),
16218            TestToken::eof("parser-test", 1, 1, 1),
16219        ]);
16220        parser.rule_context_stack = vec![
16221            RuleContextFrame {
16222                rule_index: 0,
16223                invoking_state: -1,
16224            },
16225            RuleContextFrame {
16226                rule_index: 1,
16227                invoking_state: 1,
16228            },
16229            RuleContextFrame {
16230                rule_index: 2,
16231                invoking_state: 5,
16232            },
16233        ];
16234
16235        assert_eq!(
16236            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
16237            None,
16238            "a nullable caller must unwind to its parent's consuming follow path"
16239        );
16240        assert_eq!(
16241            parser.left_recursive_loop_enter_prediction(&atn, 9, 0),
16242            None,
16243            "the caller-overlap cache must not retain a false negative"
16244        );
16245    }
16246
16247    #[test]
16248    fn left_recursive_loop_defers_after_recursive_operand_returns_to_loop() {
16249        let atn = left_recursive_loop_with_recursive_operand_return_atn(1);
16250        let mut parser = mini_parser(vec![
16251            TestToken::new(1).with_text("lookahead"),
16252            TestToken::eof("parser-test", 1, 1, 1),
16253        ]);
16254        parser.rule_context_stack = vec![
16255            RuleContextFrame {
16256                rule_index: 0,
16257                invoking_state: -1,
16258            },
16259            RuleContextFrame {
16260                rule_index: 1,
16261                invoking_state: 1,
16262            },
16263            RuleContextFrame {
16264                rule_index: 1,
16265                invoking_state: 8,
16266            },
16267        ];
16268
16269        assert_eq!(
16270            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
16271            None,
16272            "a recursive operand return must preserve its parent caller context"
16273        );
16274        assert_eq!(
16275            parser.left_recursive_loop_enter_prediction(&atn, 5, 0),
16276            None,
16277            "the caller-overlap cache must preserve the loop-boundary return"
16278        );
16279    }
16280
16281    fn token_then_eof_atn() -> Atn {
16282        AtnDeserializer::new(&SerializedAtn::from_i32(&[
16283            4, 1, 2, // version, parser, max token type
16284            3, // states
16285            2, 0, // rule start
16286            1, 0, // basic
16287            7, 0, // rule stop
16288            0, // non-greedy states
16289            0, // precedence states
16290            1, // rules
16291            0, // rule 0 start
16292            0, // modes
16293            0, // sets
16294            2, // transitions
16295            0, 1, 5, 1, 0, 0, // match token 1
16296            1, 2, 5, -1, 0, 0, // match EOF
16297            0, // decisions
16298        ]))
16299        .deserialize_parser()
16300        .expect("artificial parser ATN should deserialize")
16301    }
16302
16303    fn epsilon_cycle_atn() -> Atn {
16304        let mut atn = ParserAtnBuilder::new(1);
16305        for (state_number, kind) in [
16306            (0, AtnStateKind::RuleStart),
16307            (1, AtnStateKind::Basic),
16308            (2, AtnStateKind::RuleStop),
16309        ] {
16310            assert_eq!(
16311                atn.add_state(kind, Some(0)).expect("state").index(),
16312                state_number
16313            );
16314        }
16315        atn.set_rule_to_start_state(vec![0])
16316            .expect("rule start states");
16317        atn.set_rule_to_stop_state(vec![2])
16318            .expect("rule stop states");
16319        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
16320            .expect("transition");
16321        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
16322            .expect("self-cycle transition");
16323        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
16324            .expect("exit transition");
16325        finish_atn(atn)
16326    }
16327
16328    fn committed_non_consuming_cycle_atn() -> Atn {
16329        let mut atn = ParserAtnBuilder::new(1);
16330        for (state_number, kind) in [
16331            (0, AtnStateKind::RuleStart),
16332            (1, AtnStateKind::Basic),
16333            (2, AtnStateKind::RuleStop),
16334        ] {
16335            assert_eq!(
16336                atn.add_state(kind, Some(0)).expect("state").index(),
16337                state_number
16338            );
16339        }
16340        atn.set_rule_to_start_state(vec![0])
16341            .expect("rule start states");
16342        atn.set_rule_to_stop_state(vec![2])
16343            .expect("rule stop states");
16344        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
16345            .expect("cycle entry");
16346        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 1 })
16347            .expect("self-cycle transition");
16348        finish_atn(atn)
16349    }
16350
16351    fn eof_then_action_atn() -> Atn {
16352        AtnDeserializer::new(&SerializedAtn::from_i32(&[
16353            4, 1, 1, // version, parser, max token type
16354            3, // states
16355            2, 0, // rule start
16356            1, 0, // basic
16357            7, 0, // rule stop
16358            0, // non-greedy states
16359            0, // precedence states
16360            1, // rules
16361            0, // rule 0 start
16362            0, // modes
16363            0, // sets
16364            2, // transitions
16365            0, 1, 5, -1, 0, 0, // match EOF
16366            1, 2, 6, 0, 0, 0, // parser action
16367            0, // decisions
16368        ]))
16369        .deserialize_parser()
16370        .expect("artificial parser ATN should deserialize")
16371    }
16372
16373    fn noop_action_then_token_then_eof_atn() -> Atn {
16374        AtnDeserializer::new(&SerializedAtn::from_i32(&[
16375            4, 1, 2, // version, parser, max token type
16376            4, // states
16377            2, 0, // rule start
16378            1, 0, // basic
16379            1, 0, // basic
16380            7, 0, // rule stop
16381            0, // non-greedy states
16382            0, // precedence states
16383            1, // rules
16384            0, // rule 0 start
16385            0, // modes
16386            0, // sets
16387            3, // transitions
16388            0, 1, 6, 0, -1, 0, // no-op parser action
16389            1, 2, 5, 1, 0, 0, // match token 1
16390            2, 3, 5, -1, 0, 0, // match EOF
16391            0, // decisions
16392        ]))
16393        .deserialize_parser()
16394        .expect("artificial no-op action ATN should deserialize")
16395    }
16396
16397    fn committed_action_then_predicate_atn() -> Atn {
16398        let mut atn = ParserAtnBuilder::new(1);
16399        for (state_number, kind) in [
16400            (0, AtnStateKind::RuleStart),
16401            (1, AtnStateKind::Basic),
16402            (2, AtnStateKind::Basic),
16403            (3, AtnStateKind::Basic),
16404            (4, AtnStateKind::RuleStop),
16405        ] {
16406            assert_eq!(
16407                atn.add_state(kind, Some(0)).expect("state").index(),
16408                state_number
16409            );
16410        }
16411        atn.set_rule_to_start_state(vec![0])
16412            .expect("rule start states");
16413        atn.set_rule_to_stop_state(vec![4])
16414            .expect("rule stop states");
16415        atn.add_transition(
16416            0,
16417            ParserTransitionSpec::Action {
16418                target: 1,
16419                rule_index: 0,
16420                action_index: None,
16421                context_dependent: false,
16422            },
16423        )
16424        .expect("action transition");
16425        atn.add_transition(
16426            1,
16427            ParserTransitionSpec::Predicate {
16428                target: 2,
16429                rule_index: 0,
16430                pred_index: 0,
16431                context_dependent: false,
16432            },
16433        )
16434        .expect("predicate transition");
16435        atn.add_transition(
16436            2,
16437            ParserTransitionSpec::Atom {
16438                target: 3,
16439                label: 1,
16440            },
16441        )
16442        .expect("token transition");
16443        atn.add_transition(
16444            3,
16445            ParserTransitionSpec::Atom {
16446                target: 4,
16447                label: TOKEN_EOF,
16448            },
16449        )
16450        .expect("EOF transition");
16451        finish_atn(atn)
16452    }
16453
16454    /// ATN for `parent : child[42] {Parent();}; child[int value] : {Child();} EOF;`.
16455    fn parameterized_child_action_eof_atn() -> Atn {
16456        let mut atn = ParserAtnBuilder::new(1);
16457        for (state_number, kind, rule_index) in [
16458            (0, AtnStateKind::RuleStart, 0),
16459            (1, AtnStateKind::Basic, 0),
16460            (2, AtnStateKind::Basic, 0),
16461            (3, AtnStateKind::RuleStop, 0),
16462            (4, AtnStateKind::RuleStart, 1),
16463            (5, AtnStateKind::Basic, 1),
16464            (6, AtnStateKind::RuleStop, 1),
16465        ] {
16466            assert_eq!(
16467                atn.add_state(kind, Some(rule_index))
16468                    .expect("state")
16469                    .index(),
16470                state_number
16471            );
16472        }
16473        atn.set_rule_to_start_state(vec![0, 4])
16474            .expect("rule start states");
16475        atn.set_rule_to_stop_state(vec![3, 6])
16476            .expect("rule stop states");
16477        atn.add_transition(
16478            0,
16479            ParserTransitionSpec::Rule {
16480                target: 4,
16481                rule_index: 1,
16482                follow_state: 1,
16483                precedence: 0,
16484            },
16485        )
16486        .expect("parameterized child call");
16487        atn.add_transition(
16488            1,
16489            ParserTransitionSpec::Action {
16490                target: 2,
16491                rule_index: 0,
16492                action_index: None,
16493                context_dependent: false,
16494            },
16495        )
16496        .expect("parent action");
16497        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
16498            .expect("parent stop");
16499        atn.add_transition(
16500            4,
16501            ParserTransitionSpec::Action {
16502                target: 5,
16503                rule_index: 1,
16504                action_index: None,
16505                context_dependent: false,
16506            },
16507        )
16508        .expect("child action");
16509        atn.add_transition(
16510            5,
16511            ParserTransitionSpec::Atom {
16512                target: 6,
16513                label: TOKEN_EOF,
16514            },
16515        )
16516        .expect("child EOF");
16517        finish_atn(atn)
16518    }
16519
16520    fn action_then_nested_rule_atn() -> Atn {
16521        let mut atn = ParserAtnBuilder::new(1);
16522        for (state_number, kind, rule_index) in [
16523            (0, AtnStateKind::RuleStart, 0),
16524            (1, AtnStateKind::Basic, 0),
16525            (2, AtnStateKind::Basic, 0),
16526            (3, AtnStateKind::RuleStop, 0),
16527            (4, AtnStateKind::RuleStart, 1),
16528            (5, AtnStateKind::RuleStop, 1),
16529        ] {
16530            assert_eq!(
16531                atn.add_state(kind, Some(rule_index))
16532                    .expect("state")
16533                    .index(),
16534                state_number
16535            );
16536        }
16537        atn.set_rule_to_start_state(vec![0, 4])
16538            .expect("rule start states");
16539        atn.set_rule_to_stop_state(vec![3, 5])
16540            .expect("rule stop states");
16541        atn.add_transition(
16542            0,
16543            ParserTransitionSpec::Action {
16544                target: 1,
16545                rule_index: 0,
16546                action_index: None,
16547                context_dependent: false,
16548            },
16549        )
16550        .expect("parent action");
16551        atn.add_transition(
16552            1,
16553            ParserTransitionSpec::Rule {
16554                target: 4,
16555                rule_index: 1,
16556                follow_state: 2,
16557                precedence: 0,
16558            },
16559        )
16560        .expect("nested rule call");
16561        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
16562            .expect("parent stop");
16563        atn.add_transition(
16564            4,
16565            ParserTransitionSpec::Atom {
16566                target: 5,
16567                label: TOKEN_EOF,
16568            },
16569        )
16570        .expect("child EOF");
16571        finish_atn(atn)
16572    }
16573
16574    fn losing_alternative_action_atn() -> Atn {
16575        let mut atn = ParserAtnBuilder::new(2);
16576        for (state_number, kind) in [
16577            (0, AtnStateKind::RuleStart),
16578            (1, AtnStateKind::BlockStart),
16579            (2, AtnStateKind::Basic),
16580            (3, AtnStateKind::Basic),
16581            (4, AtnStateKind::BlockEnd),
16582            (5, AtnStateKind::RuleStop),
16583        ] {
16584            assert_eq!(
16585                atn.add_state(kind, Some(0)).expect("state").index(),
16586                state_number
16587            );
16588        }
16589        atn.set_rule_to_start_state(vec![0])
16590            .expect("rule start states");
16591        atn.set_rule_to_stop_state(vec![5])
16592            .expect("rule stop states");
16593        atn.set_end_state(1, 4).expect("block end state");
16594        atn.add_decision_state(1).expect("decision state");
16595        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
16596            .expect("entry transition");
16597        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
16598            .expect("first alternative");
16599        atn.add_transition(
16600            1,
16601            ParserTransitionSpec::Atom {
16602                target: 4,
16603                label: 2,
16604            },
16605        )
16606        .expect("second alternative");
16607        atn.add_transition(
16608            2,
16609            ParserTransitionSpec::Action {
16610                target: 3,
16611                rule_index: 0,
16612                action_index: None,
16613                context_dependent: false,
16614            },
16615        )
16616        .expect("losing action");
16617        atn.add_transition(
16618            3,
16619            ParserTransitionSpec::Atom {
16620                target: 4,
16621                label: 1,
16622            },
16623        )
16624        .expect("first alternative token");
16625        atn.add_transition(
16626            4,
16627            ParserTransitionSpec::Atom {
16628                target: 5,
16629                label: TOKEN_EOF,
16630            },
16631        )
16632        .expect("EOF transition");
16633        finish_atn(atn)
16634    }
16635
16636    fn committed_action_star_loop_atn() -> Atn {
16637        let mut atn = ParserAtnBuilder::new(1);
16638        for (state_number, kind) in [
16639            (0, AtnStateKind::RuleStart),
16640            (1, AtnStateKind::StarLoopEntry),
16641            (2, AtnStateKind::Basic),
16642            (3, AtnStateKind::Basic),
16643            (4, AtnStateKind::StarLoopBack),
16644            (5, AtnStateKind::LoopEnd),
16645            (6, AtnStateKind::RuleStop),
16646        ] {
16647            assert_eq!(
16648                atn.add_state(kind, Some(0)).expect("state").index(),
16649                state_number
16650            );
16651        }
16652        atn.set_rule_to_start_state(vec![0])
16653            .expect("rule start states");
16654        atn.set_rule_to_stop_state(vec![6])
16655            .expect("rule stop states");
16656        atn.add_decision_state(1).expect("decision state");
16657        atn.set_loop_back_state(5, 4).expect("loop back state");
16658        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
16659            .expect("entry transition");
16660        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
16661            .expect("loop body");
16662        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
16663            .expect("loop exit");
16664        atn.add_transition(
16665            2,
16666            ParserTransitionSpec::Action {
16667                target: 3,
16668                rule_index: 0,
16669                action_index: None,
16670                context_dependent: false,
16671            },
16672        )
16673        .expect("loop action");
16674        atn.add_transition(
16675            3,
16676            ParserTransitionSpec::Atom {
16677                target: 4,
16678                label: 1,
16679            },
16680        )
16681        .expect("loop token");
16682        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
16683            .expect("loop back");
16684        atn.add_transition(
16685            5,
16686            ParserTransitionSpec::Atom {
16687                target: 6,
16688                label: TOKEN_EOF,
16689            },
16690        )
16691        .expect("EOF transition");
16692        finish_atn(atn)
16693    }
16694
16695    fn committed_action_left_recursive_atn() -> Atn {
16696        let mut atn = ParserAtnBuilder::new(4);
16697        for (state, kind) in [
16698            (0, AtnStateKind::RuleStart),
16699            (1, AtnStateKind::BlockStart),
16700            (2, AtnStateKind::StarLoopEntry),
16701            (3, AtnStateKind::StarBlockStart),
16702            (4, AtnStateKind::Basic),
16703            (5, AtnStateKind::Basic),
16704            (6, AtnStateKind::Basic),
16705            (7, AtnStateKind::StarLoopBack),
16706            (8, AtnStateKind::LoopEnd),
16707            (9, AtnStateKind::RuleStop),
16708            (10, AtnStateKind::Basic),
16709        ] {
16710            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
16711        }
16712        atn.set_left_recursive_rule(0)
16713            .expect("left-recursive rule start");
16714        atn.set_precedence_rule_decision(2)
16715            .expect("precedence decision");
16716        atn.set_loop_back_state(8, 7).expect("loop-back state");
16717        atn.set_rule_to_start_state(vec![0])
16718            .expect("rule start states");
16719        atn.set_rule_to_stop_state(vec![9])
16720            .expect("rule stop states");
16721        for state in [1, 2, 3] {
16722            atn.add_decision_state(state).expect("decision state");
16723        }
16724        for (source, target) in [(0, 1), (2, 3), (2, 8), (7, 2), (8, 9)] {
16725            atn.add_transition(source, ParserTransitionSpec::Epsilon { target })
16726                .expect("epsilon transition");
16727        }
16728        for (source, target, label) in [(1, 2, 1), (1, 2, 2), (4, 6, 4), (5, 6, 3)] {
16729            atn.add_transition(source, ParserTransitionSpec::Atom { target, label })
16730                .expect("token transition");
16731        }
16732        for (target, precedence) in [(4, 2), (5, 1)] {
16733            atn.add_transition(3, ParserTransitionSpec::Precedence { target, precedence })
16734                .expect("operator precedence");
16735        }
16736        atn.add_transition(
16737            6,
16738            ParserTransitionSpec::Action {
16739                target: 10,
16740                rule_index: 0,
16741                action_index: None,
16742                context_dependent: false,
16743            },
16744        )
16745        .expect("operator action");
16746        atn.add_transition(
16747            10,
16748            ParserTransitionSpec::Atom {
16749                target: 7,
16750                label: 1,
16751            },
16752        )
16753        .expect("right operand");
16754        finish_atn(atn)
16755    }
16756
16757    fn two_alt_decision_atn() -> Atn {
16758        let mut atn = ParserAtnBuilder::new(2);
16759        assert_eq!(
16760            atn.add_state(AtnStateKind::RuleStart, Some(0))
16761                .expect("state")
16762                .index(),
16763            0
16764        );
16765        assert_eq!(
16766            atn.add_state(AtnStateKind::BlockStart, Some(0))
16767                .expect("state")
16768                .index(),
16769            1
16770        );
16771        assert_eq!(
16772            atn.add_state(AtnStateKind::Basic, Some(0))
16773                .expect("state")
16774                .index(),
16775            2
16776        );
16777        assert_eq!(
16778            atn.add_state(AtnStateKind::Basic, Some(0))
16779                .expect("state")
16780                .index(),
16781            3
16782        );
16783        assert_eq!(
16784            atn.add_state(AtnStateKind::BlockEnd, Some(0))
16785                .expect("state")
16786                .index(),
16787            4
16788        );
16789        assert_eq!(
16790            atn.add_state(AtnStateKind::RuleStop, Some(0))
16791                .expect("state")
16792                .index(),
16793            5
16794        );
16795        atn.set_rule_to_start_state(vec![0])
16796            .expect("rule start states");
16797        atn.set_rule_to_stop_state(vec![5])
16798            .expect("rule stop states");
16799        atn.add_decision_state(1).expect("decision state");
16800        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
16801            .expect("transition");
16802        atn.add_transition(
16803            1,
16804            ParserTransitionSpec::Atom {
16805                target: 2,
16806                label: 1,
16807            },
16808        )
16809        .expect("transition");
16810        atn.add_transition(
16811            1,
16812            ParserTransitionSpec::Atom {
16813                target: 3,
16814                label: 2,
16815            },
16816        )
16817        .expect("transition");
16818        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
16819            .expect("transition");
16820        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
16821            .expect("transition");
16822        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
16823            .expect("transition");
16824        finish_atn(atn)
16825    }
16826
16827    /// ATN for `start : (A)? B EOF ;` (A=1, B=2, C=3, max token type 3).
16828    /// State 1 is the nullable optional-block decision; its sync set is {A, B}.
16829    fn optional_then_b_eof_atn() -> Atn {
16830        let mut atn = ParserAtnBuilder::new(3);
16831        assert_eq!(
16832            atn.add_state(AtnStateKind::RuleStart, Some(0))
16833                .expect("state")
16834                .index(),
16835            0
16836        );
16837        assert_eq!(
16838            atn.add_state(AtnStateKind::BlockStart, Some(0))
16839                .expect("state")
16840                .index(),
16841            1
16842        );
16843        assert_eq!(
16844            atn.add_state(AtnStateKind::Basic, Some(0))
16845                .expect("state")
16846                .index(),
16847            2
16848        );
16849        assert_eq!(
16850            atn.add_state(AtnStateKind::Basic, Some(0))
16851                .expect("state")
16852                .index(),
16853            3
16854        );
16855        assert_eq!(
16856            atn.add_state(AtnStateKind::Basic, Some(0))
16857                .expect("state")
16858                .index(),
16859            4
16860        );
16861        assert_eq!(
16862            atn.add_state(AtnStateKind::RuleStop, Some(0))
16863                .expect("state")
16864                .index(),
16865            5
16866        );
16867        atn.set_rule_to_start_state(vec![0])
16868            .expect("rule start states");
16869        atn.set_rule_to_stop_state(vec![5])
16870            .expect("rule stop states");
16871        atn.add_decision_state(1).expect("decision state");
16872        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
16873            .expect("transition");
16874        // Optional block: match A then fall through, or skip straight to state 3.
16875        atn.add_transition(
16876            1,
16877            ParserTransitionSpec::Atom {
16878                target: 3,
16879                label: 1,
16880            },
16881        )
16882        .expect("transition");
16883        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
16884            .expect("transition");
16885        // Match B, then EOF.
16886        atn.add_transition(
16887            3,
16888            ParserTransitionSpec::Atom {
16889                target: 4,
16890                label: 2,
16891            },
16892        )
16893        .expect("transition");
16894        atn.add_transition(
16895            4,
16896            ParserTransitionSpec::Atom {
16897                target: 5,
16898                label: TOKEN_EOF,
16899            },
16900        )
16901        .expect("transition");
16902        finish_atn(atn)
16903    }
16904
16905    #[test]
16906    fn sync_decision_deletes_only_a_single_token() {
16907        // ANTLR sync recovery deletes exactly one token, only when LA(2) is
16908        // expected. `(A)? B EOF` at the optional-block decision:
16909        //  - `C B`   -> single-token deletion: one error node for the extra `C`.
16910        //  - `C C B` -> LA(2) is `C` (not expected), so NO deletion; sync returns
16911        //               without consuming and records the expected set for the
16912        //               subsequent mismatch (the parser must not over-consume both
16913        //               `C`s and accept the input).
16914        let atn = optional_then_b_eof_atn();
16915
16916        let mut single = mini_parser(vec![
16917            TestToken::new(3).with_text("c"),
16918            TestToken::new(2).with_text("b"),
16919            TestToken::eof("parser-test", 1, 2, 2),
16920        ]);
16921        single.rule_context_stack = vec![RuleContextFrame {
16922            rule_index: 0,
16923            invoking_state: 0,
16924        }];
16925        let children = single
16926            .sync_decision(&atn, 1, true, false)
16927            .expect("single extraneous token recovers");
16928        assert_eq!(children.len(), 1);
16929        assert_eq!(single.node(children[0]).kind(), NodeKind::Error);
16930        assert_eq!(single.number_of_syntax_errors(), 1);
16931        // Exactly one token consumed (the cursor now sits on `b`).
16932        assert_eq!(single.la(1), 2);
16933
16934        let mut double = mini_parser(vec![
16935            TestToken::new(3).with_text("c"),
16936            TestToken::new(3).with_text("c"),
16937            TestToken::new(2).with_text("b"),
16938            TestToken::eof("parser-test", 1, 3, 3),
16939        ]);
16940        double.rule_context_stack = vec![RuleContextFrame {
16941            rule_index: 0,
16942            invoking_state: 0,
16943        }];
16944        let result = double.sync_decision(&atn, 1, true, false);
16945        // No single-token deletion fires (LA(2) is `c`, not expected): sync must NOT
16946        // consume either `c`. It reports the mismatch at the first `c` (so the parser
16947        // does not over-consume both and accept the input). Nothing is consumed, so
16948        // the cursor still sits on the first `c` for rule-level recovery.
16949        let error = result.expect_err("two extraneous tokens must not be deleted by sync");
16950        match error {
16951            AntlrError::ParserError { message, .. } => {
16952                assert!(message.starts_with("mismatched input"), "got: {message}");
16953            }
16954            other => panic!("expected a mismatched-input ParserError, got {other:?}"),
16955        }
16956        assert_eq!(double.la(1), 3);
16957    }
16958
16959    /// The real serialized ATN that `antlr4-rust-gen` emits for
16960    /// `grammar T; s : A* EOF; A:'a'; C:'c';` — a `*` loop whose follow set after
16961    /// the loop is `EOF`. The loop decision is state 5.
16962    fn star_loop_then_eof_atn() -> Atn {
16963        AtnDeserializer::new(&SerializedAtn::from_i32(&[
16964            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,
16965            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,
16966            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,
16967            0, 0, 1, 9, 1, 1, 0, 0, 0, 1, 5,
16968        ]))
16969        .deserialize_parser()
16970        .expect("star-loop-then-EOF ATN should deserialize")
16971    }
16972
16973    /// ATN for `entry : nested EOF; nested : A*;`.
16974    ///
16975    /// State 5 is nullable within `nested`; its caller follow is EOF.
16976    fn nested_star_rule_atn() -> Atn {
16977        let mut atn = ParserAtnBuilder::new(2);
16978        for (state_number, kind, rule_index) in [
16979            (0, AtnStateKind::RuleStart, 0),
16980            (1, AtnStateKind::Basic, 0),
16981            (2, AtnStateKind::Basic, 0),
16982            (3, AtnStateKind::RuleStop, 0),
16983            (4, AtnStateKind::RuleStart, 1),
16984            (5, AtnStateKind::StarLoopEntry, 1),
16985            (6, AtnStateKind::Basic, 1),
16986            (7, AtnStateKind::StarLoopBack, 1),
16987            (8, AtnStateKind::LoopEnd, 1),
16988            (9, AtnStateKind::RuleStop, 1),
16989        ] {
16990            assert_eq!(
16991                atn.add_state(kind, Some(rule_index))
16992                    .expect("state")
16993                    .index(),
16994                state_number
16995            );
16996        }
16997        atn.set_rule_to_start_state(vec![0, 4])
16998            .expect("rule start states");
16999        atn.set_rule_to_stop_state(vec![3, 9])
17000            .expect("rule stop states");
17001        atn.add_decision_state(5).expect("decision state");
17002        atn.set_loop_back_state(8, 7).expect("loop back state");
17003        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
17004            .expect("transition");
17005        atn.add_transition(
17006            1,
17007            ParserTransitionSpec::Rule {
17008                target: 4,
17009                rule_index: 1,
17010                follow_state: 2,
17011                precedence: 0,
17012            },
17013        )
17014        .expect("transition");
17015        atn.add_transition(
17016            2,
17017            ParserTransitionSpec::Atom {
17018                target: 3,
17019                label: TOKEN_EOF,
17020            },
17021        )
17022        .expect("transition");
17023        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
17024            .expect("transition");
17025        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
17026            .expect("transition");
17027        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 8 })
17028            .expect("transition");
17029        atn.add_transition(
17030            6,
17031            ParserTransitionSpec::Atom {
17032                target: 7,
17033                label: 1,
17034            },
17035        )
17036        .expect("transition");
17037        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 5 })
17038            .expect("transition");
17039        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
17040            .expect("transition");
17041        finish_atn(atn)
17042    }
17043
17044    /// ATN for `s : a+ Y ; a : X ;`.
17045    ///
17046    /// At EOF, recovery can synthesize an empty failed `a` child. The enclosing
17047    /// `+` loop must not treat that zero-width child as a successful iteration
17048    /// and then re-enter the loop at the same token index.
17049    fn plus_loop_with_recovering_body_atn() -> Atn {
17050        let mut atn = ParserAtnBuilder::new(2);
17051        assert_eq!(
17052            atn.add_state(AtnStateKind::RuleStart, Some(0))
17053                .expect("state")
17054                .index(),
17055            0
17056        );
17057        assert_eq!(
17058            atn.add_state(AtnStateKind::PlusBlockStart, Some(0))
17059                .expect("state")
17060                .index(),
17061            1
17062        );
17063        assert_eq!(
17064            atn.add_state(AtnStateKind::Basic, Some(0))
17065                .expect("state")
17066                .index(),
17067            2
17068        );
17069        assert_eq!(
17070            atn.add_state(AtnStateKind::BlockEnd, Some(0))
17071                .expect("state")
17072                .index(),
17073            3
17074        );
17075        assert_eq!(
17076            atn.add_state(AtnStateKind::PlusLoopBack, Some(0))
17077                .expect("state")
17078                .index(),
17079            4
17080        );
17081        assert_eq!(
17082            atn.add_state(AtnStateKind::LoopEnd, Some(0))
17083                .expect("state")
17084                .index(),
17085            5
17086        );
17087        assert_eq!(
17088            atn.add_state(AtnStateKind::RuleStop, Some(0))
17089                .expect("state")
17090                .index(),
17091            6
17092        );
17093        assert_eq!(
17094            atn.add_state(AtnStateKind::RuleStart, Some(1))
17095                .expect("state")
17096                .index(),
17097            7
17098        );
17099        assert_eq!(
17100            atn.add_state(AtnStateKind::Basic, Some(1))
17101                .expect("state")
17102                .index(),
17103            8
17104        );
17105        assert_eq!(
17106            atn.add_state(AtnStateKind::RuleStop, Some(1))
17107                .expect("state")
17108                .index(),
17109            9
17110        );
17111        atn.set_rule_to_start_state(vec![0, 7])
17112            .expect("rule start states");
17113        atn.set_rule_to_stop_state(vec![6, 9])
17114            .expect("rule stop states");
17115        atn.set_end_state(1, 3).expect("block end state");
17116        atn.set_loop_back_state(5, 4).expect("loop back state");
17117        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
17118            .expect("transition");
17119        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
17120            .expect("transition");
17121        atn.add_transition(
17122            2,
17123            ParserTransitionSpec::Rule {
17124                target: 7,
17125                rule_index: 1,
17126                follow_state: 3,
17127                precedence: 0,
17128            },
17129        )
17130        .expect("transition");
17131        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
17132            .expect("transition");
17133        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
17134            .expect("transition");
17135        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 5 })
17136            .expect("transition");
17137        atn.add_transition(
17138            5,
17139            ParserTransitionSpec::Atom {
17140                target: 6,
17141                label: 2,
17142            },
17143        )
17144        .expect("transition");
17145        atn.add_transition(
17146            7,
17147            ParserTransitionSpec::Atom {
17148                target: 8,
17149                label: 1,
17150            },
17151        )
17152        .expect("transition");
17153        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
17154            .expect("transition");
17155        finish_atn(atn)
17156    }
17157
17158    #[test]
17159    fn runtime_options_default_exits_recovering_empty_plus_iteration() {
17160        let atn = plus_loop_with_recovering_body_atn();
17161        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
17162
17163        let error = parser
17164            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
17165            .expect_err("EOF recovery should report a bounded mismatch");
17166
17167        let AntlrError::ParserError { message, .. } = error else {
17168            panic!("expected ParserError, got {error:?}");
17169        };
17170        insta::assert_snapshot!(message, @"mismatched input '<EOF>' expecting {'x', 2}");
17171        assert_eq!(parser.number_of_syntax_errors(), 1);
17172        assert_eq!(parser.input.index(), 0, "EOF remains unconsumed");
17173    }
17174
17175    #[test]
17176    fn sync_decision_deletes_token_before_eof_at_loop_back() {
17177        // `s : A* EOF` on `c`: the loop decision (state 5) can recover onto EOF.
17178        // At the loop ENTRY (loop_back = false) a single unexpected token before
17179        // EOF is deleted as an error node (then the generated EOF match consumes
17180        // the real EOF) — matching ANTLR's `(s c <EOF>)` + "extraneous input".
17181        // EOF must be a valid scan-stop for this to fire.
17182        let atn = star_loop_then_eof_atn();
17183        let mut parser = mini_parser(vec![
17184            TestToken::new(2).with_text("c"),
17185            TestToken::eof("parser-test", 1, 1, 1),
17186        ]);
17187        parser.rule_context_stack = vec![RuleContextFrame {
17188            rule_index: 0,
17189            invoking_state: 0,
17190        }];
17191        let children = parser
17192            .sync_decision(&atn, 5, true, false)
17193            .expect("single token before EOF recovers");
17194        assert_eq!(children.len(), 1);
17195        assert_eq!(parser.node(children[0]).kind(), NodeKind::Error);
17196        assert_eq!(parser.number_of_syntax_errors(), 1);
17197        assert_eq!(
17198            parser.la(1),
17199            TOKEN_EOF,
17200            "EOF is left for the rule's EOF match"
17201        );
17202    }
17203
17204    #[test]
17205    fn sync_decision_does_not_delete_two_tokens_before_eof_at_loop_entry() {
17206        // `s : A* EOF` on `c c`: at the loop ENTRY (loop_back = false) ANTLR does
17207        // single-token deletion, which fails because LA(2) = `c` is not expected —
17208        // so it reports `mismatched input` and consumes nothing (ANTLR: `(s c c)`
17209        // with no EOF). The scan must NOT multi-token-consume both `c`s here.
17210        let atn = star_loop_then_eof_atn();
17211        let mut parser = mini_parser(vec![
17212            TestToken::new(2).with_text("c"),
17213            TestToken::new(2).with_text("c"),
17214            TestToken::eof("parser-test", 1, 2, 2),
17215        ]);
17216        parser.rule_context_stack = vec![RuleContextFrame {
17217            rule_index: 0,
17218            invoking_state: 0,
17219        }];
17220        let error = parser
17221            .sync_decision(&atn, 5, true, false)
17222            .expect_err("two tokens at the loop entry must not be deleted");
17223        match error {
17224            AntlrError::ParserError { message, .. } => {
17225                assert!(message.starts_with("mismatched input"), "got: {message}");
17226            }
17227            other => panic!("expected mismatched-input ParserError, got {other:?}"),
17228        }
17229        assert_eq!(
17230            parser.la(1),
17231            2,
17232            "nothing consumed; cursor still on first `c`"
17233        );
17234    }
17235
17236    #[test]
17237    fn sync_decision_consumes_until_eof_at_loop_back() {
17238        // Same `s : A* EOF` decision, but at a loop-BACK (loop_back = true, i.e.
17239        // after ≥1 `A` matched). ANTLR uses multi-token `consumeUntil(recoverSet)`
17240        // there, so two unexpected tokens before EOF are BOTH deleted and the rule
17241        // recovers (matching `(s a c c <EOF>)` for input `a c c`). Here we feed the
17242        // post-`a` state directly: `c c <EOF>` with loop_back = true.
17243        let atn = star_loop_then_eof_atn();
17244        let mut parser = mini_parser(vec![
17245            TestToken::new(2).with_text("c"),
17246            TestToken::new(2).with_text("c"),
17247            TestToken::eof("parser-test", 1, 2, 2),
17248        ]);
17249        parser.rule_context_stack = vec![RuleContextFrame {
17250            rule_index: 0,
17251            invoking_state: 0,
17252        }];
17253        let children = parser
17254            .sync_decision(&atn, 5, false, true)
17255            .expect("loop-back multi-token deletion recovers onto EOF");
17256        assert_eq!(children.len(), 2, "both `c`s deleted as error nodes");
17257        assert!(
17258            children
17259                .iter()
17260                .all(|child| parser.node(*child).kind() == NodeKind::Error)
17261        );
17262        assert_eq!(parser.number_of_syntax_errors(), 1);
17263        assert_eq!(parser.la(1), TOKEN_EOF, "EOF left for the rule's EOF match");
17264    }
17265
17266    #[test]
17267    fn sync_decision_returns_before_recovery_for_nullable_exit() {
17268        let atn = nested_star_rule_atn();
17269        for (current_context_empty, loop_back) in [(true, false), (false, true)] {
17270            let mut parser = mini_parser(vec![
17271                TestToken::new(2).with_text("c"),
17272                TestToken::new(1).with_text("a"),
17273                TestToken::eof("parser-test", 1, 2, 2),
17274            ]);
17275            parser.rule_context_stack = vec![
17276                RuleContextFrame {
17277                    rule_index: 0,
17278                    invoking_state: 0,
17279                },
17280                RuleContextFrame {
17281                    rule_index: 1,
17282                    invoking_state: 1,
17283                },
17284            ];
17285
17286            let children = parser
17287                .sync_decision(&atn, 5, current_context_empty, loop_back)
17288                .expect("nullable synchronization is a no-op");
17289
17290            assert!(children.is_empty());
17291            assert_eq!(parser.la(1), 2, "the caller must receive the current token");
17292            assert_eq!(parser.number_of_syntax_errors(), 0);
17293            assert_eq!(
17294                parser
17295                    .generated_sync_expected
17296                    .as_ref()
17297                    .expect("nullable sync preserves expected symbols")
17298                    .to_btree_set(),
17299                BTreeSet::from([TOKEN_EOF, 1])
17300            );
17301        }
17302    }
17303
17304    fn predicate_after_token_atn() -> Atn {
17305        let mut atn = ParserAtnBuilder::new(2);
17306        assert_eq!(
17307            atn.add_state(AtnStateKind::RuleStart, Some(0))
17308                .expect("state")
17309                .index(),
17310            0
17311        );
17312        assert_eq!(
17313            atn.add_state(AtnStateKind::Basic, Some(0))
17314                .expect("state")
17315                .index(),
17316            1
17317        );
17318        assert_eq!(
17319            atn.add_state(AtnStateKind::Basic, Some(0))
17320                .expect("state")
17321                .index(),
17322            2
17323        );
17324        assert_eq!(
17325            atn.add_state(AtnStateKind::Basic, Some(0))
17326                .expect("state")
17327                .index(),
17328            3
17329        );
17330        assert_eq!(
17331            atn.add_state(AtnStateKind::RuleStop, Some(0))
17332                .expect("state")
17333                .index(),
17334            4
17335        );
17336        atn.set_rule_to_start_state(vec![0])
17337            .expect("rule start states");
17338        atn.set_rule_to_stop_state(vec![4])
17339            .expect("rule stop states");
17340        atn.add_transition(
17341            0,
17342            ParserTransitionSpec::Atom {
17343                target: 1,
17344                label: 1,
17345            },
17346        )
17347        .expect("transition");
17348        atn.add_transition(
17349            1,
17350            ParserTransitionSpec::Predicate {
17351                target: 2,
17352                rule_index: 0,
17353                pred_index: 0,
17354                context_dependent: false,
17355            },
17356        )
17357        .expect("transition");
17358        atn.add_transition(
17359            2,
17360            ParserTransitionSpec::Atom {
17361                target: 3,
17362                label: 2,
17363            },
17364        )
17365        .expect("transition");
17366        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
17367            .expect("transition");
17368        finish_atn(atn)
17369    }
17370
17371    fn predicate_gated_same_lookahead_atn(pred_indexes: [usize; 2]) -> Atn {
17372        let mut atn = ParserAtnBuilder::new(1);
17373        for (state_number, kind) in [
17374            (0, AtnStateKind::RuleStart),
17375            (1, AtnStateKind::BlockStart),
17376            (2, AtnStateKind::Basic),
17377            (3, AtnStateKind::Basic),
17378            (4, AtnStateKind::Basic),
17379            (5, AtnStateKind::Basic),
17380            (6, AtnStateKind::BlockEnd),
17381            (7, AtnStateKind::RuleStop),
17382        ] {
17383            assert_eq!(
17384                atn.add_state(kind, Some(0)).expect("state").index(),
17385                state_number
17386            );
17387        }
17388        atn.set_rule_to_start_state(vec![0])
17389            .expect("rule start states");
17390        atn.set_rule_to_stop_state(vec![7])
17391            .expect("rule stop states");
17392        atn.add_decision_state(1).expect("decision state");
17393        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
17394            .expect("transition");
17395        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
17396            .expect("transition");
17397        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
17398            .expect("transition");
17399        atn.add_transition(
17400            2,
17401            ParserTransitionSpec::Predicate {
17402                target: 4,
17403                rule_index: 0,
17404                pred_index: pred_indexes[0],
17405                context_dependent: false,
17406            },
17407        )
17408        .expect("transition");
17409        atn.add_transition(
17410            3,
17411            ParserTransitionSpec::Predicate {
17412                target: 5,
17413                rule_index: 0,
17414                pred_index: pred_indexes[1],
17415                context_dependent: false,
17416            },
17417        )
17418        .expect("transition");
17419        atn.add_transition(
17420            4,
17421            ParserTransitionSpec::Atom {
17422                target: 6,
17423                label: 1,
17424            },
17425        )
17426        .expect("transition");
17427        atn.add_transition(
17428            5,
17429            ParserTransitionSpec::Atom {
17430                target: 6,
17431                label: 1,
17432            },
17433        )
17434        .expect("transition");
17435        atn.add_transition(
17436            6,
17437            ParserTransitionSpec::Atom {
17438                target: 7,
17439                label: TOKEN_EOF,
17440            },
17441        )
17442        .expect("transition");
17443        finish_atn(atn)
17444    }
17445
17446    /// ATN for `s : A B | {false}? A C | {true}? A C;`.
17447    fn semantic_fallback_viability_atn() -> Atn {
17448        let mut atn = ParserAtnBuilder::new(3);
17449        for (state_number, kind) in [
17450            (0, AtnStateKind::RuleStart),
17451            (1, AtnStateKind::BlockStart),
17452            (2, AtnStateKind::Basic),
17453            (3, AtnStateKind::Basic),
17454            (4, AtnStateKind::Basic),
17455            (5, AtnStateKind::Basic),
17456            (6, AtnStateKind::Basic),
17457            (7, AtnStateKind::Basic),
17458            (8, AtnStateKind::Basic),
17459            (9, AtnStateKind::BlockEnd),
17460            (10, AtnStateKind::RuleStop),
17461        ] {
17462            assert_eq!(
17463                atn.add_state(kind, Some(0)).expect("state").index(),
17464                state_number
17465            );
17466        }
17467        atn.set_rule_to_start_state(vec![0])
17468            .expect("rule start states");
17469        atn.set_rule_to_stop_state(vec![10])
17470            .expect("rule stop states");
17471        atn.set_end_state(1, 9).expect("block end state");
17472        atn.add_decision_state(1).expect("decision state");
17473        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
17474            .expect("entry transition");
17475        atn.add_transition(
17476            1,
17477            ParserTransitionSpec::Atom {
17478                target: 2,
17479                label: 1,
17480            },
17481        )
17482        .expect("first alternative");
17483        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
17484            .expect("second alternative");
17485        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
17486            .expect("third alternative");
17487        atn.add_transition(
17488            2,
17489            ParserTransitionSpec::Atom {
17490                target: 9,
17491                label: 2,
17492            },
17493        )
17494        .expect("first alternative suffix");
17495        for (source, target, pred_index) in [(3, 4, 0), (6, 7, 1)] {
17496            atn.add_transition(
17497                source,
17498                ParserTransitionSpec::Predicate {
17499                    target,
17500                    rule_index: 0,
17501                    pred_index,
17502                    context_dependent: false,
17503                },
17504            )
17505            .expect("predicate transition");
17506        }
17507        for (source, target, label) in [(4, 5, 1), (5, 9, 3), (7, 8, 1), (8, 9, 3)] {
17508            atn.add_transition(source, ParserTransitionSpec::Atom { target, label })
17509                .expect("predicate alternative token");
17510        }
17511        atn.add_transition(
17512            9,
17513            ParserTransitionSpec::Atom {
17514                target: 10,
17515                label: TOKEN_EOF,
17516            },
17517        )
17518        .expect("EOF transition");
17519        finish_atn(atn)
17520    }
17521
17522    /// ATN for `s : gated | A; gated : {false}? A;`.
17523    fn rule_call_predicate_decision_atn() -> Atn {
17524        let mut atn = ParserAtnBuilder::new(1);
17525        for (state_number, kind, rule_index) in [
17526            (0, AtnStateKind::RuleStart, 0),
17527            (1, AtnStateKind::BlockStart, 0),
17528            (2, AtnStateKind::Basic, 0),
17529            (3, AtnStateKind::Basic, 0),
17530            (4, AtnStateKind::BlockEnd, 0),
17531            (5, AtnStateKind::RuleStop, 0),
17532            (6, AtnStateKind::RuleStart, 1),
17533            (7, AtnStateKind::Basic, 1),
17534            (8, AtnStateKind::RuleStop, 1),
17535        ] {
17536            assert_eq!(
17537                atn.add_state(kind, Some(rule_index))
17538                    .expect("state")
17539                    .index(),
17540                state_number
17541            );
17542        }
17543        atn.set_rule_to_start_state(vec![0, 6])
17544            .expect("rule start states");
17545        atn.set_rule_to_stop_state(vec![5, 8])
17546            .expect("rule stop states");
17547        atn.set_end_state(1, 4).expect("block end state");
17548        atn.add_decision_state(1).expect("decision state");
17549        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
17550            .expect("entry transition");
17551        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
17552            .expect("gated alternative entry");
17553        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
17554            .expect("direct alternative entry");
17555        atn.add_transition(
17556            2,
17557            ParserTransitionSpec::Rule {
17558                target: 6,
17559                rule_index: 1,
17560                follow_state: 4,
17561                precedence: 0,
17562            },
17563        )
17564        .expect("gated alternative");
17565        atn.add_transition(
17566            3,
17567            ParserTransitionSpec::Atom {
17568                target: 4,
17569                label: 1,
17570            },
17571        )
17572        .expect("direct alternative");
17573        atn.add_transition(
17574            4,
17575            ParserTransitionSpec::Atom {
17576                target: 5,
17577                label: TOKEN_EOF,
17578            },
17579        )
17580        .expect("EOF transition");
17581        atn.add_transition(
17582            6,
17583            ParserTransitionSpec::Predicate {
17584                target: 7,
17585                rule_index: 1,
17586                pred_index: 0,
17587                context_dependent: false,
17588            },
17589        )
17590        .expect("callee predicate");
17591        atn.add_transition(
17592            7,
17593            ParserTransitionSpec::Atom {
17594                target: 8,
17595                label: 1,
17596            },
17597        )
17598        .expect("callee token");
17599        finish_atn(atn)
17600    }
17601
17602    /// ATN for `s : ({true}? A)* EOF;`.
17603    fn predicate_gated_star_loop_atn() -> Atn {
17604        let mut atn = ParserAtnBuilder::new(2);
17605        for (state_number, kind) in [
17606            (0, AtnStateKind::RuleStart),
17607            (1, AtnStateKind::StarLoopEntry),
17608            (2, AtnStateKind::Basic),
17609            (3, AtnStateKind::Basic),
17610            (4, AtnStateKind::StarLoopBack),
17611            (5, AtnStateKind::LoopEnd),
17612            (6, AtnStateKind::RuleStop),
17613        ] {
17614            assert_eq!(
17615                atn.add_state(kind, Some(0)).expect("state").index(),
17616                state_number
17617            );
17618        }
17619        atn.set_rule_to_start_state(vec![0])
17620            .expect("rule start states");
17621        atn.set_rule_to_stop_state(vec![6])
17622            .expect("rule stop states");
17623        atn.add_decision_state(1).expect("decision state");
17624        atn.set_loop_back_state(5, 4).expect("loop back state");
17625        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
17626            .expect("entry transition");
17627        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
17628            .expect("loop enter");
17629        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 5 })
17630            .expect("loop exit");
17631        atn.add_transition(
17632            2,
17633            ParserTransitionSpec::Predicate {
17634                target: 3,
17635                rule_index: 0,
17636                pred_index: 0,
17637                context_dependent: false,
17638            },
17639        )
17640        .expect("loop predicate");
17641        atn.add_transition(
17642            3,
17643            ParserTransitionSpec::Atom {
17644                target: 4,
17645                label: 1,
17646            },
17647        )
17648        .expect("loop token");
17649        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 1 })
17650            .expect("loop back");
17651        atn.add_transition(
17652            5,
17653            ParserTransitionSpec::Atom {
17654                target: 6,
17655                label: TOKEN_EOF,
17656            },
17657        )
17658        .expect("EOF transition");
17659        finish_atn(atn)
17660    }
17661
17662    fn nested_nullable_context_atn() -> Atn {
17663        let mut atn = ParserAtnBuilder::new(1);
17664        for state_number in 0..=20 {
17665            let kind = match state_number {
17666                0 | 10 | 16 => AtnStateKind::RuleStart,
17667                9 | 15 | 20 => AtnStateKind::RuleStop,
17668                _ => AtnStateKind::Basic,
17669            };
17670            let rule_index = match state_number {
17671                0..=9 => 0,
17672                10..=15 => 1,
17673                _ => 2,
17674            };
17675            assert_eq!(
17676                atn.add_state(kind, Some(rule_index))
17677                    .expect("state")
17678                    .index(),
17679                state_number
17680            );
17681        }
17682        atn.set_rule_to_start_state(vec![0, 10, 16])
17683            .expect("rule start states");
17684        atn.set_rule_to_stop_state(vec![9, 15, 20])
17685            .expect("rule stop states");
17686        atn.add_transition(
17687            1,
17688            ParserTransitionSpec::Rule {
17689                target: 10,
17690                rule_index: 1,
17691                follow_state: 8,
17692                precedence: 0,
17693            },
17694        )
17695        .expect("transition");
17696        atn.add_transition(
17697            8,
17698            ParserTransitionSpec::Atom {
17699                target: 9,
17700                label: 1,
17701            },
17702        )
17703        .expect("transition");
17704        atn.add_transition(8, ParserTransitionSpec::Epsilon { target: 9 })
17705            .expect("transition");
17706        atn.add_transition(
17707            2,
17708            ParserTransitionSpec::Rule {
17709                target: 16,
17710                rule_index: 2,
17711                follow_state: 14,
17712                precedence: 0,
17713            },
17714        )
17715        .expect("transition");
17716        atn.add_transition(14, ParserTransitionSpec::Epsilon { target: 15 })
17717            .expect("transition");
17718        finish_atn(atn)
17719    }
17720
17721    fn generated_match_recovery_atn() -> Atn {
17722        let mut atn = ParserAtnBuilder::new(2);
17723        assert_eq!(
17724            atn.add_state(AtnStateKind::RuleStart, Some(0))
17725                .expect("state")
17726                .index(),
17727            0
17728        );
17729        assert_eq!(
17730            atn.add_state(AtnStateKind::Basic, Some(0))
17731                .expect("state")
17732                .index(),
17733            1
17734        );
17735        assert_eq!(
17736            atn.add_state(AtnStateKind::Basic, Some(0))
17737                .expect("state")
17738                .index(),
17739            2
17740        );
17741        assert_eq!(
17742            atn.add_state(AtnStateKind::RuleStop, Some(0))
17743                .expect("state")
17744                .index(),
17745            3
17746        );
17747        assert_eq!(
17748            atn.add_state(AtnStateKind::RuleStart, Some(1))
17749                .expect("state")
17750                .index(),
17751            4
17752        );
17753        assert_eq!(
17754            atn.add_state(AtnStateKind::RuleStop, Some(1))
17755                .expect("state")
17756                .index(),
17757            5
17758        );
17759        atn.set_rule_to_start_state(vec![0, 4])
17760            .expect("rule start states");
17761        atn.set_rule_to_stop_state(vec![3, 5])
17762            .expect("rule stop states");
17763        atn.add_transition(
17764            1,
17765            ParserTransitionSpec::Rule {
17766                target: 4,
17767                rule_index: 1,
17768                follow_state: 2,
17769                precedence: 0,
17770            },
17771        )
17772        .expect("transition");
17773        atn.add_transition(
17774            2,
17775            ParserTransitionSpec::Atom {
17776                target: 3,
17777                label: TOKEN_EOF,
17778            },
17779        )
17780        .expect("transition");
17781        finish_atn(atn)
17782    }
17783
17784    fn complement_set_atn() -> Atn {
17785        let mut atn = ParserAtnBuilder::new(1);
17786        assert_eq!(
17787            atn.add_state(AtnStateKind::RuleStart, Some(0))
17788                .expect("state")
17789                .index(),
17790            0
17791        );
17792        assert_eq!(
17793            atn.add_state(AtnStateKind::RuleStop, Some(0))
17794                .expect("state")
17795                .index(),
17796            1
17797        );
17798        atn.set_rule_to_start_state(vec![0])
17799            .expect("rule start states");
17800        atn.set_rule_to_stop_state(vec![1])
17801            .expect("rule stop states");
17802        let excluded = atn.add_interval_set([(1, 1)]).expect("excluded set");
17803        atn.add_transition(
17804            0,
17805            ParserTransitionSpec::NotSet {
17806                target: 1,
17807                set: excluded,
17808            },
17809        )
17810        .expect("transition");
17811        finish_atn(atn)
17812    }
17813
17814    /// ATN for `start : . EOF ;`: a wildcard whose follow state explicitly matches
17815    /// EOF. State 0 (`RuleStart`) -wildcard-> 2 -EOF-> 1 (`RuleStop`).
17816    fn wildcard_then_eof_atn() -> Atn {
17817        let mut atn = ParserAtnBuilder::new(1);
17818        assert_eq!(
17819            atn.add_state(AtnStateKind::RuleStart, Some(0))
17820                .expect("state")
17821                .index(),
17822            0
17823        );
17824        assert_eq!(
17825            atn.add_state(AtnStateKind::RuleStop, Some(0))
17826                .expect("state")
17827                .index(),
17828            1
17829        );
17830        assert_eq!(
17831            atn.add_state(AtnStateKind::Basic, Some(0))
17832                .expect("state")
17833                .index(),
17834            2
17835        );
17836        atn.set_rule_to_start_state(vec![0])
17837            .expect("rule start states");
17838        atn.set_rule_to_stop_state(vec![1])
17839            .expect("rule stop states");
17840        atn.add_transition(0, ParserTransitionSpec::Wildcard { target: 2 })
17841            .expect("transition");
17842        atn.add_transition(
17843            2,
17844            ParserTransitionSpec::Atom {
17845                target: 1,
17846                label: TOKEN_EOF,
17847            },
17848        )
17849        .expect("transition");
17850        finish_atn(atn)
17851    }
17852
17853    #[test]
17854    fn parser_matches_token_and_reports_mismatch() {
17855        let source = Source {
17856            tokens: vec![
17857                TestToken::new(1).with_text("x"),
17858                TestToken::eof("parser-test", 1, 1, 1),
17859            ],
17860            index: 0,
17861        };
17862        let data = RecognizerData::new(
17863            "Mini.g4",
17864            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
17865        );
17866        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
17867        let matched = parser.match_token(1).expect("token 1 should match");
17868        assert_eq!(parser.node(matched).text(), "x");
17869        assert!(parser.match_token(1).is_err());
17870    }
17871
17872    #[test]
17873    fn parser_matches_token_sets() {
17874        let mut parser = mini_parser(vec![
17875            TestToken::new(1).with_text("x"),
17876            TestToken::eof("parser-test", 1, 1, 1),
17877        ]);
17878
17879        let matched = parser
17880            .match_set(&[(1, 1), (3, 4)])
17881            .expect("token set should match");
17882        assert_eq!(parser.node(matched).text(), "x");
17883        assert!(parser.match_not_set(&[(1, 1)], 1, 4).is_err());
17884    }
17885
17886    #[test]
17887    fn generated_rule_api_tracks_state_and_precedence() {
17888        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
17889
17890        let context = parser.enter_rule(7, 2);
17891        assert_eq!(context.rule_index(), 2);
17892        assert_eq!(parser.state(), 7);
17893        assert_eq!(
17894            parser.rule_context_stack,
17895            vec![RuleContextFrame {
17896                rule_index: 2,
17897                invoking_state: 7
17898            }]
17899        );
17900
17901        let recursive = parser.enter_recursion_rule(11, 3, 4);
17902        assert_eq!(recursive.rule_index(), 3);
17903        assert!(parser.precpred(4));
17904        assert!(parser.precpred(5));
17905        assert!(!parser.precpred(3));
17906
17907        let next = parser.push_new_recursion_context(13, 3);
17908        assert_eq!(next.invoking_state(), 13);
17909        parser.unroll_recursion_context();
17910        assert_eq!(parser.precedence_stack, vec![0]);
17911        assert_eq!(
17912            parser.rule_context_stack,
17913            vec![RuleContextFrame {
17914                rule_index: 2,
17915                invoking_state: 7
17916            }]
17917        );
17918
17919        parser.exit_rule();
17920        assert!(parser.rule_context_stack.is_empty());
17921    }
17922
17923    #[test]
17924    fn reset_rewinds_input_and_clears_parser_owned_parse_state() {
17925        let mut parser = mini_parser(vec![
17926            TestToken::new(1).with_text("x"),
17927            TestToken::eof("parser-test", 1, 1, 1),
17928        ]);
17929        let matched = parser.match_token(1).expect("token should match");
17930        assert_eq!(parser.node(matched).text(), "x");
17931        parser.record_generated_syntax_error();
17932        parser.set_int_member(7, 11);
17933        parser.set_build_parse_trees(false);
17934        parser.set_report_diagnostic_errors(true);
17935        parser.set_prediction_mode(PredictionMode::Sll);
17936        parser.set_bail_on_error(true);
17937        let _context = parser.enter_recursion_rule(9, 0, 4);
17938        parser.pending_invoking_states.push(5);
17939        parser.unknown_predicate_hits.push((0, 1));
17940        parser.unhandled_action_hits.push((0, 2));
17941
17942        parser.reset();
17943
17944        assert_eq!(parser.input.index(), 0);
17945        assert_eq!(parser.la(1), 1);
17946        assert_eq!(parser.state(), -1);
17947        assert_eq!(parser.number_of_syntax_errors(), 0);
17948        assert_eq!(parser.parse_tree_storage().node_count(), 0);
17949        assert!(parser.rule_context_stack.is_empty());
17950        assert!(parser.pending_invoking_states.is_empty());
17951        assert_eq!(parser.precedence_stack, [0]);
17952        assert!(parser.unknown_predicate_hits.is_empty());
17953        assert!(parser.unhandled_action_hits.is_empty());
17954        assert_eq!(parser.int_member(7), Some(11));
17955        assert!(!parser.build_parse_trees());
17956        assert!(parser.report_diagnostic_errors());
17957        assert_eq!(parser.prediction_mode(), PredictionMode::Sll);
17958        assert!(parser.bail_on_error());
17959    }
17960
17961    #[test]
17962    fn set_token_stream_replaces_input_and_resets_parser() {
17963        let mut parser = mini_parser(vec![
17964            TestToken::new(1).with_text("old"),
17965            TestToken::eof("parser-test", 1, 1, 1),
17966        ]);
17967        parser.consume();
17968        parser.record_generated_syntax_error();
17969        let replacement = CommonTokenStream::new(Source {
17970            tokens: vec![
17971                TestToken::new(2).with_text("new"),
17972                TestToken::eof("parser-test", 1, 1, 1),
17973            ],
17974            index: 0,
17975        });
17976
17977        parser.set_token_stream(replacement);
17978
17979        assert_eq!(parser.input.index(), 0);
17980        assert_eq!(parser.la(1), 2);
17981        assert_eq!(parser.input.text_all(), "new");
17982        assert_eq!(parser.number_of_syntax_errors(), 0);
17983    }
17984
17985    #[test]
17986    fn active_invocation_states_exclude_the_root_frame() {
17987        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
17988
17989        let _root = parser.enter_rule(0, 0);
17990        assert!(parser.active_invocation_states().is_empty());
17991
17992        let marker = parser.push_invoking_state(6);
17993        let _child = parser.enter_rule(2, 1);
17994        parser.discard_invoking_state(marker);
17995        assert_eq!(parser.active_invocation_states(), [6]);
17996
17997        let marker = parser.push_invoking_state(13);
17998        let _grandchild = parser.enter_rule(4, 2);
17999        parser.discard_invoking_state(marker);
18000        assert_eq!(parser.active_invocation_states(), [13, 6]);
18001
18002        parser.exit_rule();
18003        parser.exit_rule();
18004        parser.exit_rule();
18005    }
18006
18007    #[test]
18008    fn parser_predicates_support_token_adjacency() {
18009        let mut parser = mini_parser(vec![
18010            TestToken::new(1).with_text("=").with_span(0, 0),
18011            TestToken::new(1).with_text(">").with_span(1, 1),
18012            TestToken::eof("parser-test", 2, 1, 2),
18013        ]);
18014        parser.consume();
18015        parser.consume();
18016
18017        let predicates = [(0, 0, ParserPredicate::TokenPairAdjacent)];
18018
18019        assert!(parser.parser_semantic_predicate_matches(&predicates, 0, 0));
18020
18021        let mut parser = mini_parser(vec![
18022            TestToken::new(1).with_text("=").with_span(0, 0),
18023            TestToken::new(1)
18024                .with_text(" ")
18025                .with_channel(HIDDEN_CHANNEL)
18026                .with_span(1, 1),
18027            TestToken::new(1).with_text(">").with_span(2, 2),
18028            TestToken::eof("parser-test", 3, 1, 3),
18029        ]);
18030        parser.consume();
18031        parser.consume();
18032
18033        assert!(!parser.parser_semantic_predicate_matches(&predicates, 0, 0));
18034    }
18035
18036    #[test]
18037    fn parser_predicates_support_context_child_text_checks() {
18038        let mut parser = mini_parser(vec![
18039            TestToken::new(1).with_text("var"),
18040            TestToken::eof("parser-test", 1, 1, 1),
18041        ]);
18042        let mut context = ParserRuleContext::new(1, 0);
18043        let mut child_context = ParserRuleContext::new(2, 0);
18044        let terminal = parser.terminal_tree(TokenId::try_from(0).expect("test token ID"));
18045        parser.tree.add_child(&mut child_context, terminal);
18046        let child = parser.rule_node(child_context);
18047        parser.tree.add_child(&mut context, child);
18048        let predicates = [(
18049            1,
18050            0,
18051            ParserPredicate::ContextChildRuleTextNotEquals {
18052                rule_index: 2,
18053                text: "var",
18054            },
18055        )];
18056
18057        assert!(
18058            !parser.parser_semantic_predicate_matches_with_context_and_local(
18059                &predicates,
18060                1,
18061                0,
18062                &context,
18063                0,
18064            )
18065        );
18066    }
18067
18068    #[test]
18069    fn context_expected_symbols_walks_nullable_parent_contexts() {
18070        let atn = nested_nullable_context_atn();
18071        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
18072        parser.rule_context_stack = vec![
18073            RuleContextFrame {
18074                rule_index: 0,
18075                invoking_state: 0,
18076            },
18077            RuleContextFrame {
18078                rule_index: 1,
18079                invoking_state: 1,
18080            },
18081            RuleContextFrame {
18082                rule_index: 2,
18083                invoking_state: 2,
18084            },
18085        ];
18086
18087        let expected = parser.context_expected_symbols(&atn);
18088
18089        assert!(expected.contains(&1));
18090        assert!(expected.contains(&TOKEN_EOF));
18091    }
18092
18093    #[test]
18094    fn prediction_context_return_states_track_rule_stack_changes() {
18095        let atn = nested_nullable_context_atn();
18096        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
18097        parser.rule_context_stack = vec![
18098            RuleContextFrame {
18099                rule_index: 0,
18100                invoking_state: 0,
18101            },
18102            RuleContextFrame {
18103                rule_index: 1,
18104                invoking_state: 1,
18105            },
18106            RuleContextFrame {
18107                rule_index: 2,
18108                invoking_state: 2,
18109            },
18110        ];
18111
18112        let initial_version = parser.rule_context_version();
18113        let first: Vec<_> = parser.prediction_context_return_states(&atn).collect();
18114        let second: Vec<_> = parser.prediction_context_return_states(&atn).collect();
18115        assert_eq!(first, second);
18116        assert_eq!(parser.rule_context_version(), initial_version);
18117
18118        parser.exit_rule();
18119        let after_pop: Vec<_> = parser.prediction_context_return_states(&atn).collect();
18120        assert_ne!(first, after_pop);
18121        assert_ne!(parser.rule_context_version(), initial_version);
18122    }
18123
18124    #[test]
18125    fn generated_match_token_recovers_missing_token_from_context_follow() {
18126        let atn = generated_match_recovery_atn();
18127        let data = RecognizerData::new(
18128            "Mini.g4",
18129            Vocabulary::new(
18130                [None, Some("'X'"), Some("'Y'")],
18131                [None, Some("X"), Some("Y")],
18132                [None::<&str>, None, None],
18133            ),
18134        );
18135        let mut parser = BaseParser::new(
18136            CommonTokenStream::new(Source {
18137                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
18138                index: 0,
18139            }),
18140            data,
18141        );
18142        parser.rule_context_stack = vec![
18143            RuleContextFrame {
18144                rule_index: 0,
18145                invoking_state: 0,
18146            },
18147            RuleContextFrame {
18148                rule_index: 1,
18149                invoking_state: 1,
18150            },
18151        ];
18152        assert_eq!(parser.number_of_syntax_errors(), 0);
18153
18154        let node = parser
18155            .match_token_recovering(2, 5, &atn)
18156            .expect("generated match should insert missing token");
18157
18158        assert_eq!(node.children().len(), 1);
18159        assert_eq!(parser.node(node.children()[0]).text(), "<missing 'Y'>");
18160        assert_eq!(
18161            node.clone()
18162                .into_child_iter()
18163                .map(|child| parser.node(child).text())
18164                .collect::<Vec<_>>(),
18165            ["<missing 'Y'>"]
18166        );
18167        // Single-token insertion synthesizes a missing token and consumes nothing,
18168        // so no EOF terminal is consumed even though lookahead is EOF.
18169        assert!(!node.consumed_eof());
18170        assert_eq!(parser.la(1), TOKEN_EOF);
18171        assert_eq!(parser.number_of_syntax_errors(), 1);
18172        assert_eq!(
18173            parser.generated_parser_diagnostics,
18174            [ParserDiagnostic {
18175                line: 1,
18176                column: 3,
18177                message: "missing 'Y' at '<EOF>'".to_owned(),
18178                offending: parser.input.lt_id(1),
18179            }]
18180        );
18181    }
18182
18183    #[test]
18184    fn generated_match_token_counts_single_token_deletion_recovery() {
18185        let atn = generated_match_recovery_atn();
18186        let data = RecognizerData::new(
18187            "Mini.g4",
18188            Vocabulary::new(
18189                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
18190                [None, Some("X"), Some("Y"), Some("Z")],
18191                [None::<&str>, None, None, None],
18192            ),
18193        );
18194        let mut parser = BaseParser::new(
18195            CommonTokenStream::new(Source {
18196                tokens: vec![
18197                    TestToken::new(3).with_text("z"),
18198                    TestToken::new(2).with_text("y"),
18199                    TestToken::eof("parser-test", 3, 1, 3),
18200                ],
18201                index: 0,
18202            }),
18203            data,
18204        );
18205
18206        let node = parser
18207            .match_token_recovering(2, 5, &atn)
18208            .expect("generated match should delete the extraneous token");
18209
18210        assert_eq!(node.children().len(), 2);
18211        assert_eq!(parser.node(node.children()[0]).kind(), NodeKind::Error);
18212        assert_eq!(parser.node(node.children()[0]).text(), "z");
18213        assert_eq!(parser.node(node.children()[1]).text(), "y");
18214        assert_eq!(
18215            node.into_child_iter()
18216                .map(|child| parser.node(child).text())
18217                .collect::<Vec<_>>(),
18218            ["z", "y"]
18219        );
18220        assert_eq!(parser.number_of_syntax_errors(), 1);
18221    }
18222
18223    #[test]
18224    fn generated_match_token_iterates_single_success_without_a_children_vec() {
18225        let atn = generated_match_recovery_atn();
18226        let data = RecognizerData::new(
18227            "Mini.g4",
18228            Vocabulary::new(
18229                [None, Some("'X'"), Some("'Y'")],
18230                [None, Some("X"), Some("Y")],
18231                [None::<&str>, None, None],
18232            ),
18233        );
18234        let mut parser = BaseParser::new(
18235            CommonTokenStream::new(Source {
18236                tokens: vec![
18237                    TestToken::new(2).with_text("y"),
18238                    TestToken::eof("parser-test", 1, 1, 1),
18239                ],
18240                index: 0,
18241            }),
18242            data,
18243        );
18244
18245        let node = parser
18246            .match_token_recovering(2, 5, &atn)
18247            .expect("generated match should consume the expected token");
18248
18249        assert_eq!(
18250            node.into_child_iter()
18251                .map(|child| parser.node(child).text())
18252                .collect::<Vec<_>>(),
18253            ["y"]
18254        );
18255        assert_eq!(parser.number_of_syntax_errors(), 0);
18256    }
18257
18258    #[test]
18259    fn generated_diagnostic_restore_rolls_back_syntax_error_count() {
18260        let atn = generated_match_recovery_atn();
18261        let data = RecognizerData::new(
18262            "Mini.g4",
18263            Vocabulary::new(
18264                [None, Some("'X'"), Some("'Y'")],
18265                [None, Some("X"), Some("Y")],
18266                [None::<&str>, None, None],
18267            ),
18268        );
18269        let mut parser = BaseParser::new(
18270            CommonTokenStream::new(Source {
18271                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
18272                index: 0,
18273            }),
18274            data,
18275        );
18276        parser.rule_context_stack = vec![
18277            RuleContextFrame {
18278                rule_index: 0,
18279                invoking_state: 0,
18280            },
18281            RuleContextFrame {
18282                rule_index: 1,
18283                invoking_state: 1,
18284            },
18285        ];
18286        let marker = parser.generated_diagnostics_checkpoint();
18287
18288        let _ = parser
18289            .match_token_recovering(2, 5, &atn)
18290            .expect("generated match should insert missing token");
18291        assert_eq!(parser.number_of_syntax_errors(), 1);
18292
18293        parser.restore_generated_diagnostics(marker);
18294
18295        assert_eq!(parser.number_of_syntax_errors(), 0);
18296        assert!(parser.generated_parser_diagnostics.is_empty());
18297    }
18298
18299    #[test]
18300    fn generated_prediction_diagnostics_use_adaptive_context() {
18301        let atn = two_alt_decision_atn();
18302        let data = RecognizerData::new(
18303            "Mini.g4",
18304            Vocabulary::new(
18305                [None, Some("'x'"), Some("'y'")],
18306                [None, Some("X"), Some("Y")],
18307                [None::<&str>, None, None],
18308            ),
18309        )
18310        .with_rule_names(["s"]);
18311        let mut parser = BaseParser::new(
18312            CommonTokenStream::new(Source {
18313                tokens: vec![
18314                    TestToken::new(1)
18315                        .with_text("x")
18316                        .with_position(1, 0)
18317                        .with_span(0, 0),
18318                    TestToken::new(2)
18319                        .with_text("y")
18320                        .with_position(1, 2)
18321                        .with_span(1, 1),
18322                    TestToken::eof("parser-test", 2, 1, 3),
18323                ],
18324                index: 0,
18325            }),
18326            data,
18327        );
18328        parser.set_report_diagnostic_errors(true);
18329
18330        parser.record_generated_prediction_diagnostic(
18331            &atn,
18332            1,
18333            &ParserAtnPrediction {
18334                alt: 1,
18335                requires_full_context: true,
18336                has_semantic_context: false,
18337                diagnostic: Some(ParserAtnPredictionDiagnostic {
18338                    kind: ParserAtnPredictionDiagnosticKind::ContextSensitivity,
18339                    start_index: 0,
18340                    sll_stop_index: 1,
18341                    ll_stop_index: 0,
18342                    conflicting_alts: vec![1, 2],
18343                    exact: false,
18344                }),
18345            },
18346        );
18347        // Ambiguities from the default LL prediction mode are non-exact, so —
18348        // matching Java's exactOnly DiagnosticErrorListener — only the
18349        // attempting-full-context line is reported. Exact-ambiguity mode
18350        // reports the ambiguity itself.
18351        parser.record_generated_prediction_diagnostic(
18352            &atn,
18353            1,
18354            &ParserAtnPrediction {
18355                alt: 1,
18356                requires_full_context: true,
18357                has_semantic_context: false,
18358                diagnostic: Some(ParserAtnPredictionDiagnostic {
18359                    kind: ParserAtnPredictionDiagnosticKind::Ambiguity,
18360                    start_index: 0,
18361                    sll_stop_index: 1,
18362                    ll_stop_index: 1,
18363                    conflicting_alts: vec![1, 2],
18364                    exact: false,
18365                }),
18366            },
18367        );
18368
18369        // The full-context/context-sensitivity diagnostic trace (order + decision + input windows)
18370        // is one snapshot rather than three ParserDiagnostic literals.
18371        insta::assert_debug_snapshot!(
18372            "generated_prediction_diagnostics_use_adaptive_context",
18373            parser.generated_parser_diagnostics
18374        );
18375    }
18376
18377    #[test]
18378    fn generated_match_not_set_recovers_empty_complement_at_eof() {
18379        let atn = complement_set_atn();
18380        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
18381        parser.rule_context_stack = vec![RuleContextFrame {
18382            rule_index: 0,
18383            invoking_state: 0,
18384        }];
18385
18386        let node = parser
18387            .match_not_token_set_recovering(
18388                atn.token_set(0).expect("excluded token set"),
18389                1,
18390                1,
18391                1,
18392                &atn,
18393            )
18394            .expect("empty complement should recover at EOF");
18395
18396        assert_eq!(node.children().len(), 1);
18397        // Recovery synthesizes a missing token without consuming EOF, so the
18398        // enclosing rule must not record EOF as its stop token.
18399        assert!(!node.consumed_eof());
18400        assert_eq!(parser.la(1), TOKEN_EOF);
18401        assert_eq!(
18402            parser.generated_parser_diagnostics,
18403            [ParserDiagnostic {
18404                line: 1,
18405                column: 1,
18406                message: "missing {} at '<EOF>'".to_owned(),
18407                offending: parser.input.lt_id(1),
18408            }]
18409        );
18410    }
18411
18412    #[test]
18413    fn wildcard_recovers_via_insertion_when_follow_expects_eof_at_eof() {
18414        // `start : . EOF ;` on empty input. The wildcard is modeled as an
18415        // empty-complement not-set; at EOF the follow state (the explicit EOF
18416        // match) expects EOF, so even in the start rule recovery must perform
18417        // single-token insertion (`<missing ...>`) rather than aborting — matching
18418        // ANTLR's `(start <missing ...> <EOF>)` / "missing ... at '<EOF>'".
18419        let atn = wildcard_then_eof_atn();
18420        let data = RecognizerData::new(
18421            "Mini.g4",
18422            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
18423        );
18424        let mut parser = BaseParser::new(
18425            CommonTokenStream::new(Source {
18426                tokens: vec![TestToken::eof("parser-test", 1, 1, 1)],
18427                index: 0,
18428            }),
18429            data,
18430        );
18431        parser.rule_context_stack = vec![RuleContextFrame {
18432            rule_index: 0,
18433            invoking_state: 0,
18434        }];
18435
18436        let node = parser
18437            .match_not_set_recovering(&[], 1, atn.max_token_type(), 2, &atn)
18438            .expect("wildcard at EOF should recover by insertion when follow expects EOF");
18439
18440        // A single `<missing ...>` error node is inserted; EOF is not consumed.
18441        assert_eq!(node.children().len(), 1);
18442        assert!(!node.consumed_eof());
18443        assert!(
18444            parser
18445                .node(node.children()[0])
18446                .text()
18447                .starts_with("<missing")
18448        );
18449        assert_eq!(parser.la(1), TOKEN_EOF);
18450        assert_eq!(
18451            parser.generated_parser_diagnostics,
18452            [ParserDiagnostic {
18453                line: 1,
18454                column: 1,
18455                message: "missing 'x' at '<EOF>'".to_owned(),
18456                offending: parser.input.lt_id(1),
18457            }]
18458        );
18459    }
18460
18461    #[test]
18462    fn generated_rule_recovery_consumes_to_parent_follow() {
18463        let atn = generated_match_recovery_atn();
18464        let data = RecognizerData::new(
18465            "Mini.g4",
18466            Vocabulary::new(
18467                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
18468                [None, Some("X"), Some("Y"), Some("Z")],
18469                [None::<&str>, None, None, None],
18470            ),
18471        );
18472        let mut parser = BaseParser::new(
18473            CommonTokenStream::new(Source {
18474                tokens: vec![
18475                    TestToken::new(3).with_text("z"),
18476                    TestToken::eof("parser-test", 1, 1, 1),
18477                ],
18478                index: 0,
18479            }),
18480            data,
18481        );
18482        let _parent = parser.enter_rule(0, 0);
18483        let marker = parser.push_invoking_state(1);
18484        let mut child = parser.enter_rule(4, 1);
18485        parser.discard_invoking_state(marker);
18486
18487        // The anchor recorded where the error was built must survive into the
18488        // dispatched diagnostic even though recovery consumes past it below.
18489        let offending = parser.input.lt_id(1);
18490        assert!(offending.is_some(), "the 'z' token should be buffered");
18491        parser.recover_generated_rule(
18492            &mut child,
18493            &atn,
18494            AntlrError::ParserError {
18495                line: 1,
18496                column: 0,
18497                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
18498                offending,
18499            },
18500        );
18501        let tree = parser.finish_rule(child, false);
18502
18503        assert_eq!(parser.la(1), TOKEN_EOF);
18504        assert_eq!(
18505            parser.node(tree).to_string_tree_with_names(&["s", "a"]),
18506            "(a z)"
18507        );
18508        assert_eq!(parser.number_of_syntax_errors(), 1);
18509        assert_eq!(
18510            parser.generated_parser_diagnostics,
18511            [ParserDiagnostic {
18512                line: 1,
18513                column: 0,
18514                message: "mismatched input 'z' expecting {'X', 'Y'}".to_owned(),
18515                offending,
18516            }]
18517        );
18518        parser.exit_rule();
18519    }
18520
18521    #[test]
18522    fn generated_rule_recovery_forces_progress_after_repeated_error_state() {
18523        let atn = nested_nullable_context_atn();
18524        let mut parser = mini_parser(vec![
18525            TestToken::new(1).with_text("x"),
18526            TestToken::eof("parser-test", 1, 1, 1),
18527        ]);
18528        parser.rule_context_stack = vec![
18529            RuleContextFrame {
18530                rule_index: 0,
18531                invoking_state: 0,
18532            },
18533            RuleContextFrame {
18534                rule_index: 1,
18535                invoking_state: 1,
18536            },
18537            RuleContextFrame {
18538                rule_index: 2,
18539                invoking_state: 2,
18540            },
18541        ];
18542        parser.set_state(20);
18543        let mut context = ParserRuleContext::new(2, 2);
18544
18545        parser.recover_generated_rule(
18546            &mut context,
18547            &atn,
18548            AntlrError::NoViableAlternative {
18549                input: "'x'".to_owned(),
18550            },
18551        );
18552        assert_eq!(parser.input.index(), 0);
18553
18554        parser.set_state(21);
18555        parser.recover_generated_rule(
18556            &mut context,
18557            &atn,
18558            AntlrError::NoViableAlternative {
18559                input: "'x'".to_owned(),
18560            },
18561        );
18562        assert_eq!(parser.input.index(), 0);
18563        assert_eq!(
18564            parser.generated_recovery_error_states,
18565            BTreeSet::from([20, 21])
18566        );
18567
18568        parser.set_state(20);
18569        parser.recover_generated_rule(
18570            &mut context,
18571            &atn,
18572            AntlrError::NoViableAlternative {
18573                input: "'x'".to_owned(),
18574            },
18575        );
18576
18577        assert_eq!(parser.input.index(), 1);
18578        assert_eq!(parser.la(1), TOKEN_EOF);
18579        assert!(context.has_matched_child());
18580        assert_eq!(parser.generated_recovery_error_states, BTreeSet::from([20]));
18581
18582        parser.match_eof().expect("EOF should match");
18583        assert_eq!(parser.generated_recovery_error_index, None);
18584        assert!(parser.generated_recovery_error_states.is_empty());
18585    }
18586
18587    #[test]
18588    fn greedy_ll1_alt_handles_nullable_loop_exit() {
18589        let mut body_symbols = TokenBitSet::default();
18590        body_symbols.insert(1);
18591        let entry = DecisionLookahead {
18592            transitions: vec![
18593                TransitionLookSet {
18594                    symbols: body_symbols,
18595                    nullable: false,
18596                },
18597                TransitionLookSet {
18598                    symbols: TokenBitSet::default(),
18599                    nullable: true,
18600                },
18601            ],
18602        };
18603
18604        assert_eq!(ll1_unique_alt(&entry, 2), None);
18605        assert_eq!(ll1_greedy_alt(&entry, 2, false), Some(1));
18606        assert_eq!(ll1_greedy_alt(&entry, 1, false), None);
18607        assert_eq!(ll1_greedy_alt(&entry, 1, true), None);
18608    }
18609
18610    #[test]
18611    fn ordinary_repetition_builds_tree_in_input_order() {
18612        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
18613            let mut parser = mini_parser(repeated_x_tokens(3));
18614            let tree = parser
18615                .parse_atn_rule(&atn, 0)
18616                .expect("ordinary repetition should parse");
18617
18618            let root = parser
18619                .node(tree)
18620                .as_rule()
18621                .expect("entry result should be a rule");
18622            let body_rules = root.child_rules(1).collect::<Vec<_>>();
18623            assert_eq!(root.text(), "xxx<EOF>");
18624            assert_eq!(body_rules.len(), 3);
18625            assert_eq!(
18626                body_rules
18627                    .iter()
18628                    .map(|rule| rule.start_id().expect("body start").index())
18629                    .collect::<Vec<_>>(),
18630                [0, 1, 2]
18631            );
18632            assert_eq!(
18633                body_rules
18634                    .iter()
18635                    .map(|rule| rule.stop_id().expect("body stop").index())
18636                    .collect::<Vec<_>>(),
18637                [0, 1, 2]
18638            );
18639            assert_eq!(parser.number_of_syntax_errors(), 0);
18640        }
18641    }
18642
18643    #[test]
18644    fn deeply_nested_deferred_rules_materialize_on_small_stack() {
18645        const DEPTH: usize = 20_000;
18646
18647        std::thread::Builder::new()
18648            .name("deferred-rule-materialization".to_owned())
18649            .stack_size(256 * 1024)
18650            .spawn(|| {
18651                let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
18652                let mut root = FastDeferredNodeId::EMPTY;
18653                for depth in 0..DEPTH {
18654                    root = parser
18655                        .recognition_arena
18656                        .deferred_rule_node(FastDeferredRule {
18657                            rule_index: u32::try_from(depth).expect("depth fits in u32"),
18658                            invoking_state: i32::try_from(depth).expect("depth fits in i32"),
18659                            start_index: 0,
18660                            stop_index: None,
18661                            deferred_children: root,
18662                            children: NodeSeqId::EMPTY,
18663                        });
18664                }
18665
18666                let (mut children, alt_number) =
18667                    parser.materialize_fast_deferred_nodes(root, NodeSeqId::EMPTY);
18668                assert_eq!(alt_number, 0);
18669                for expected_rule in (0..DEPTH).rev() {
18670                    let mut nodes = parser.recognition_arena.iter(children);
18671                    let node = nodes.next().expect("nested rule node");
18672                    assert!(nodes.next().is_none(), "each rule has one child");
18673                    let ArenaRecognizedNode::Rule {
18674                        rule_index,
18675                        children: nested,
18676                        ..
18677                    } = parser.recognition_arena.node(node)
18678                    else {
18679                        panic!("expected nested rule");
18680                    };
18681                    assert_eq!(rule_index as usize, expected_rule);
18682                    children = nested;
18683                }
18684                assert!(children.is_empty());
18685            })
18686            .expect("small-stack thread should start")
18687            .join()
18688            .expect("deferred rules should materialize without recursion");
18689    }
18690
18691    #[test]
18692    fn deferred_alternatives_preserve_left_recursive_contexts() {
18693        let mut parser = mini_parser(vec![
18694            TestToken::new(1).with_text("1"),
18695            TestToken::new(2).with_text("+"),
18696            TestToken::new(1).with_text("2"),
18697            TestToken::eof("parser-test", 3, 1, 3),
18698        ]);
18699        let base = parser.arena_token_node(0, false);
18700        let operator = parser.arena_token_node(1, false);
18701        let right = parser.arena_token_node(2, false);
18702
18703        let base = parser.recognition_arena.prepend(NodeSeqId::EMPTY, base);
18704        let base = parser.recognition_arena.deferred_fragment(base);
18705        let operator = parser.recognition_arena.prepend(NodeSeqId::EMPTY, operator);
18706        let operator = parser.recognition_arena.deferred_fragment(operator);
18707        let right = parser.recognition_arena.prepend(NodeSeqId::EMPTY, right);
18708        let right = parser.recognition_arena.deferred_fragment(right);
18709        let base_alt = parser.recognition_arena.deferred_alternative(1);
18710        let boundary = parser.recognition_arena.deferred_left_recursive_boundary(0);
18711        let operator_alt = parser.recognition_arena.deferred_alternative(6);
18712
18713        let mut deferred = FastDeferredNodeId::EMPTY;
18714        for fragment in [base_alt, base, boundary, operator_alt, operator, right] {
18715            deferred = parser
18716                .recognition_arena
18717                .concat_deferred_nodes(deferred, fragment);
18718        }
18719        let (nodes, root_alt_number) =
18720            parser.materialize_fast_deferred_nodes(deferred, NodeSeqId::EMPTY);
18721        let nodes = parser
18722            .recognition_arena
18723            .fold_left_recursive_boundaries(nodes);
18724
18725        let mut root = ParserRuleContext::new(0, -1);
18726        root.set_context_alt_number(root_alt_number);
18727        let mut cursor = nodes;
18728        while let Some(link) = parser.recognition_arena.link(cursor) {
18729            let child = parser
18730                .arena_recognized_node_tree(link.head, false, true)
18731                .expect("materialized child should become a public tree");
18732            parser.tree.add_child(&mut root, child);
18733            cursor = link.tail;
18734        }
18735        let tree = parser.rule_node(root);
18736        let contexts = parser
18737            .node(tree)
18738            .descendants()
18739            .filter_map(Node::as_rule)
18740            .map(|rule| {
18741                (
18742                    rule.rule_index(),
18743                    rule.alt_number(),
18744                    rule.context_alt_number(),
18745                    rule.text(),
18746                )
18747            })
18748            .collect::<Vec<_>>();
18749
18750        insta::assert_debug_snapshot!(
18751            "deferred_alternatives_preserve_left_recursive_contexts",
18752            contexts
18753        );
18754    }
18755
18756    #[test]
18757    fn fast_recognizer_preserves_labeled_left_recursive_operator_context() {
18758        let atn = labeled_left_recursive_operator_atn();
18759        let mut parser = mini_parser(vec![
18760            TestToken::new(1).with_text("a"),
18761            TestToken::new(3).with_text("+"),
18762            TestToken::new(1).with_text("b"),
18763            TestToken::eof("parser-test", 3, 1, 3),
18764        ]);
18765
18766        let (tree, _) = parser
18767            .parse_atn_rule_with_runtime_options(
18768                &atn,
18769                0,
18770                ParserRuntimeOptions {
18771                    track_context_alt_numbers: true,
18772                    ..ParserRuntimeOptions::default()
18773                },
18774            )
18775            .expect("labeled left-recursive addition should parse");
18776        let contexts = parser
18777            .node(tree)
18778            .descendants()
18779            .filter_map(Node::as_rule)
18780            .map(|rule| {
18781                let operator = rule
18782                    .children()
18783                    .next()
18784                    .and_then(Node::as_rule)
18785                    .is_some_and(|child| child.rule_index() == rule.rule_index());
18786                (operator, rule.context_alt_number(), rule.text())
18787            })
18788            .collect::<Vec<_>>();
18789
18790        insta::assert_debug_snapshot!(
18791            "fast_recognizer_preserves_labeled_left_recursive_operator_context",
18792            contexts
18793        );
18794        assert!(!parser.recognition_arena.deferred_nodes.is_empty());
18795        assert_eq!(parser.number_of_syntax_errors(), 0);
18796    }
18797
18798    #[test]
18799    fn deeply_nested_rule_calls_grow_the_stack() {
18800        const DEPTH: usize = 4_096;
18801        const STACK_SIZE: usize = 256 * 1024;
18802        let atn = nested_rule_chain_atn(DEPTH);
18803        std::thread::Builder::new()
18804            .name("nested-adaptive-set-rules".to_owned())
18805            .stack_size(STACK_SIZE)
18806            .spawn(move || {
18807                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
18808                parser.set_build_parse_trees(false);
18809                // This test isolates recognizer depth from the separately
18810                // cached FIRST-set metadata walk.
18811                parser.fast_first_set_prefilter = false;
18812                parser
18813                    .parse_atn_rule(&atn, 0)
18814                    .expect("nested rule chain should grow the native stack");
18815                assert_eq!(parser.input.index(), 1);
18816            })
18817            .expect("small-stack thread should start")
18818            .join()
18819            .expect("nested rule chain should not overflow its stack");
18820    }
18821
18822    #[test]
18823    fn deeply_nested_branching_rules_grow_the_stack() {
18824        const DEPTH: usize = 4_096;
18825        const STACK_SIZE: usize = 256 * 1024;
18826        let atn = nested_rule_graph_atn(DEPTH, true, false);
18827        std::thread::Builder::new()
18828            .name("nested-branching-rules".to_owned())
18829            .stack_size(STACK_SIZE)
18830            .spawn(move || {
18831                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
18832                parser.set_build_parse_trees(false);
18833                parser
18834                    .parse_atn_rule(&atn, 0)
18835                    .expect("branching rule chain should grow the native stack");
18836                assert_eq!(parser.input.index(), 1);
18837            })
18838            .expect("small-stack thread should start")
18839            .join()
18840            .expect("branching rule chain should not overflow its stack");
18841    }
18842
18843    #[test]
18844    fn deeply_nested_rule_follows_grow_the_stack() {
18845        const DEPTH: usize = 4_096;
18846        const STACK_SIZE: usize = 256 * 1024;
18847        let atn = nested_rule_graph_atn(DEPTH, false, true);
18848        std::thread::Builder::new()
18849            .name("nested-rule-follows".to_owned())
18850            .stack_size(STACK_SIZE)
18851            .spawn(move || {
18852                let mut parser = mini_parser(repeated_x_tokens(DEPTH));
18853                parser.set_build_parse_trees(false);
18854                parser.fast_first_set_prefilter = false;
18855                parser
18856                    .parse_atn_rule(&atn, 0)
18857                    .expect("rule follow chain should grow the native stack");
18858                assert_eq!(parser.input.index(), DEPTH);
18859            })
18860            .expect("small-stack thread should start")
18861            .join()
18862            .expect("nested rule follow chain should not overflow its stack");
18863    }
18864
18865    #[test]
18866    fn deeply_nested_recovery_grows_the_stack() {
18867        const DEPTH: usize = 4_096;
18868        const STACK_SIZE: usize = 256 * 1024;
18869        let atn = nested_rule_chain_atn(DEPTH);
18870        std::thread::Builder::new()
18871            .name("nested-rule-recovery".to_owned())
18872            .stack_size(STACK_SIZE)
18873            .spawn(move || {
18874                let mut parser = mini_parser(vec![
18875                    TestToken::new(2).with_text("z"),
18876                    TestToken::new(1).with_text("x"),
18877                    TestToken::eof("parser-test", 2, 1, 2),
18878                ]);
18879                parser.set_build_parse_trees(false);
18880                parser.fast_first_set_prefilter = false;
18881                parser
18882                    .parse_atn_rule(&atn, 0)
18883                    .expect("nested recovery should grow the native stack");
18884                assert_eq!(parser.input.index(), 2);
18885                assert_eq!(parser.number_of_syntax_errors(), 1);
18886            })
18887            .expect("small-stack thread should start")
18888            .join()
18889            .expect("nested rule recovery should not overflow its stack");
18890    }
18891
18892    #[test]
18893    fn ambiguous_ordinary_repetition_merges_equivalent_coordinates() {
18894        const REPETITIONS: usize = 64;
18895
18896        let atn = ambiguous_ordinary_star_loop_atn();
18897        let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
18898        let tree = parser
18899            .parse_atn_rule(&atn, 0)
18900            .expect("ambiguous ordinary repetition should parse");
18901
18902        let root = parser
18903            .node(tree)
18904            .as_rule()
18905            .expect("entry result should be a rule");
18906        assert_eq!(root.text(), format!("{}<EOF>", "x".repeat(REPETITIONS)));
18907        assert_eq!(parser.input.index(), REPETITIONS);
18908        assert!(
18909            parser.recognition_arena.deferred_nodes.len() <= REPETITIONS * 8,
18910            "equivalent segmentations should keep deferred storage linear"
18911        );
18912        assert_eq!(parser.number_of_syntax_errors(), 0);
18913    }
18914
18915    #[test]
18916    fn long_ordinary_repetition_does_not_consume_native_stack() {
18917        const REPETITIONS: usize = 20_000;
18918
18919        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
18920            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
18921            parser.set_build_parse_trees(false);
18922            parser
18923                .parse_atn_rule(&atn, 0)
18924                .expect("long ordinary repetition should parse");
18925
18926            assert_eq!(parser.input.index(), REPETITIONS);
18927            assert_eq!(parser.number_of_syntax_errors(), 0);
18928        }
18929    }
18930
18931    #[test]
18932    fn long_rule_repetition_materializes_tree_with_linear_arena_growth() {
18933        const REPETITIONS: usize = 2_000;
18934        let expected_text = format!("{}<EOF>", "x".repeat(REPETITIONS));
18935
18936        for atn in [ordinary_star_loop_atn(), ordinary_plus_loop_atn()] {
18937            let mut parser = mini_parser(repeated_x_tokens(REPETITIONS));
18938            let tree = parser
18939                .parse_atn_rule(&atn, 0)
18940                .expect("long rule repetition should parse");
18941
18942            let root = parser
18943                .node(tree)
18944                .as_rule()
18945                .expect("entry result should be a rule");
18946            assert_eq!(root.text(), expected_text);
18947            assert_eq!(root.child_rules(1).count(), REPETITIONS);
18948            let first_body = root.child_rules(1).next().expect("first body rule");
18949            let last_body = root.child_rules(1).next_back().expect("last body rule");
18950            assert_eq!(first_body.start_id().expect("first body start").index(), 0);
18951            assert_eq!(
18952                last_body.stop_id().expect("last body stop").index(),
18953                REPETITIONS - 1
18954            );
18955
18956            let stats = parser.recognition_arena_stats();
18957            assert_eq!(
18958                (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
18959                (REPETITIONS, REPETITIONS, 0)
18960            );
18961            assert_eq!(
18962                (stats.total_links, stats.live_links, stats.dead_links),
18963                (REPETITIONS, REPETITIONS, 0)
18964            );
18965            assert_eq!(parser.recognition_arena.deferred_rules.len(), REPETITIONS);
18966            assert_eq!(
18967                parser.recognition_arena.deferred_nodes.len(),
18968                REPETITIONS * 2 - 1
18969            );
18970            assert_eq!(parser.number_of_syntax_errors(), 0);
18971        }
18972    }
18973
18974    #[test]
18975    fn clean_memo_probe_selects_sparse_promote_and_reprobe_modes() {
18976        let key = |state_number| FastRecognizeKey {
18977            state_number,
18978            stop_state: 10,
18979            index: state_number,
18980            rule_start_index: 0,
18981            decision_start_index: None,
18982            precedence: 0,
18983            recovery_symbols_id: 0,
18984            recovery_state: None,
18985        };
18986
18987        let mut sparse = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
18988        for state_number in 0..(CLEAN_MEMO_PROBE_LIMIT - 1) {
18989            assert!(sparse.clean_memo_enabled_for_key(&key(state_number)));
18990        }
18991        assert!(!sparse.clean_memo_enabled_for_key(&key(CLEAN_MEMO_PROBE_LIMIT)));
18992        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Sparse);
18993
18994        let mut promote = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
18995        let repeated = key(1);
18996        for _ in 0..=CLEAN_MEMO_REPEAT_LIMIT {
18997            assert!(promote.clean_memo_enabled_for_key(&repeated));
18998        }
18999        assert_eq!(promote.clean_memo_mode, CleanMemoMode::Promote);
19000
19001        for _ in 1..CLEAN_MEMO_REPROBE_INTERVAL {
19002            assert!(!sparse.clean_memo_enabled_for_key(&repeated));
19003        }
19004        assert!(sparse.clean_memo_enabled_for_key(&repeated));
19005        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Probe);
19006        for _ in 0..CLEAN_MEMO_REPEAT_LIMIT {
19007            assert!(sparse.clean_memo_enabled_for_key(&repeated));
19008        }
19009        assert_eq!(sparse.clean_memo_mode, CleanMemoMode::Promote);
19010    }
19011
19012    #[test]
19013    fn fast_recognize_memo_capacity_scales_from_small_floor_to_bounded_maximum() {
19014        assert_eq!(
19015            fast_recognize_memo_capacity(0),
19016            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
19017        );
19018        assert_eq!(
19019            fast_recognize_memo_capacity(FAST_RECOGNIZE_MIN_MEMO_CAPACITY / 8),
19020            FAST_RECOGNIZE_MIN_MEMO_CAPACITY
19021        );
19022        assert_eq!(fast_recognize_memo_capacity(1_000), 8_000);
19023        assert_eq!(
19024            fast_recognize_memo_capacity(usize::MAX),
19025            FAST_RECOGNIZE_MAX_MEMO_CAPACITY
19026        );
19027    }
19028
19029    #[test]
19030    fn fast_recognize_scratch_reuses_small_tables_and_releases_oversized_memo() {
19031        let mut scratch = FastRecognizeTopScratch::default();
19032        scratch.prepare(FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
19033        let retained_capacity = scratch.memo.capacity();
19034        assert!(retained_capacity >= FAST_RECOGNIZE_MIN_MEMO_CAPACITY);
19035        assert!(retained_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
19036
19037        let larger_capacity = retained_capacity + 1;
19038        scratch.prepare(larger_capacity);
19039        let grown_capacity = scratch.memo.capacity();
19040        assert!(grown_capacity >= larger_capacity);
19041        assert!(grown_capacity <= FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
19042
19043        scratch.memo.insert(
19044            FastRecognizeKey {
19045                state_number: 0,
19046                stop_state: 0,
19047                index: 0,
19048                rule_start_index: 0,
19049                decision_start_index: None,
19050                precedence: 0,
19051                recovery_symbols_id: 0,
19052                recovery_state: None,
19053            },
19054            Rc::from([FastRecognizeOutcome {
19055                index: 0,
19056                consumed_eof: false,
19057                diagnostics: DiagnosticSeqId::EMPTY,
19058                deferred_nodes: FastDeferredNodeId::EMPTY,
19059                nodes: NodeSeqId::EMPTY,
19060            }]),
19061        );
19062        scratch.release_oversized_memo();
19063        assert!(scratch.memo.is_empty());
19064        assert_eq!(scratch.memo.capacity(), grown_capacity);
19065
19066        scratch
19067            .memo
19068            .reserve(FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY * 2);
19069        assert!(scratch.memo.capacity() > FAST_RECOGNIZE_MAX_RETAINED_MEMO_CAPACITY);
19070
19071        scratch.release_oversized_memo();
19072        assert!(scratch.memo.is_empty());
19073        assert_eq!(scratch.memo.capacity(), 0);
19074    }
19075
19076    #[test]
19077    fn clean_empty_multi_alt_outcomes_are_memoized() {
19078        let mut atn = ParserAtnBuilder::new(2);
19079        assert_eq!(
19080            atn.add_state(AtnStateKind::RuleStart, Some(0))
19081                .expect("state")
19082                .index(),
19083            0
19084        );
19085        assert_eq!(
19086            atn.add_state(AtnStateKind::BlockStart, Some(0))
19087                .expect("state")
19088                .index(),
19089            1
19090        );
19091        assert_eq!(
19092            atn.add_state(AtnStateKind::RuleStop, Some(0))
19093                .expect("state")
19094                .index(),
19095            2
19096        );
19097        atn.set_rule_to_start_state(vec![0])
19098            .expect("rule start states");
19099        atn.set_rule_to_stop_state(vec![2])
19100            .expect("rule stop states");
19101        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
19102            .expect("transition");
19103        atn.add_transition(
19104            1,
19105            ParserTransitionSpec::Atom {
19106                target: 2,
19107                label: 1,
19108            },
19109        )
19110        .expect("transition");
19111        atn.add_transition(
19112            1,
19113            ParserTransitionSpec::Atom {
19114                target: 2,
19115                label: 2,
19116            },
19117        )
19118        .expect("transition");
19119        let atn = finish_atn(atn);
19120
19121        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
19122        parser.fast_recovery_enabled = false;
19123        let mut visiting = FxHashSet::default();
19124        let mut memo = FxHashMap::default();
19125        let mut expected = ExpectedTokens::default();
19126        let outcomes = parser.recognize_state_fast(
19127            &atn,
19128            FastRecognizeRequest {
19129                state_number: 1,
19130                stop_state: 2,
19131                index: 0,
19132                rule_start_index: 0,
19133                decision_start_index: None,
19134                precedence: 0,
19135                depth: 0,
19136                recovery_symbols: parser.empty_recovery_symbols(),
19137                recovery_state: None,
19138            },
19139            FastRecognizeScratch {
19140                predicate_context: None,
19141                visiting: &mut visiting,
19142                memo: &mut memo,
19143                expected: &mut expected,
19144                native_depth: 0,
19145            },
19146        );
19147
19148        assert!(outcomes.is_empty());
19149        assert_eq!(memo.len(), 1);
19150        assert!(memo.values().next().expect("memo entry").is_empty());
19151
19152        parser.clean_memo_mode = CleanMemoMode::Sparse;
19153        visiting.clear();
19154        memo.clear();
19155        expected = ExpectedTokens::default();
19156        let sparse_outcomes = parser.recognize_state_fast(
19157            &atn,
19158            FastRecognizeRequest {
19159                state_number: 1,
19160                stop_state: 2,
19161                index: 0,
19162                rule_start_index: 0,
19163                decision_start_index: None,
19164                precedence: 0,
19165                depth: 0,
19166                recovery_symbols: parser.empty_recovery_symbols(),
19167                recovery_state: None,
19168            },
19169            FastRecognizeScratch {
19170                predicate_context: None,
19171                visiting: &mut visiting,
19172                memo: &mut memo,
19173                expected: &mut expected,
19174                native_depth: 0,
19175            },
19176        );
19177
19178        assert!(sparse_outcomes.is_empty());
19179        assert!(memo.is_empty());
19180    }
19181
19182    #[test]
19183    fn wildcard_matches_non_eof_only() {
19184        let mut parser = mini_parser(vec![
19185            TestToken::new(1).with_text("x"),
19186            TestToken::eof("parser-test", 1, 1, 1),
19187        ]);
19188        let matched = parser.match_wildcard().expect("wildcard");
19189        assert_eq!(parser.node(matched).text(), "x");
19190        assert!(parser.match_wildcard().is_err());
19191    }
19192
19193    #[test]
19194    fn add_parse_child_records_match_even_without_tree_building() {
19195        // `sync_decision`'s "is the current context empty" flag must reflect real
19196        // matches, not parse-tree children: when `build_parse_trees(false)`,
19197        // `children` stays empty but `has_matched_child` must still flip so nested
19198        // recovery does not wrongly suppress single-token deletion.
19199        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 1, 1, 1)]);
19200        let token = TestToken::new(1).with_text("x");
19201
19202        parser.set_build_parse_trees(false);
19203        let mut ctx = ParserRuleContext::new(0, 0);
19204        assert!(!ctx.has_matched_child());
19205        let child = parser.terminal_tree(token.id);
19206        parser.add_parse_child(&mut ctx, child);
19207        // Tree building is off, so no child is stored...
19208        assert_eq!(ctx.child_count(), 0);
19209        assert_eq!(parser.parse_tree_storage().node_count(), 0);
19210        // ...but the match is recorded, so the context is no longer "empty".
19211        assert!(ctx.has_matched_child());
19212
19213        // With tree building on, the child is stored and the match is recorded.
19214        parser.set_build_parse_trees(true);
19215        let mut ctx = ParserRuleContext::new(0, 0);
19216        let child = parser.terminal_tree(token.id);
19217        parser.add_parse_child(&mut ctx, child);
19218        assert_eq!(ctx.child_count(), 1);
19219        assert!(ctx.has_matched_child());
19220    }
19221
19222    #[test]
19223    fn disabled_tree_building_does_not_grow_flat_storage() {
19224        let mut parser = mini_parser(vec![
19225            TestToken::new(1).with_text("x"),
19226            TestToken::new(1).with_text("y"),
19227            TestToken::eof("parser-test", 2, 1, 2),
19228        ]);
19229        parser.set_build_parse_trees(false);
19230        let mut context = ParserRuleContext::new(0, -1);
19231
19232        for _ in 0..2 {
19233            let child = parser.match_token(1).expect("token should match");
19234            parser.add_parse_child(&mut context, child);
19235        }
19236        let current = parser.input.lt_id(1).expect("EOF token");
19237        let error = parser.error_tree(current);
19238        parser.add_parse_child(&mut context, error);
19239        let root = parser.rule_node(context);
19240
19241        assert_eq!(
19242            parser.parse_tree_storage().stats(),
19243            ParseTreeStats::default()
19244        );
19245        assert!(
19246            parser
19247                .parse_tree_storage()
19248                .node(parser.token_store(), root)
19249                .is_none(),
19250            "the no-tree sentinel must not resolve to stored data"
19251        );
19252    }
19253
19254    #[test]
19255    fn disabled_tree_building_skips_recognition_rule_node_storage() {
19256        let atn = ordinary_star_loop_atn();
19257        let mut parser = mini_parser(repeated_x_tokens(3));
19258        parser.set_build_parse_trees(false);
19259
19260        parser
19261            .parse_atn_rule(&atn, 0)
19262            .expect("ordinary repetition should parse without a tree");
19263
19264        assert_eq!(parser.input.index(), 3);
19265        assert!(parser.recognition_arena.nodes.is_empty());
19266        assert!(parser.recognition_arena.seq_links.is_empty());
19267        assert!(parser.recognition_arena.deferred_nodes.is_empty());
19268        assert!(parser.recognition_arena.deferred_rules.is_empty());
19269        assert!(!parser.fast_token_nodes_enabled);
19270        assert!(parser.fast_recognize_scratch.memo.is_empty());
19271    }
19272
19273    #[test]
19274    fn parser_interprets_simple_atn_rule() {
19275        let atn = token_then_eof_atn();
19276        let mut parser = mini_parser(vec![
19277            TestToken::new(1).with_text("x"),
19278            TestToken::eof("parser-test", 1, 1, 1),
19279        ]);
19280
19281        let tree = parser
19282            .parse_atn_rule(&atn, 0)
19283            .expect("artificial parser rule should parse");
19284        assert_eq!(parser.node(tree).text(), "x<EOF>");
19285        assert_eq!(parser.number_of_syntax_errors(), 0);
19286        assert_eq!(
19287            parser
19288                .node(tree)
19289                .first_rule_stop(0)
19290                .expect("rule should stop at EOF")
19291                .token_type(),
19292            TOKEN_EOF
19293        );
19294
19295        let mut parser = mini_parser(vec![
19296            TestToken::new(1).with_text("x"),
19297            TestToken::eof("parser-test", 1, 1, 1),
19298        ]);
19299        let (tree, actions) = parser
19300            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
19301            .expect("runtime-option parser rule should parse");
19302        assert!(actions.is_empty());
19303        assert_eq!(
19304            parser
19305                .node(tree)
19306                .first_rule_stop(0)
19307                .expect("rule should stop at EOF")
19308                .token_type(),
19309            TOKEN_EOF
19310        );
19311    }
19312
19313    #[test]
19314    fn runtime_options_default_ignores_noop_action_transitions() {
19315        let atn = noop_action_then_token_then_eof_atn();
19316        let mut parser = mini_parser(vec![
19317            TestToken::new(1).with_text("x"),
19318            TestToken::eof("parser-test", 1, 1, 1),
19319        ]);
19320
19321        let (tree, actions) = parser
19322            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
19323            .expect("no-op parser action should not force action replay");
19324
19325        assert_eq!(parser.node(tree).text(), "x<EOF>");
19326        assert!(
19327            actions.is_empty(),
19328            "action_index=None transitions are ANTLR metadata, not replay actions"
19329        );
19330        assert_eq!(parser.number_of_syntax_errors(), 0);
19331    }
19332
19333    #[test]
19334    fn parser_exposes_buffered_token_stream_after_parse() {
19335        let atn = token_then_eof_atn();
19336        let mut parser = mini_parser(vec![
19337            TestToken::new(1).with_text("x"),
19338            TestToken::eof("parser-test", 1, 1, 1),
19339        ]);
19340
19341        let tree = parser
19342            .parse_atn_rule(&atn, 0)
19343            .expect("artificial parser rule should parse");
19344        assert_eq!(parser.node(tree).text(), "x<EOF>");
19345
19346        let stream = parser.token_stream();
19347        let source_index_after_parse = stream.token_source().index;
19348        let buffered = stream.tokens().collect::<Vec<_>>();
19349        assert_eq!(buffered.len(), 2);
19350        assert_eq!(buffered[0].text(), Some("x"));
19351        assert_eq!(buffered[0].token_id().index(), 0);
19352        assert_eq!(buffered[1].token_type(), TOKEN_EOF);
19353        assert_eq!(stream.token_source().index, source_index_after_parse);
19354        drop(buffered);
19355
19356        let stream = parser.into_token_stream();
19357        assert_eq!(stream.token_source().index, source_index_after_parse);
19358        assert_eq!(
19359            stream.tokens().next().expect("first token").text(),
19360            Some("x")
19361        );
19362        assert_eq!(
19363            stream.tokens().nth(1).expect("EOF token").token_type(),
19364            TOKEN_EOF
19365        );
19366    }
19367
19368    #[test]
19369    fn parsed_file_exposes_all_buffered_tokens() {
19370        let atn = token_then_eof_atn();
19371        let mut parser = mini_parser(vec![
19372            TestToken::new(99)
19373                .with_text(" comment")
19374                .with_channel(HIDDEN_CHANNEL),
19375            TestToken::new(1).with_text("x"),
19376            TestToken::eof("parser-test", 9, 1, 9),
19377        ]);
19378
19379        let tree = parser
19380            .parse_atn_rule(&atn, 0)
19381            .expect("artificial parser rule should parse");
19382        let parsed = parser.into_parsed_file(tree);
19383
19384        // Snapshot the full buffered stream — hidden-channel comment, default-channel token, EOF —
19385        // as (type, channel, text) triples; contents make the count self-evident.
19386        insta::assert_debug_snapshot!(
19387            "parsed_file_exposes_all_buffered_tokens",
19388            parsed
19389                .tokens()
19390                .iter()
19391                .map(|token| (token.token_type(), token.channel(), token.text()))
19392                .collect::<Vec<_>>()
19393        );
19394        assert_eq!(parsed.tokens().into_iter().count(), 3);
19395    }
19396
19397    #[test]
19398    fn parser_syntax_error_count_tracks_interpreted_recovery() {
19399        let atn = token_then_eof_atn();
19400        let mut parser = mini_parser(vec![
19401            TestToken::new(1).with_text("x"),
19402            TestToken::new(2).with_text("y"),
19403            TestToken::eof("parser-test", 2, 1, 2),
19404        ]);
19405
19406        let tree = parser
19407            .parse_atn_rule(&atn, 0)
19408            .expect("invalid token should recover into an error node");
19409
19410        assert_eq!(parser.number_of_syntax_errors(), 1);
19411        assert_eq!(
19412            parser
19413                .node(tree)
19414                .first_error_token()
19415                .expect("recovery should embed an error token")
19416                .text(),
19417            Some("y")
19418        );
19419    }
19420
19421    #[test]
19422    fn failed_interpreted_parse_notifies_error_listener() {
19423        let atn = token_then_eof_atn();
19424        let mut parser = mini_parser(vec![
19425            TestToken::new(2)
19426                .with_text("y")
19427                .with_span(0, 0)
19428                .with_byte_span(0, 1)
19429                .with_position(3, 5),
19430            TestToken::eof("parser-test", 1, 1, 1),
19431        ]);
19432        parser.remove_error_listeners();
19433        let diagnostics = Arc::new(Mutex::new(Vec::new()));
19434        parser.add_error_listener(RecordingErrorListener {
19435            diagnostics: Arc::clone(&diagnostics),
19436        });
19437
19438        let error = parser
19439            .parse_atn_rule(&atn, 0)
19440            .expect_err("start-rule mismatch should remain a parser error");
19441
19442        assert_eq!(parser.number_of_syntax_errors(), 1);
19443        assert!(matches!(&error, AntlrError::ParserError { .. }));
19444        insta::assert_debug_snapshot!(
19445            "failed_interpreted_parse_notifies_error_listener",
19446            *diagnostics.lock().expect("recorded diagnostics lock")
19447        );
19448    }
19449
19450    #[test]
19451    fn adaptive_direct_rule_uses_simulator_decision() {
19452        let atn = two_alt_decision_atn();
19453        let mut simulator = ParserAtnSimulator::new(&atn);
19454        let mut parser = mini_parser(vec![
19455            TestToken::new(2).with_text("y"),
19456            TestToken::eof("parser-test", 1, 1, 1),
19457        ]);
19458
19459        let tree = parser
19460            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
19461            .expect("direct adaptive rule should parse");
19462
19463        assert_eq!(parser.node(tree).text(), "y");
19464        assert_eq!(parser.input.index(), 1);
19465    }
19466
19467    #[test]
19468    fn adaptive_direct_rule_restores_input_on_fallback() {
19469        let atn = predicate_after_token_atn();
19470        let mut simulator = ParserAtnSimulator::new(&atn);
19471        let mut parser = mini_parser(vec![
19472            TestToken::new(1).with_text("x"),
19473            TestToken::new(2).with_text("y"),
19474            TestToken::eof("parser-test", 2, 1, 2),
19475        ]);
19476
19477        let tree = parser
19478            .parse_atn_rule_adaptive_or_fallback(&atn, &mut simulator, 0)
19479            .expect("fallback recognizer should parse");
19480
19481        assert_eq!(parser.node(tree).text(), "xy");
19482        assert_eq!(parser.input.index(), 2);
19483        let stats = parser.parse_tree_storage().stats();
19484        assert_eq!(stats.nodes, parser.node(tree).descendants().count());
19485        assert_eq!(stats.edges, stats.nodes.saturating_sub(1));
19486        assert_eq!(stats.scratch_links, 0);
19487    }
19488
19489    #[test]
19490    fn unknown_predicate_policy_defaults_to_assume_true() {
19491        let atn = predicate_after_token_atn();
19492        let mut parser = mini_parser(vec![
19493            TestToken::new(1).with_text("x"),
19494            TestToken::new(2).with_text("y"),
19495            TestToken::eof("parser-test", 2, 1, 2),
19496        ]);
19497
19498        let (tree, _) = parser
19499            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
19500            .expect("unknown predicate should pass under the default policy");
19501
19502        assert_eq!(parser.node(tree).text(), "xy");
19503        assert_eq!(parser.number_of_syntax_errors(), 0);
19504    }
19505
19506    #[test]
19507    fn private_context_alt_tracking_keeps_fast_predicate_recognition() {
19508        let atn = predicate_gated_same_lookahead_atn([0, 1]);
19509        let mut parser = mini_parser(vec![
19510            TestToken::new(1).with_text("x"),
19511            TestToken::eof("parser-test", 1, 1, 1),
19512        ]);
19513
19514        let (tree, _) = parser
19515            .parse_atn_rule_with_runtime_options(
19516                &atn,
19517                0,
19518                ParserRuntimeOptions {
19519                    predicates: &[
19520                        (0, 0, ParserPredicate::False),
19521                        (0, 1, ParserPredicate::True),
19522                    ],
19523                    track_context_alt_numbers: true,
19524                    ..ParserRuntimeOptions::default()
19525                },
19526            )
19527            .expect("the second predicate-gated alternative should match");
19528
19529        let root = parser.node(tree).as_rule().expect("entry result is a rule");
19530        insta::assert_debug_snapshot!(
19531            "private_context_alt_tracking_keeps_fast_predicate_recognition",
19532            (root.alt_number(), root.context_alt_number(), root.text())
19533        );
19534        assert_eq!(parser.number_of_syntax_errors(), 0);
19535        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&false));
19536        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 1)), Some(&true));
19537    }
19538
19539    #[test]
19540    fn nested_interpreted_parse_preserves_prior_unknown_predicate_hits() {
19541        // A generated parent may record an unknown-predicate coordinate, then
19542        // descend into an interpreted child. The child's interpreter entry must
19543        // not wipe the parent's recorded hit before the top-level surfaces it.
19544        let atn = token_then_eof_atn();
19545        let mut parser = mini_parser(vec![
19546            TestToken::new(1).with_text("x"),
19547            TestToken::eof("parser-test", 1, 1, 1),
19548        ]);
19549
19550        // Simulate the parent having recorded a fail-loud coordinate.
19551        parser.unknown_predicate_hits.push((7, 3));
19552
19553        // Run an interpreted child parse that records no coordinate of its own.
19554        parser
19555            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
19556            .expect("child rule parses");
19557
19558        // The parent's coordinate must still be present for the top-level entry.
19559        let error = parser
19560            .take_unknown_semantic_error()
19561            .expect("parent's recorded coordinate must survive the nested interpreted parse");
19562        let AntlrError::Unsupported(message) = error else {
19563            panic!("expected AntlrError::Unsupported, got {error:?}");
19564        };
19565        assert!(message.contains("pred_index=3"), "message: {message}");
19566    }
19567
19568    #[test]
19569    fn nested_committed_parse_preserves_prior_unhandled_action_hits() {
19570        let atn = token_then_eof_atn();
19571        let mut parser = mini_parser(vec![
19572            TestToken::new(1).with_text("x"),
19573            TestToken::eof("parser-test", 1, 1, 1),
19574        ]);
19575        parser.unhandled_action_hits.push((7, 42));
19576
19577        parser
19578            .parse_atn_rule_with_runtime_options(
19579                &atn,
19580                0,
19581                ParserRuntimeOptions {
19582                    action_indices: &[(usize::MAX, 0)],
19583                    ..ParserRuntimeOptions::default()
19584                },
19585            )
19586            .expect("a child with no action miss must not observe its parent's miss");
19587
19588        let error = parser
19589            .take_unknown_semantic_error()
19590            .expect("the parent's action miss must survive the nested committed parse");
19591        let AntlrError::Unsupported(message) = error else {
19592            panic!("expected AntlrError::Unsupported, got {error:?}");
19593        };
19594        assert!(
19595            message.contains("rule_index=7") && message.contains("state=42"),
19596            "message: {message}"
19597        );
19598    }
19599
19600    #[test]
19601    fn unknown_predicate_policy_assume_false_kills_the_guarded_path() {
19602        let atn = predicate_after_token_atn();
19603        let mut parser = mini_parser(vec![
19604            TestToken::new(1).with_text("x"),
19605            TestToken::new(2).with_text("y"),
19606            TestToken::eof("parser-test", 2, 1, 2),
19607        ]);
19608
19609        let result = parser.parse_atn_rule_with_runtime_options(
19610            &atn,
19611            0,
19612            ParserRuntimeOptions {
19613                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
19614                ..ParserRuntimeOptions::default()
19615            },
19616        );
19617
19618        assert!(
19619            result.is_err(),
19620            "the only path is predicate-guarded, so assume-false must fail the parse"
19621        );
19622    }
19623
19624    #[test]
19625    fn predicate_failure_message_keeps_semantic_recovery_path() {
19626        let atn = predicate_after_token_atn();
19627        let mut parser = mini_parser(vec![
19628            TestToken::new(1).with_text("x"),
19629            TestToken::new(2).with_text("y"),
19630            TestToken::eof("parser-test", 2, 1, 2),
19631        ]);
19632
19633        let (tree, _) = parser
19634            .parse_atn_rule_with_runtime_options(
19635                &atn,
19636                0,
19637                ParserRuntimeOptions {
19638                    predicates: &[(
19639                        0,
19640                        0,
19641                        ParserPredicate::FalseWithMessage {
19642                            message: "predicate rejected input",
19643                        },
19644                    )],
19645                    ..ParserRuntimeOptions::default()
19646                },
19647            )
19648            .expect("failure-message predicates recover through the semantic interpreter");
19649
19650        assert_eq!(parser.node(tree).text(), "xy");
19651        assert_eq!(parser.number_of_syntax_errors(), 1);
19652        assert!(
19653            parser.fast_predicate_cache.is_empty(),
19654            "failure-message predicates need the semantic interpreter's recovery outcome"
19655        );
19656    }
19657
19658    #[test]
19659    fn unknown_predicate_policy_error_names_the_coordinate() {
19660        let atn = predicate_after_token_atn();
19661        let mut parser = mini_parser(vec![
19662            TestToken::new(1).with_text("x"),
19663            TestToken::new(2).with_text("y"),
19664            TestToken::eof("parser-test", 2, 1, 2),
19665        ]);
19666
19667        let error = parser
19668            .parse_atn_rule_with_runtime_options(
19669                &atn,
19670                0,
19671                ParserRuntimeOptions {
19672                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
19673                    ..ParserRuntimeOptions::default()
19674                },
19675            )
19676            .expect_err("evaluating an unknown predicate under Error policy must fail");
19677
19678        let AntlrError::Unsupported(message) = error else {
19679            panic!("expected AntlrError::Unsupported, got {error:?}");
19680        };
19681        assert!(
19682            message.contains("unsupported semantic predicate"),
19683            "message should name the failure class: {message}"
19684        );
19685        assert!(
19686            message.contains("pred_index=0"),
19687            "message should carry the coordinate: {message}"
19688        );
19689    }
19690
19691    #[test]
19692    fn fail_loud_hits_do_not_leak_into_a_reused_interpreter_parse() {
19693        // A parser reused after a fail-loud parse must not carry the old
19694        // coordinates into a later parse. The fail-loud return keeps the hits
19695        // (so a generated parent can surface a recovered child's coordinate),
19696        // and the next parse's entry stashes/replaces them, so a subsequent
19697        // clean parse surfaces no stale error.
19698        let atn = predicate_after_token_atn();
19699        let mut parser = mini_parser(vec![
19700            TestToken::new(1).with_text("x"),
19701            TestToken::new(2).with_text("y"),
19702            TestToken::eof("parser-test", 2, 1, 2),
19703        ]);
19704
19705        parser
19706            .parse_atn_rule_with_runtime_options(
19707                &atn,
19708                0,
19709                ParserRuntimeOptions {
19710                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
19711                    ..ParserRuntimeOptions::default()
19712                },
19713            )
19714            .expect_err("first parse fails loud under the Error policy");
19715
19716        // The failed parse kept its coordinate on the parser (so a generated
19717        // parent could surface a recovered child). A top-level reuse resets the
19718        // hits — generated parsers call `reset_unknown_semantic_hits` at their
19719        // public entry; direct interpreter-API callers do the same.
19720        parser.reset_unknown_semantic_hits();
19721        assert!(
19722            parser.take_unknown_semantic_error().is_none(),
19723            "reset must drop stale unknown-predicate coordinates before a reused parse"
19724        );
19725    }
19726
19727    #[derive(Debug, Default)]
19728    struct RecordingHooks {
19729        predicates: Vec<(usize, usize, usize, Option<String>)>,
19730        actions: Vec<(usize, String, Option<String>)>,
19731        action_trees: Vec<Option<String>>,
19732    }
19733
19734    impl SemanticHooks for RecordingHooks {
19735        fn sempred<S>(
19736            &mut self,
19737            ctx: &mut ParserSemCtx<'_, S>,
19738            rule_index: usize,
19739            pred_index: usize,
19740        ) -> Option<bool>
19741        where
19742            S: TokenSource,
19743        {
19744            self.predicates.push((
19745                ctx.input_index(),
19746                rule_index,
19747                pred_index,
19748                ctx.token_text(1)
19749                    .and_then(|token| token.text().map(str::to_owned)),
19750            ));
19751            Some(true)
19752        }
19753
19754        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
19755        where
19756            S: TokenSource,
19757        {
19758            self.actions.push((
19759                action.source_state(),
19760                ctx.action_text(),
19761                ctx.rule_name().map(str::to_owned),
19762            ));
19763            self.action_trees.push(ctx.tree().map(Node::text));
19764            true
19765        }
19766    }
19767
19768    #[derive(Debug, Default)]
19769    struct StatefulActionHooks {
19770        entered: bool,
19771        events: Vec<String>,
19772    }
19773
19774    impl SemanticHooks for StatefulActionHooks {
19775        fn sempred<S>(
19776            &mut self,
19777            _ctx: &mut ParserSemCtx<'_, S>,
19778            _rule_index: usize,
19779            _pred_index: usize,
19780        ) -> Option<bool>
19781        where
19782            S: TokenSource,
19783        {
19784            self.events.push(format!("predicate:{}", self.entered));
19785            Some(self.entered)
19786        }
19787
19788        fn action<S>(&mut self, _ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
19789        where
19790            S: TokenSource,
19791        {
19792            self.events.push(format!(
19793                "action:{}",
19794                action
19795                    .action_index()
19796                    .map_or_else(|| "legacy".to_owned(), |index| index.to_string())
19797            ));
19798            self.entered = true;
19799            true
19800        }
19801    }
19802
19803    #[derive(Debug, Default)]
19804    struct InitOrderingHooks {
19805        initialized: bool,
19806        events: Vec<String>,
19807    }
19808
19809    impl SemanticHooks for InitOrderingHooks {
19810        fn sempred<S>(
19811            &mut self,
19812            _ctx: &mut ParserSemCtx<'_, S>,
19813            _rule_index: usize,
19814            _pred_index: usize,
19815        ) -> Option<bool>
19816        where
19817            S: TokenSource,
19818        {
19819            self.events.push(format!("predicate:{}", self.initialized));
19820            Some(self.initialized)
19821        }
19822
19823        fn action<S>(&mut self, _ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
19824        where
19825            S: TokenSource,
19826        {
19827            if action.is_rule_init() {
19828                self.initialized = true;
19829                self.events.push("init".to_owned());
19830            } else {
19831                self.events.push(format!(
19832                    "action:{}:initialized={}",
19833                    action
19834                        .action_index()
19835                        .map_or_else(|| "legacy".to_owned(), |index| index.to_string()),
19836                    self.initialized
19837                ));
19838            }
19839            true
19840        }
19841    }
19842
19843    #[derive(Debug, Default)]
19844    struct ActionContextHooks {
19845        actions: Vec<(usize, Option<i64>, Option<usize>)>,
19846    }
19847
19848    impl SemanticHooks for ActionContextHooks {
19849        fn action<S>(&mut self, ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
19850        where
19851            S: TokenSource,
19852        {
19853            self.actions.push((
19854                action.action_index().unwrap_or(usize::MAX),
19855                ctx.local_int_arg(),
19856                action.stop_index(),
19857            ));
19858            true
19859        }
19860    }
19861
19862    #[derive(Debug, Default)]
19863    struct DecliningActionHooks {
19864        actions: Vec<usize>,
19865    }
19866
19867    impl SemanticHooks for DecliningActionHooks {
19868        fn action<S>(&mut self, _ctx: &mut ParserSemCtx<'_, S>, action: ParserAction) -> bool
19869        where
19870            S: TokenSource,
19871        {
19872            self.actions.push(action.source_state());
19873            false
19874        }
19875    }
19876
19877    #[derive(Debug, Default)]
19878    struct ForcedSecondAlternativeHooks {
19879        decisions: Vec<(usize, usize, usize)>,
19880    }
19881
19882    impl SemanticHooks for ForcedSecondAlternativeHooks {
19883        fn observes_parser_decisions(&self) -> bool {
19884            true
19885        }
19886
19887        fn parser_decision_override(
19888            &mut self,
19889            decision: usize,
19890            input_index: usize,
19891            alternative_count: usize,
19892        ) -> Option<usize> {
19893            self.decisions
19894                .push((decision, input_index, alternative_count));
19895            Some(2)
19896        }
19897    }
19898
19899    struct RecordingParseListener {
19900        events: Arc<Mutex<Vec<String>>>,
19901    }
19902
19903    impl ParseListener for RecordingParseListener {
19904        fn enter_every_rule(&mut self, event: &EnterRuleEvent<'_>) -> Result<(), AntlrError> {
19905            self.events
19906                .lock()
19907                .expect("parse-listener event lock")
19908                .push(format!("enter:{}", event.rule_index));
19909            Ok(())
19910        }
19911
19912        fn exit_every_rule(&mut self, rule_index: usize) {
19913            self.events
19914                .lock()
19915                .expect("parse-listener event lock")
19916                .push(format!("exit:{rule_index}"));
19917        }
19918    }
19919
19920    #[derive(Debug, Default)]
19921    struct RejectingPredicateHooks {
19922        predicates: Vec<(usize, usize, usize, Option<String>)>,
19923    }
19924
19925    impl SemanticHooks for RejectingPredicateHooks {
19926        fn sempred<S>(
19927            &mut self,
19928            ctx: &mut ParserSemCtx<'_, S>,
19929            rule_index: usize,
19930            pred_index: usize,
19931        ) -> Option<bool>
19932        where
19933            S: TokenSource,
19934        {
19935            self.predicates.push((
19936                ctx.input_index(),
19937                rule_index,
19938                pred_index,
19939                ctx.token_text(1)
19940                    .and_then(|token| token.text().map(str::to_owned)),
19941            ));
19942            Some(false)
19943        }
19944    }
19945
19946    #[test]
19947    fn fast_predicate_cache_replays_hook_once_per_coordinate_and_input() {
19948        let atn = predicate_gated_same_lookahead_atn([0, 0]);
19949        let mut parser = mini_parser_with_hooks(
19950            vec![
19951                TestToken::new(1).with_text("x"),
19952                TestToken::eof("parser-test", 1, 1, 1),
19953            ],
19954            RecordingHooks::default(),
19955        );
19956
19957        let (tree, _) = parser
19958            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
19959            .expect("both alternatives share one replay-safe predicate result");
19960
19961        assert_eq!(parser.node(tree).text(), "x<EOF>");
19962        assert_eq!(
19963            parser.semantic_hooks.predicates,
19964            vec![(0, 0, 0, Some("x".to_owned()))]
19965        );
19966        assert_eq!(parser.fast_predicate_cache.get(&(0, 0, 0)), Some(&true));
19967    }
19968
19969    #[test]
19970    fn semantic_hook_handles_unknown_predicate_before_error_policy() {
19971        let atn = predicate_after_token_atn();
19972        let mut parser = mini_parser_with_hooks(
19973            vec![
19974                TestToken::new(1).with_text("x"),
19975                TestToken::new(2).with_text("y"),
19976                TestToken::eof("parser-test", 2, 1, 2),
19977            ],
19978            RecordingHooks::default(),
19979        );
19980
19981        let (tree, _) = parser
19982            .parse_atn_rule_with_runtime_options(
19983                &atn,
19984                0,
19985                ParserRuntimeOptions {
19986                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
19987                    ..ParserRuntimeOptions::default()
19988                },
19989            )
19990            .expect("hook supplies the missing predicate result");
19991
19992        assert_eq!(parser.node(tree).text(), "xy");
19993        assert_eq!(
19994            parser.semantic_hooks.predicates,
19995            vec![(1, 0, 0, Some("y".to_owned()))]
19996        );
19997        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&true));
19998    }
19999
20000    #[test]
20001    fn runtime_options_default_preserves_semantic_hook_predicates() {
20002        let atn = predicate_after_token_atn();
20003        let mut parser = mini_parser_with_hooks(
20004            vec![
20005                TestToken::new(1).with_text("x"),
20006                TestToken::new(2).with_text("y"),
20007                TestToken::eof("parser-test", 2, 1, 2),
20008            ],
20009            RejectingPredicateHooks::default(),
20010        );
20011
20012        let result =
20013            parser.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default());
20014
20015        assert!(
20016            result.is_err(),
20017            "default runtime options must not bypass semantic hooks for predicate ATNs"
20018        );
20019        assert_eq!(
20020            parser.semantic_hooks.predicates,
20021            vec![(1, 0, 0, Some("y".to_owned()))]
20022        );
20023        assert_eq!(parser.fast_predicate_cache.get(&(1, 0, 0)), Some(&false));
20024    }
20025
20026    #[test]
20027    fn committed_action_runs_before_later_predicate() {
20028        let atn = committed_action_then_predicate_atn();
20029        let mut parser = mini_parser_with_hooks(
20030            vec![
20031                TestToken::new(1).with_text("x"),
20032                TestToken::eof("parser-test", 1, 1, 1),
20033            ],
20034            StatefulActionHooks::default(),
20035        );
20036
20037        let (tree, deferred_actions) = parser
20038            .parse_atn_rule_with_runtime_options(
20039                &atn,
20040                0,
20041                ParserRuntimeOptions {
20042                    action_indices: &[(0, 7)],
20043                    ..ParserRuntimeOptions::default()
20044                },
20045            )
20046            .expect("the predicate should observe the preceding committed action");
20047
20048        assert_eq!(parser.node(tree).text(), "x<EOF>");
20049        assert!(deferred_actions.is_empty());
20050        assert_eq!(parser.semantic_hooks.events, ["action:7", "predicate:true"]);
20051    }
20052
20053    #[test]
20054    fn committed_action_hook_observes_parameterized_rule_argument() {
20055        let atn = parameterized_child_action_eof_atn();
20056        let rule_args = [ParserRuleArg {
20057            source_state: 0,
20058            rule_index: 1,
20059            value: 42,
20060            inherit_local: false,
20061        }];
20062        let mut parser = mini_parser_with_hooks(
20063            vec![TestToken::eof("parser-test", 0, 1, 0)],
20064            ActionContextHooks::default(),
20065        );
20066
20067        parser
20068            .parse_atn_rule_with_runtime_options(
20069                &atn,
20070                0,
20071                ParserRuntimeOptions {
20072                    action_indices: &[(1, 20), (4, 10)],
20073                    rule_args: &rule_args,
20074                    ..ParserRuntimeOptions::default()
20075                },
20076            )
20077            .expect("the parameterized child should parse");
20078
20079        assert_eq!(
20080            parser.semantic_hooks.actions[0],
20081            (10, Some(42), None),
20082            "the child action should observe its invocation argument"
20083        );
20084    }
20085
20086    #[test]
20087    fn committed_parent_propagates_child_eof_consumption() {
20088        let atn = parameterized_child_action_eof_atn();
20089        let mut parser = mini_parser_with_hooks(
20090            vec![TestToken::eof("parser-test", 0, 1, 0)],
20091            ActionContextHooks::default(),
20092        );
20093
20094        let (tree, _) = parser
20095            .parse_atn_rule_with_runtime_options(
20096                &atn,
20097                0,
20098                ParserRuntimeOptions {
20099                    action_indices: &[(1, 20), (4, 10)],
20100                    ..ParserRuntimeOptions::default()
20101                },
20102            )
20103            .expect("the parent should retain its child's EOF boundary");
20104
20105        assert_eq!(
20106            parser.semantic_hooks.actions[1],
20107            (20, None, Some(0)),
20108            "the parent action should stop at EOF"
20109        );
20110        let root = parser.node(tree).as_rule().expect("entry result is a rule");
20111        assert_eq!(root.stop().map(|token| token.token_type()), Some(TOKEN_EOF));
20112        let child = root
20113            .child_rules(1)
20114            .next()
20115            .expect("the parent should contain the child rule");
20116        assert_eq!(
20117            child.stop().map(|token| token.token_type()),
20118            Some(TOKEN_EOF)
20119        );
20120    }
20121
20122    #[test]
20123    fn committed_walker_does_not_run_action_in_losing_alternative() {
20124        let atn = losing_alternative_action_atn();
20125        let mut parser = mini_parser_with_hooks(
20126            vec![
20127                TestToken::new(2).with_text("y"),
20128                TestToken::eof("parser-test", 1, 1, 1),
20129            ],
20130            StatefulActionHooks::default(),
20131        );
20132
20133        let (tree, deferred_actions) = parser
20134            .parse_atn_rule_with_runtime_options(
20135                &atn,
20136                0,
20137                ParserRuntimeOptions {
20138                    action_indices: &[(2, 0)],
20139                    ..ParserRuntimeOptions::default()
20140                },
20141            )
20142            .expect("the token-led second alternative should be selected");
20143
20144        assert_eq!(parser.node(tree).text(), "y");
20145        assert!(deferred_actions.is_empty());
20146        assert!(parser.semantic_hooks.events.is_empty());
20147    }
20148
20149    #[test]
20150    fn committed_walker_honors_decision_overrides() {
20151        let atn = predicate_gated_same_lookahead_atn([0, 1]);
20152        let predicates = [(0, 0, ParserPredicate::True), (0, 1, ParserPredicate::True)];
20153        let mut parser = mini_parser_with_hooks(
20154            vec![
20155                TestToken::new(1).with_text("x"),
20156                TestToken::eof("parser-test", 1, 1, 1),
20157            ],
20158            ForcedSecondAlternativeHooks::default(),
20159        );
20160
20161        let (tree, deferred_actions) = parser
20162            .parse_atn_rule_with_runtime_options(
20163                &atn,
20164                0,
20165                ParserRuntimeOptions {
20166                    action_indices: &[(usize::MAX, 0)],
20167                    track_alt_numbers: true,
20168                    predicates: &predicates,
20169                    ..ParserRuntimeOptions::default()
20170                },
20171            )
20172            .expect("the forced second alternative should parse");
20173
20174        let root = parser.node(tree).as_rule().expect("entry result is a rule");
20175        assert_eq!(root.alt_number(), 2);
20176        assert_eq!(root.text(), "x<EOF>");
20177        assert!(deferred_actions.is_empty());
20178        assert_eq!(parser.semantic_hooks.decisions, [(0, 0, 2)]);
20179        assert_eq!(parser.number_of_syntax_errors(), 0);
20180    }
20181
20182    #[test]
20183    fn committed_walker_sll_mode_does_not_report_full_context_diagnostics() {
20184        let atn = predicate_gated_same_lookahead_atn([0, 1]);
20185        let predicates = [(0, 0, ParserPredicate::True), (0, 1, ParserPredicate::True)];
20186        let diagnostics = Arc::new(Mutex::new(Vec::new()));
20187        let mut parser = mini_parser(vec![
20188            TestToken::new(1).with_text("x"),
20189            TestToken::eof("parser-test", 1, 1, 1),
20190        ]);
20191        parser.set_prediction_mode(PredictionMode::Sll);
20192        parser.set_report_diagnostic_errors(true);
20193        parser.remove_error_listeners();
20194        parser.add_error_listener(RecordingErrorListener {
20195            diagnostics: Arc::clone(&diagnostics),
20196        });
20197
20198        let (tree, deferred_actions) = parser
20199            .parse_atn_rule_with_runtime_options(
20200                &atn,
20201                0,
20202                ParserRuntimeOptions {
20203                    action_indices: &[(usize::MAX, 0)],
20204                    predicates: &predicates,
20205                    ..ParserRuntimeOptions::default()
20206                },
20207            )
20208            .expect("SLL prediction should select the first viable alternative");
20209
20210        assert_eq!(parser.node(tree).text(), "x<EOF>");
20211        assert!(deferred_actions.is_empty());
20212        assert_eq!(parser.number_of_syntax_errors(), 0);
20213        assert!(
20214            diagnostics
20215                .lock()
20216                .expect("recorded diagnostics lock")
20217                .is_empty(),
20218            "SLL mode must not retry with full context or report LL diagnostics"
20219        );
20220    }
20221
20222    #[test]
20223    fn committed_walker_filters_diagnostics_after_semantic_selection() {
20224        let atn = predicate_gated_same_lookahead_atn([0, 1]);
20225        let predicates = [
20226            (0, 0, ParserPredicate::False),
20227            (0, 1, ParserPredicate::True),
20228        ];
20229        let diagnostics = Arc::new(Mutex::new(Vec::new()));
20230        let mut parser = mini_parser(vec![
20231            TestToken::new(1).with_text("x"),
20232            TestToken::eof("parser-test", 1, 1, 1),
20233        ]);
20234        parser.set_prediction_mode(PredictionMode::LlExactAmbigDetection);
20235        parser.set_report_diagnostic_errors(true);
20236        parser.remove_error_listeners();
20237        parser.add_error_listener(RecordingErrorListener {
20238            diagnostics: Arc::clone(&diagnostics),
20239        });
20240
20241        let (tree, _) = parser
20242            .parse_atn_rule_with_runtime_options(
20243                &atn,
20244                0,
20245                ParserRuntimeOptions {
20246                    action_indices: &[(usize::MAX, 0)],
20247                    track_alt_numbers: true,
20248                    predicates: &predicates,
20249                    ..ParserRuntimeOptions::default()
20250                },
20251            )
20252            .expect("the true predicate should make the second alternative unique");
20253
20254        let root = parser.node(tree).as_rule().expect("entry result is a rule");
20255        assert_eq!(root.alt_number(), 2);
20256        assert!(
20257            diagnostics
20258                .lock()
20259                .expect("recorded diagnostics lock")
20260                .is_empty(),
20261            "predicate filtering made the decision unambiguous"
20262        );
20263    }
20264
20265    #[test]
20266    fn committed_walker_skips_diagnostic_only_predicates_when_reporting_is_disabled() {
20267        let atn = predicate_gated_same_lookahead_atn([0, 1]);
20268        let mut parser = mini_parser_with_hooks(
20269            vec![
20270                TestToken::new(1).with_text("x"),
20271                TestToken::eof("parser-test", 1, 1, 1),
20272            ],
20273            RecordingHooks::default(),
20274        );
20275        parser.set_prediction_mode(PredictionMode::LlExactAmbigDetection);
20276
20277        let (tree, _) = parser
20278            .parse_atn_rule_with_runtime_options(
20279                &atn,
20280                0,
20281                ParserRuntimeOptions {
20282                    action_indices: &[(usize::MAX, 0)],
20283                    track_alt_numbers: true,
20284                    ..ParserRuntimeOptions::default()
20285                },
20286            )
20287            .expect("the first predicate-bearing alternative should parse");
20288
20289        let root = parser.node(tree).as_rule().expect("entry result is a rule");
20290        assert_eq!(root.alt_number(), 1);
20291        assert_eq!(
20292            parser.semantic_hooks.predicates,
20293            [
20294                (0, 0, 0, Some("x".to_owned())),
20295                (0, 0, 0, Some("x".to_owned())),
20296            ],
20297            "diagnostic-only alternatives must not invoke semantic hooks"
20298        );
20299    }
20300
20301    #[test]
20302    fn committed_walker_falls_back_only_to_simulator_viable_alternatives() {
20303        let atn = semantic_fallback_viability_atn();
20304        let predicates = [
20305            (0, 0, ParserPredicate::False),
20306            (0, 1, ParserPredicate::True),
20307        ];
20308        let mut parser = mini_parser(vec![
20309            TestToken::new(1).with_text("a"),
20310            TestToken::new(3).with_text("c"),
20311            TestToken::eof("parser-test", 2, 1, 2),
20312        ]);
20313
20314        let (tree, deferred_actions) = parser
20315            .parse_atn_rule_with_runtime_options(
20316                &atn,
20317                0,
20318                ParserRuntimeOptions {
20319                    action_indices: &[(usize::MAX, 0)],
20320                    track_alt_numbers: true,
20321                    predicates: &predicates,
20322                    ..ParserRuntimeOptions::default()
20323                },
20324            )
20325            .expect("the true A C alternative should survive semantic fallback");
20326
20327        let root = parser.node(tree).as_rule().expect("entry result is a rule");
20328        assert_eq!(root.alt_number(), 3);
20329        assert_eq!(root.text(), "ac<EOF>");
20330        assert!(deferred_actions.is_empty());
20331        assert_eq!(parser.number_of_syntax_errors(), 0);
20332    }
20333
20334    #[test]
20335    fn committed_walker_evaluates_predicates_reached_through_rule_calls() {
20336        let atn = rule_call_predicate_decision_atn();
20337        let predicates = [(1, 0, ParserPredicate::False)];
20338        let mut parser = mini_parser(vec![
20339            TestToken::new(1).with_text("a"),
20340            TestToken::eof("parser-test", 1, 1, 1),
20341        ]);
20342
20343        let (tree, deferred_actions) = parser
20344            .parse_atn_rule_with_runtime_options(
20345                &atn,
20346                0,
20347                ParserRuntimeOptions {
20348                    action_indices: &[(usize::MAX, 0)],
20349                    track_alt_numbers: true,
20350                    predicates: &predicates,
20351                    ..ParserRuntimeOptions::default()
20352                },
20353            )
20354            .expect("the direct caller alternative should survive the false callee predicate");
20355
20356        let root = parser.node(tree).as_rule().expect("entry result is a rule");
20357        assert_eq!(root.alt_number(), 2);
20358        assert_eq!(root.text(), "a<EOF>");
20359        assert_eq!(root.child_rules(1).count(), 0);
20360        assert!(deferred_actions.is_empty());
20361        assert_eq!(parser.number_of_syntax_errors(), 0);
20362    }
20363
20364    #[test]
20365    fn committed_walker_uses_callee_argument_for_prediction_predicates() {
20366        let atn = rule_call_predicate_decision_atn();
20367        let predicates = [(1, 0, ParserPredicate::LocalIntEquals { value: 1 })];
20368        let rule_args = [ParserRuleArg {
20369            source_state: 2,
20370            rule_index: 1,
20371            value: 2,
20372            inherit_local: false,
20373        }];
20374        let mut parser = mini_parser(vec![
20375            TestToken::new(1).with_text("a"),
20376            TestToken::eof("parser-test", 1, 1, 1),
20377        ]);
20378
20379        let (tree, _) = parser
20380            .parse_atn_rule_with_runtime_options(
20381                &atn,
20382                0,
20383                ParserRuntimeOptions {
20384                    action_indices: &[(usize::MAX, 0)],
20385                    track_alt_numbers: true,
20386                    predicates: &predicates,
20387                    rule_args: &rule_args,
20388                    ..ParserRuntimeOptions::default()
20389                },
20390            )
20391            .expect("the direct alternative should survive the false callee predicate");
20392
20393        let root = parser.node(tree).as_rule().expect("entry result is a rule");
20394        assert_eq!(root.alt_number(), 2);
20395        assert_eq!(root.child_rules(1).count(), 0);
20396        assert_eq!(parser.number_of_syntax_errors(), 0);
20397    }
20398
20399    #[test]
20400    fn committed_predicate_star_loop_uses_single_token_deletion() {
20401        let atn = predicate_gated_star_loop_atn();
20402        let predicates = [(0, 0, ParserPredicate::True)];
20403        let diagnostics = Arc::new(Mutex::new(Vec::new()));
20404        let mut parser = mini_parser(vec![
20405            TestToken::new(2).with_text("x"),
20406            TestToken::new(1).with_text("a"),
20407            TestToken::eof("parser-test", 2, 1, 2),
20408        ]);
20409        parser.remove_error_listeners();
20410        parser.add_error_listener(RecordingErrorListener {
20411            diagnostics: Arc::clone(&diagnostics),
20412        });
20413
20414        let (tree, deferred_actions) = parser
20415            .parse_atn_rule_with_runtime_options(
20416                &atn,
20417                0,
20418                ParserRuntimeOptions {
20419                    action_indices: &[(usize::MAX, 0)],
20420                    predicates: &predicates,
20421                    ..ParserRuntimeOptions::default()
20422                },
20423            )
20424            .expect("the loop decision should delete the extraneous token and continue");
20425
20426        assert_eq!(parser.node(tree).text(), "xa<EOF>");
20427        assert!(deferred_actions.is_empty());
20428        assert_eq!(parser.number_of_syntax_errors(), 1);
20429        insta::assert_debug_snapshot!(
20430            "committed_predicate_star_loop_uses_single_token_deletion",
20431            *diagnostics.lock().expect("recorded diagnostics lock")
20432        );
20433    }
20434
20435    #[test]
20436    fn committed_walker_applies_legacy_and_semir_actions_before_indexed_hooks() {
20437        let atn = committed_action_then_predicate_atn();
20438        let member_actions = [ParserMemberAction {
20439            source_state: 0,
20440            member: 0,
20441            delta: 2,
20442        }];
20443        let return_actions = [ParserReturnAction {
20444            source_state: 0,
20445            rule_index: 0,
20446            name: "legacy",
20447            value: 3,
20448        }];
20449        let predicates = [(
20450            0,
20451            0,
20452            ParserPredicate::MemberEquals {
20453                member: 0,
20454                value: 7,
20455                equals: true,
20456            },
20457        )];
20458        let mut ir = SemIr::new();
20459        let semantic_member = ParserMemberAction {
20460            source_state: 0,
20461            member: 0,
20462            delta: 5,
20463        }
20464        .lower_into_semir(&mut ir);
20465        let semantic_return = ParserReturnAction {
20466            source_state: 0,
20467            rule_index: 0,
20468            name: "semantic",
20469            value: 11,
20470        }
20471        .lower_into_semir(&mut ir);
20472        let semantics = ParserSemantics {
20473            ir,
20474            predicates: Vec::new(),
20475            actions: vec![semantic_member, semantic_return],
20476        };
20477        let mut parser = mini_parser_with_hooks(
20478            vec![
20479                TestToken::new(1).with_text("x"),
20480                TestToken::eof("parser-test", 1, 1, 1),
20481            ],
20482            StatefulActionHooks::default(),
20483        );
20484
20485        let (tree, deferred_actions) = parser
20486            .parse_atn_rule_with_runtime_options(
20487                &atn,
20488                0,
20489                ParserRuntimeOptions {
20490                    action_indices: &[(0, 7)],
20491                    predicates: &predicates,
20492                    semantics: Some(&semantics),
20493                    member_actions: &member_actions,
20494                    return_actions: &return_actions,
20495                    ..ParserRuntimeOptions::default()
20496                },
20497            )
20498            .expect("the predicate should observe both committed member actions");
20499
20500        let root = parser.node(tree).as_rule().expect("entry result is a rule");
20501        assert_eq!(root.text(), "x<EOF>");
20502        assert_eq!(root.int_return("legacy"), Some(3));
20503        assert_eq!(root.int_return("semantic"), Some(11));
20504        assert_eq!(parser.int_member(0), Some(7));
20505        assert!(deferred_actions.is_empty());
20506        assert_eq!(parser.semantic_hooks.events, ["action:7"]);
20507        assert_eq!(parser.number_of_syntax_errors(), 0);
20508    }
20509
20510    #[test]
20511    fn committed_walker_runs_action_once_per_star_loop_iteration() {
20512        let atn = committed_action_star_loop_atn();
20513        let mut parser = mini_parser_with_hooks(
20514            vec![
20515                TestToken::new(1).with_text("a"),
20516                TestToken::new(1).with_text("b"),
20517                TestToken::eof("parser-test", 2, 1, 2),
20518            ],
20519            StatefulActionHooks::default(),
20520        );
20521
20522        let (tree, deferred_actions) = parser
20523            .parse_atn_rule_with_runtime_options(
20524                &atn,
20525                0,
20526                ParserRuntimeOptions {
20527                    action_indices: &[(2, 3)],
20528                    ..ParserRuntimeOptions::default()
20529                },
20530            )
20531            .expect("the committed star loop should parse");
20532
20533        assert_eq!(parser.node(tree).text(), "ab<EOF>");
20534        assert!(deferred_actions.is_empty());
20535        assert_eq!(parser.semantic_hooks.events, ["action:3", "action:3"]);
20536    }
20537
20538    #[test]
20539    fn committed_walker_has_no_total_step_cap() {
20540        const TOKEN_COUNT: usize = RECOGNITION_DEPTH_LIMIT + 1;
20541        let atn = committed_action_star_loop_atn();
20542        let mut parser = mini_parser(repeated_x_tokens(TOKEN_COUNT));
20543        parser.set_build_parse_trees(false);
20544
20545        parser
20546            .parse_atn_rule_with_runtime_options(
20547                &atn,
20548                0,
20549                ParserRuntimeOptions {
20550                    action_indices: &[(usize::MAX, 0)],
20551                    ..ParserRuntimeOptions::default()
20552                },
20553            )
20554            .expect("valid committed loops must not have a total-work cap");
20555
20556        assert_eq!(parser.input.index(), TOKEN_COUNT);
20557        assert_eq!(parser.number_of_syntax_errors(), 0);
20558    }
20559
20560    #[test]
20561    fn committed_walker_rejects_non_consuming_cycles() {
20562        let atn = committed_non_consuming_cycle_atn();
20563        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
20564        parser.set_bail_on_error(true);
20565
20566        let error = parser
20567            .parse_atn_rule_with_runtime_options(
20568                &atn,
20569                0,
20570                ParserRuntimeOptions {
20571                    action_indices: &[(usize::MAX, 0)],
20572                    ..ParserRuntimeOptions::default()
20573                },
20574            )
20575            .expect_err("a non-consuming cycle must not spin forever");
20576
20577        assert!(
20578            error.to_string().contains("non-consuming ATN cycle"),
20579            "unexpected error: {error}"
20580        );
20581    }
20582
20583    #[test]
20584    fn deeply_nested_committed_rule_calls_grow_the_stack() {
20585        const DEPTH: usize = 4_096;
20586        const STACK_SIZE: usize = 256 * 1024;
20587        let atn = nested_rule_chain_atn(DEPTH);
20588        std::thread::Builder::new()
20589            .name("nested-committed-rules".to_owned())
20590            .stack_size(STACK_SIZE)
20591            .spawn(move || {
20592                let mut parser = mini_parser(vec![TestToken::new(1).with_text("x")]);
20593                parser.set_build_parse_trees(false);
20594                parser
20595                    .parse_atn_rule_with_runtime_options(
20596                        &atn,
20597                        0,
20598                        ParserRuntimeOptions {
20599                            action_indices: &[(usize::MAX, 0)],
20600                            ..ParserRuntimeOptions::default()
20601                        },
20602                    )
20603                    .expect("nested committed rules should grow the native stack");
20604                assert_eq!(parser.input.index(), 1);
20605            })
20606            .expect("small-stack thread should start")
20607            .join()
20608            .expect("nested committed rules should not overflow their stack");
20609    }
20610
20611    #[test]
20612    fn committed_walker_runs_action_once_per_left_recursive_operator() {
20613        let atn = committed_action_left_recursive_atn();
20614        let mut parser = mini_parser_with_hooks(
20615            vec![
20616                TestToken::new(1).with_text("a"),
20617                TestToken::new(3).with_text("+"),
20618                TestToken::new(1).with_text("b"),
20619                TestToken::new(3).with_text("+"),
20620                TestToken::new(1).with_text("c"),
20621                TestToken::eof("parser-test", 5, 1, 5),
20622            ],
20623            StatefulActionHooks::default(),
20624        );
20625
20626        let (tree, deferred_actions) = parser
20627            .parse_atn_rule_with_runtime_options(
20628                &atn,
20629                0,
20630                ParserRuntimeOptions {
20631                    action_indices: &[(6, 11)],
20632                    ..ParserRuntimeOptions::default()
20633                },
20634            )
20635            .expect("the committed left-recursive rule should parse");
20636
20637        assert_eq!(parser.node(tree).text(), "a+b+c");
20638        assert!(deferred_actions.is_empty());
20639        assert_eq!(parser.semantic_hooks.events, ["action:11", "action:11"]);
20640    }
20641
20642    #[test]
20643    fn committed_left_recursive_depth_cap_keeps_listener_events_balanced() {
20644        let atn = committed_action_left_recursive_atn();
20645        let events = Arc::new(Mutex::new(Vec::new()));
20646        let mut parser = mini_parser(vec![
20647            TestToken::new(1).with_text("a"),
20648            TestToken::new(3).with_text("+"),
20649            TestToken::new(1).with_text("b"),
20650            TestToken::eof("parser-test", 3, 1, 3),
20651        ]);
20652        parser.set_max_rule_depth(Some(1));
20653        parser.add_parse_listener(RecordingParseListener {
20654            events: Arc::clone(&events),
20655        });
20656
20657        let error = parser
20658            .parse_atn_rule_with_runtime_options(
20659                &atn,
20660                0,
20661                ParserRuntimeOptions {
20662                    action_indices: &[(6, 11)],
20663                    ..ParserRuntimeOptions::default()
20664                },
20665            )
20666            .expect_err("the left-recursive expansion should exceed the depth cap");
20667
20668        insta::assert_debug_snapshot!(
20669            "committed_left_recursive_depth_cap_keeps_listener_events_balanced",
20670            (
20671                error.to_string(),
20672                events.lock().expect("parse-listener event lock").as_slice(),
20673            )
20674        );
20675    }
20676
20677    #[test]
20678    fn committed_walker_preserves_nested_rule_listener_events() {
20679        let atn = ordinary_star_loop_atn();
20680        let events = Arc::new(Mutex::new(Vec::new()));
20681        let mut parser = mini_parser(vec![
20682            TestToken::new(1).with_text("a"),
20683            TestToken::new(1).with_text("b"),
20684            TestToken::eof("parser-test", 2, 1, 2),
20685        ]);
20686        parser.add_parse_listener(RecordingParseListener {
20687            events: Arc::clone(&events),
20688        });
20689
20690        let (tree, _) = parser
20691            .parse_atn_rule_with_runtime_options(
20692                &atn,
20693                0,
20694                ParserRuntimeOptions {
20695                    action_indices: &[(usize::MAX, 0)],
20696                    ..ParserRuntimeOptions::default()
20697                },
20698            )
20699            .expect("the committed nested-rule path should parse");
20700
20701        assert_eq!(parser.node(tree).text(), "ab<EOF>");
20702        assert_eq!(
20703            *events.lock().expect("parse-listener event lock"),
20704            [
20705                "enter:0", "enter:1", "exit:1", "enter:1", "exit:1", "exit:0",
20706            ]
20707        );
20708    }
20709
20710    #[test]
20711    fn committed_walker_enforces_rule_depth_cap() {
20712        let atn = ordinary_star_loop_atn();
20713        let mut parser = mini_parser(vec![
20714            TestToken::new(1).with_text("a"),
20715            TestToken::eof("parser-test", 1, 1, 1),
20716        ]);
20717        parser.set_max_rule_depth(Some(1));
20718
20719        let error = parser
20720            .parse_atn_rule_with_runtime_options(
20721                &atn,
20722                0,
20723                ParserRuntimeOptions {
20724                    action_indices: &[(usize::MAX, 0)],
20725                    ..ParserRuntimeOptions::default()
20726                },
20727            )
20728            .expect_err("the nested rule should exceed the committed-path cap");
20729
20730        assert!(
20731            error
20732                .to_string()
20733                .contains("rule nesting depth limit of 1 exceeded"),
20734            "unexpected error: {error}"
20735        );
20736    }
20737
20738    #[test]
20739    fn committed_abort_precedes_and_clears_unhandled_action_error() {
20740        let atn = action_then_nested_rule_atn();
20741        let mut parser = mini_parser_with_hooks(
20742            vec![TestToken::eof("parser-test", 0, 1, 0)],
20743            DecliningActionHooks::default(),
20744        );
20745        parser.set_max_rule_depth(Some(1));
20746
20747        let error = parser
20748            .parse_atn_rule_with_runtime_options(
20749                &atn,
20750                0,
20751                ParserRuntimeOptions {
20752                    action_indices: &[(0, 7)],
20753                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
20754                    ..ParserRuntimeOptions::default()
20755                },
20756            )
20757            .expect_err("the recovered child abort must outrank the earlier action miss");
20758
20759        assert_eq!(parser.semantic_hooks.actions, [0]);
20760        assert!(
20761            error
20762                .to_string()
20763                .contains("rule nesting depth limit of 1 exceeded"),
20764            "unexpected error: {error}"
20765        );
20766        assert!(
20767            parser.take_parse_abort().is_none(),
20768            "the returned abort must not remain sticky"
20769        );
20770        assert!(
20771            parser.take_unknown_semantic_error().is_none(),
20772            "the masked action miss must not poison parser reuse"
20773        );
20774    }
20775
20776    #[test]
20777    fn top_level_committed_semantic_error_does_not_poison_reuse() {
20778        let atn = committed_action_then_predicate_atn();
20779        let predicates = [(0, 0, ParserPredicate::True)];
20780        let mut parser = mini_parser_with_hooks(
20781            vec![
20782                TestToken::new(1).with_text("x"),
20783                TestToken::eof("parser-test", 1, 1, 1),
20784            ],
20785            DecliningActionHooks::default(),
20786        );
20787
20788        let error = parser
20789            .parse_atn_rule_with_runtime_options(
20790                &atn,
20791                0,
20792                ParserRuntimeOptions {
20793                    action_indices: &[(0, 7)],
20794                    predicates: &predicates,
20795                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
20796                    ..ParserRuntimeOptions::default()
20797                },
20798            )
20799            .expect_err("the declined committed action must fail loud");
20800        assert!(
20801            error.to_string().contains("unhandled semantic action"),
20802            "unexpected error: {error}"
20803        );
20804
20805        parser.input.seek(0);
20806        let (tree, _) = parser
20807            .parse_atn_rule_with_runtime_options(
20808                &atn,
20809                0,
20810                ParserRuntimeOptions {
20811                    predicates: &predicates,
20812                    ..ParserRuntimeOptions::default()
20813                },
20814            )
20815            .expect("a clean interpreted reuse must not observe the prior action miss");
20816
20817        assert_eq!(parser.node(tree).text(), "x<EOF>");
20818        assert!(
20819            parser.take_unknown_semantic_error().is_none(),
20820            "the returned top-level semantic error must drain its recorded hit"
20821        );
20822    }
20823
20824    #[test]
20825    fn committed_walker_runs_handled_rule_init_before_indexed_action() {
20826        let atn = committed_action_then_predicate_atn();
20827        let mut parser = mini_parser_with_hooks(
20828            vec![
20829                TestToken::new(1).with_text("x"),
20830                TestToken::eof("parser-test", 1, 1, 1),
20831            ],
20832            InitOrderingHooks::default(),
20833        );
20834
20835        let (_, deferred_actions) = parser
20836            .parse_atn_rule_with_runtime_options(
20837                &atn,
20838                0,
20839                ParserRuntimeOptions {
20840                    init_action_rules: &[0],
20841                    action_indices: &[(0, 7)],
20842                    ..ParserRuntimeOptions::default()
20843                },
20844            )
20845            .expect("the named action should observe rule-init state");
20846
20847        assert!(deferred_actions.is_empty());
20848        assert_eq!(
20849            parser.semantic_hooks.events,
20850            ["init", "action:7:initialized=true", "predicate:true",]
20851        );
20852    }
20853
20854    #[test]
20855    fn committed_walker_defers_unhandled_rule_init_for_legacy_replay() {
20856        let atn = token_then_eof_atn();
20857        let mut parser = mini_parser(vec![
20858            TestToken::new(1).with_text("x"),
20859            TestToken::eof("parser-test", 1, 1, 1),
20860        ]);
20861
20862        let (_, deferred_actions) = parser
20863            .parse_atn_rule_with_runtime_options(
20864                &atn,
20865                0,
20866                ParserRuntimeOptions {
20867                    init_action_rules: &[0],
20868                    action_indices: &[(usize::MAX, 0)],
20869                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
20870                    ..ParserRuntimeOptions::default()
20871                },
20872            )
20873            .expect("a declined init should remain available for legacy replay");
20874
20875        assert_eq!(
20876            deferred_actions,
20877            [ParserAction::new_rule_init(0, 0, Some(0))]
20878        );
20879    }
20880
20881    #[test]
20882    fn committed_walker_dispatches_recovery_diagnostics() {
20883        let atn = noop_action_then_token_then_eof_atn();
20884        let diagnostics = Arc::new(Mutex::new(Vec::new()));
20885        let mut parser = mini_parser_with_hooks(
20886            vec![
20887                TestToken::new(1).with_text("x"),
20888                TestToken::new(2).with_text("y"),
20889                TestToken::eof("parser-test", 2, 1, 2),
20890            ],
20891            StatefulActionHooks::default(),
20892        );
20893        parser.remove_error_listeners();
20894        parser.add_error_listener(RecordingErrorListener {
20895            diagnostics: Arc::clone(&diagnostics),
20896        });
20897
20898        let (tree, _) = parser
20899            .parse_atn_rule_with_runtime_options(
20900                &atn,
20901                0,
20902                ParserRuntimeOptions {
20903                    action_indices: &[(0, 5)],
20904                    ..ParserRuntimeOptions::default()
20905                },
20906            )
20907            .expect("the committed rule should recover");
20908
20909        assert_eq!(parser.node(tree).text(), "xy<EOF>");
20910        assert_eq!(parser.number_of_syntax_errors(), 1);
20911        insta::assert_debug_snapshot!(
20912            "committed_walker_dispatches_recovery_diagnostics",
20913            *diagnostics.lock().expect("recorded diagnostics lock")
20914        );
20915    }
20916
20917    #[test]
20918    fn committed_bail_error_notifies_error_listener() {
20919        let atn = noop_action_then_token_then_eof_atn();
20920        let diagnostics = Arc::new(Mutex::new(Vec::new()));
20921        let mut parser = mini_parser(vec![
20922            TestToken::new(2)
20923                .with_text("y")
20924                .with_span(0, 0)
20925                .with_byte_span(0, 1)
20926                .with_position(3, 5),
20927            TestToken::eof("parser-test", 1, 1, 1),
20928        ]);
20929        parser.set_bail_on_error(true);
20930        parser.remove_error_listeners();
20931        parser.add_error_listener(RecordingErrorListener {
20932            diagnostics: Arc::clone(&diagnostics),
20933        });
20934
20935        let error = parser
20936            .parse_atn_rule_with_runtime_options(
20937                &atn,
20938                0,
20939                ParserRuntimeOptions {
20940                    action_indices: &[(0, 5)],
20941                    ..ParserRuntimeOptions::default()
20942                },
20943            )
20944            .expect_err("bail mode must return the committed token mismatch");
20945        let diagnostics = diagnostics
20946            .lock()
20947            .expect("recorded diagnostics lock")
20948            .clone();
20949
20950        insta::assert_debug_snapshot!(
20951            "committed_bail_error_notifies_error_listener",
20952            (error, diagnostics)
20953        );
20954    }
20955
20956    #[test]
20957    fn semantic_hook_handles_committed_parser_action() {
20958        let atn = token_then_eof_atn();
20959        let mut parser = mini_parser_with_hooks(
20960            vec![
20961                TestToken::new(1).with_text("x"),
20962                TestToken::eof("parser-test", 1, 1, 1),
20963            ],
20964            RecordingHooks::default(),
20965        );
20966        let (tree, _) = parser
20967            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
20968            .expect("rule parses before action hook is tested");
20969
20970        assert!(parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
20971        assert_eq!(
20972            parser.semantic_hooks.actions,
20973            vec![(42, "x".to_owned(), Some("s".to_owned()))]
20974        );
20975        assert_eq!(
20976            parser.semantic_hooks.action_trees,
20977            [Some("x<EOF>".to_owned())]
20978        );
20979    }
20980
20981    #[test]
20982    fn unhandled_committed_action_fails_loud_under_error_policy() {
20983        // An action offered to the hook that no hook handles (returns false)
20984        // must be recorded and surfaced as `AntlrError::Unsupported` under the
20985        // Error policy, so a `hook`-disposed action is not silently dropped.
20986        let mut parser = mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
20987        parser.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
20988        let tree = parser.rule_node(ParserRuleContext::new(0, -1));
20989
20990        // DecliningHooks::action returns false (unhandled).
20991        assert!(!parser.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
20992
20993        let error = parser
20994            .take_unknown_semantic_error()
20995            .expect("an unhandled committed action under Error policy must fail loud");
20996        let AntlrError::Unsupported(message) = error else {
20997            panic!("expected AntlrError::Unsupported, got {error:?}");
20998        };
20999        assert!(
21000            message.contains("unhandled semantic action") && message.contains("state=42"),
21001            "message should name the dropped action coordinate: {message}"
21002        );
21003
21004        // Under the default (assume-true) policy the same miss is not recorded.
21005        let mut lenient =
21006            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
21007        let tree = lenient.rule_node(ParserRuleContext::new(0, -1));
21008        assert!(!lenient.parser_action_hook(ParserAction::new(42, 0, 0, Some(0)), tree));
21009        assert!(lenient.take_unknown_semantic_error().is_none());
21010    }
21011
21012    #[test]
21013    fn translated_predicate_is_unaffected_by_error_policy() {
21014        let atn = predicate_after_token_atn();
21015        let mut parser = mini_parser(vec![
21016            TestToken::new(1).with_text("x"),
21017            TestToken::new(2).with_text("y"),
21018            TestToken::eof("parser-test", 2, 1, 2),
21019        ]);
21020
21021        let (tree, _) = parser
21022            .parse_atn_rule_with_runtime_options(
21023                &atn,
21024                0,
21025                ParserRuntimeOptions {
21026                    predicates: &[(0, 0, ParserPredicate::True)],
21027                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
21028                    ..ParserRuntimeOptions::default()
21029                },
21030            )
21031            .expect("a predicate covered by the table is not an unknown coordinate");
21032
21033        assert_eq!(parser.node(tree).text(), "xy");
21034    }
21035
21036    /// Stack-valued member statements must execute on the parser's speculative
21037    /// replay path, not just the lexer's committed one (issue #206). This drives
21038    /// `apply_member_actions` -> `ParserTableSemCtx` -> `MemberEnv` directly,
21039    /// which is the path a generated parser's `@members` stack state takes.
21040    #[test]
21041    fn parser_speculative_replay_threads_stack_member_state() {
21042        let mut ir = SemIr::new();
21043        let one = ir.expr(PExpr::Int(1));
21044        let push = ir.stmt(AStmt::PushMember(0, one));
21045        let pop = ir.stmt(AStmt::PopMember(0));
21046        let semantics = ParserSemantics {
21047            ir,
21048            predicates: Vec::new(),
21049            actions: vec![
21050                ParserSemanticAction {
21051                    source_state: 1,
21052                    rule_index: usize::MAX,
21053                    stmt: push,
21054                    speculative: true,
21055                },
21056                ParserSemanticAction {
21057                    source_state: 2,
21058                    rule_index: usize::MAX,
21059                    stmt: pop,
21060                    speculative: true,
21061                },
21062            ],
21063        };
21064
21065        // Replaying the push state must be visible to a later read...
21066        let pushed = member_values_after_action(1, &[], Some(&semantics), &MemberEnv::new());
21067        assert_eq!(pushed.stack_top(0), Some(1));
21068        assert_eq!(pushed.stack_len(0), 1);
21069
21070        // ...and must not mutate the caller's env: speculative paths are
21071        // path-local, so an abandoned branch cannot leak state to its sibling.
21072        assert_eq!(MemberEnv::new().stack_len(0), 0);
21073
21074        // Replaying the pop state restores the empty, canonical env, so the
21075        // resulting memo key matches an equivalent untouched path.
21076        let popped = member_values_after_action(2, &[], Some(&semantics), &pushed);
21077        assert_eq!(popped.stack_top(0), None);
21078        assert_eq!(popped, MemberEnv::new(), "emptied stack must canonicalize");
21079
21080        // An unbalanced pop is a defined no-op rather than a panic.
21081        let underflowed = member_values_after_action(2, &[], Some(&semantics), &MemberEnv::new());
21082        assert_eq!(underflowed, MemberEnv::new());
21083    }
21084
21085    /// Hooks that decline (`None`) must fall through to the configured policy
21086    /// even when the coordinate carries a [`semir`] `Hook` node, matching the
21087    /// legacy table path. Regression for the `unwrap_or(false)` that silently
21088    /// rejected declined hook nodes and bypassed [`UnknownSemanticPolicy`].
21089    fn hook_predicate_semantics() -> ParserSemantics {
21090        let mut ir = SemIr::new();
21091        let expr = ir.expr(PExpr::Hook(HookId::new(0)));
21092        ParserSemantics {
21093            ir,
21094            predicates: vec![ParserSemanticPredicate {
21095                rule_index: 0,
21096                pred_index: 0,
21097                expr,
21098                failure_message: None,
21099            }],
21100            actions: Vec::new(),
21101        }
21102    }
21103
21104    #[derive(Debug, Default)]
21105    struct DecliningHooks;
21106
21107    impl SemanticHooks for DecliningHooks {}
21108
21109    #[test]
21110    fn semir_hook_none_falls_through_to_assume_true() {
21111        let atn = predicate_after_token_atn();
21112        let semantics = hook_predicate_semantics();
21113        let mut parser = mini_parser_with_hooks(
21114            vec![
21115                TestToken::new(1).with_text("x"),
21116                TestToken::new(2).with_text("y"),
21117                TestToken::eof("parser-test", 2, 1, 2),
21118            ],
21119            DecliningHooks,
21120        );
21121
21122        let (tree, _) = parser
21123            .parse_atn_rule_with_runtime_options(
21124                &atn,
21125                0,
21126                ParserRuntimeOptions {
21127                    semantics: Some(&semantics),
21128                    unknown_predicate_policy: UnknownSemanticPolicy::AssumeTrue,
21129                    ..ParserRuntimeOptions::default()
21130                },
21131            )
21132            .expect("a declined SemIR hook must pass under assume-true");
21133
21134        assert_eq!(parser.node(tree).text(), "xy");
21135    }
21136
21137    #[test]
21138    fn semir_hook_none_falls_through_to_assume_false() {
21139        let atn = predicate_after_token_atn();
21140        let semantics = hook_predicate_semantics();
21141        let mut parser = mini_parser_with_hooks(
21142            vec![
21143                TestToken::new(1).with_text("x"),
21144                TestToken::new(2).with_text("y"),
21145                TestToken::eof("parser-test", 2, 1, 2),
21146            ],
21147            DecliningHooks,
21148        );
21149
21150        let result = parser.parse_atn_rule_with_runtime_options(
21151            &atn,
21152            0,
21153            ParserRuntimeOptions {
21154                semantics: Some(&semantics),
21155                unknown_predicate_policy: UnknownSemanticPolicy::AssumeFalse,
21156                ..ParserRuntimeOptions::default()
21157            },
21158        );
21159
21160        assert!(
21161            result.is_err(),
21162            "a declined SemIR hook must fail the only guarded path under assume-false"
21163        );
21164    }
21165
21166    #[test]
21167    fn semir_hook_none_records_coordinate_under_error_policy() {
21168        let atn = predicate_after_token_atn();
21169        let semantics = hook_predicate_semantics();
21170        let mut parser = mini_parser_with_hooks(
21171            vec![
21172                TestToken::new(1).with_text("x"),
21173                TestToken::new(2).with_text("y"),
21174                TestToken::eof("parser-test", 2, 1, 2),
21175            ],
21176            DecliningHooks,
21177        );
21178
21179        let error = parser
21180            .parse_atn_rule_with_runtime_options(
21181                &atn,
21182                0,
21183                ParserRuntimeOptions {
21184                    semantics: Some(&semantics),
21185                    unknown_predicate_policy: UnknownSemanticPolicy::Error,
21186                    ..ParserRuntimeOptions::default()
21187                },
21188            )
21189            .expect_err("a declined SemIR hook under Error policy must fail the parse");
21190
21191        let AntlrError::Unsupported(message) = error else {
21192            panic!("expected AntlrError::Unsupported, got {error:?}");
21193        };
21194        assert!(
21195            message.contains("unsupported semantic predicate") && message.contains("pred_index=0"),
21196            "message should name the unresolved coordinate: {message}"
21197        );
21198    }
21199
21200    #[test]
21201    fn generated_direct_predicate_honors_installed_policy() {
21202        // The generated recursive-descent path calls
21203        // `parser_semantic_ir_predicate_matches_with_context_and_local` without
21204        // going through `ParserRuntimeOptions`, so the policy must be installed
21205        // via `set_unknown_predicate_policy` (as the generated constructor now
21206        // does). A declining hook must then honor it rather than the default.
21207        let semantics = hook_predicate_semantics();
21208        let context = ParserRuleContext::new(0, -1);
21209
21210        let mut assume_true =
21211            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
21212        assert!(
21213            assume_true.parser_semantic_ir_predicate_matches_with_context_and_local(
21214                &semantics, 0, 0, &context, 0
21215            ),
21216            "default AssumeTrue accepts a declined hook"
21217        );
21218        assert!(assume_true.take_unknown_semantic_error().is_none());
21219
21220        let mut error_policy =
21221            mini_parser_with_hooks(vec![TestToken::eof("t", 0, 1, 0)], DecliningHooks);
21222        error_policy.set_unknown_predicate_policy(UnknownSemanticPolicy::Error);
21223        assert!(
21224            !error_policy.parser_semantic_ir_predicate_matches_with_context_and_local(
21225                &semantics, 0, 0, &context, 0
21226            ),
21227            "Error policy rejects a declined hook on the generated-direct path"
21228        );
21229        let error = error_policy
21230            .take_unknown_semantic_error()
21231            .expect("Error policy records the unresolved coordinate for the generated path");
21232        let AntlrError::Unsupported(message) = error else {
21233            panic!("expected AntlrError::Unsupported, got {error:?}");
21234        };
21235        assert!(message.contains("pred_index=0"), "message: {message}");
21236    }
21237
21238    #[test]
21239    fn parser_rule_start_skips_leading_hidden_tokens() {
21240        let atn = token_then_eof_atn();
21241        let mut parser = mini_parser(vec![
21242            TestToken::new(99)
21243                .with_text(" ")
21244                .with_channel(HIDDEN_CHANNEL),
21245            TestToken::new(1).with_text("x"),
21246            TestToken::eof("parser-test", 2, 1, 2),
21247        ]);
21248
21249        let tree = parser
21250            .parse_atn_rule(&atn, 0)
21251            .expect("artificial parser rule should parse");
21252        let Some(rule) = parser.node(tree).first_rule(0).and_then(Node::as_rule) else {
21253            panic!("rule node should be present");
21254        };
21255        assert_eq!(
21256            rule.start()
21257                .expect("rule should have a start token")
21258                .token_type(),
21259            1
21260        );
21261    }
21262
21263    #[test]
21264    fn parser_action_after_eof_stops_at_eof_token() {
21265        let atn = eof_then_action_atn();
21266        let mut parser = mini_parser(vec![TestToken::eof("parser-test", 0, 1, 0)]);
21267
21268        let (_, actions) = parser
21269            .parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
21270            .expect("EOF action rule should parse");
21271
21272        assert_eq!(actions.len(), 1);
21273        assert_eq!(actions[0].stop_index(), Some(0));
21274        assert_eq!(
21275            parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
21276            ""
21277        );
21278    }
21279
21280    #[test]
21281    fn after_action_stop_uses_rule_context_stop_not_cursor() {
21282        // A rule that ends right before EOF without matching it (e.g. `a: ID;`
21283        // called from `start: a EOF;`): after matching ID the cursor parks on EOF,
21284        // but the rule did not consume it. The @after stop must follow the rule
21285        // context's recorded stop (ID at index 0), not the cursor's EOF (index 1).
21286        let mut id = TestToken::new(1).with_text("x");
21287        id.set_token_index(0);
21288        let mut eof = TestToken::eof("parser-test", 1, 1, 1);
21289        eof.set_token_index(1);
21290        let mut parser = mini_parser(vec![id.clone(), eof]);
21291        // Advance the cursor onto EOF, as it would be after `a` matched ID.
21292        parser.consume();
21293        assert_eq!(parser.la(1), TOKEN_EOF);
21294
21295        // Rule `a` matched only ID, so its context stop is the ID token (index 0),
21296        // exactly what finish_rule(consumed_eof = false) records.
21297        let mut ctx = ParserRuleContext::new(0, 0);
21298        parser.set_context_stop(
21299            &mut ctx,
21300            parser.token_id_at(0).expect("ID token should be buffered"),
21301        );
21302        let tree = parser.rule_node(ctx);
21303
21304        let current_index = parser.input.index();
21305        // Cursor-only inference would wrongly pick EOF (the parked cursor)...
21306        assert_eq!(parser.after_action_stop_index(current_index), Some(1));
21307        // ...but the tree-aware helper follows the rule context stop (ID).
21308        assert_eq!(
21309            parser.after_action_stop_index_for_tree(tree, current_index),
21310            Some(0)
21311        );
21312    }
21313
21314    #[test]
21315    fn after_action_start_uses_rule_context_start_not_cursor() {
21316        // A rule that begins after leading hidden-channel tokens: the rule context
21317        // start (set by `enter_rule`) is the first visible token, not the raw cursor
21318        // that may still point at the hidden prefix. The @after start must follow
21319        // the context start so `$start`/`$text` excludes the hidden prefix.
21320        let mut parser = mini_parser(vec![
21321            TestToken::new(9)
21322                .with_text(" ")
21323                .with_channel(HIDDEN_CHANNEL),
21324            TestToken::new(9)
21325                .with_text(" ")
21326                .with_channel(HIDDEN_CHANNEL),
21327            TestToken::new(1).with_text("x"),
21328            TestToken::eof("parser-test", 3, 1, 3),
21329        ]);
21330
21331        let mut ctx = ParserRuleContext::new(0, 0);
21332        parser.set_context_start(
21333            &mut ctx,
21334            parser.token_id_at(2).expect("ID token should be buffered"),
21335        );
21336        let tree = parser.rule_node(ctx);
21337
21338        // The raw fallback (pre-rule cursor) would be 0 (the hidden prefix)...
21339        // ...but the tree-aware helper follows the rule context start (index 2).
21340        assert_eq!(parser.after_action_start_index_for_tree(tree, 0), 2);
21341
21342        // With no rule start recorded, it falls back to the provided index.
21343        let empty = parser.rule_node(ParserRuleContext::new(0, 0));
21344        assert_eq!(parser.after_action_start_index_for_tree(empty, 7), 7);
21345    }
21346
21347    fn clean_fast_outcome(index: usize, consumed_eof: bool, marker: u32) -> FastRecognizeOutcome {
21348        FastRecognizeOutcome {
21349            index,
21350            consumed_eof,
21351            diagnostics: DiagnosticSeqId::EMPTY,
21352            deferred_nodes: FastDeferredNodeId::EMPTY,
21353            nodes: NodeSeqId(marker),
21354        }
21355    }
21356
21357    #[test]
21358    fn clean_fast_outcome_dedupe_scans_small_lists_inline() {
21359        let mut outcomes = vec![
21360            clean_fast_outcome(4, false, 0),
21361            clean_fast_outcome(2, false, 1),
21362            clean_fast_outcome(4, false, 2),
21363            clean_fast_outcome(4, true, 3),
21364            clean_fast_outcome(2, false, 4),
21365        ];
21366        let mut scratch = FastOutcomeDedupScratch::default();
21367
21368        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
21369
21370        assert_eq!(strategy, FastOutcomeDedupStrategy::Inline);
21371        assert_eq!(
21372            outcomes
21373                .iter()
21374                .map(|outcome| (outcome.index, outcome.consumed_eof, outcome.nodes.0))
21375                .collect::<Vec<_>>(),
21376            vec![(4, false, 0), (2, false, 1), (4, true, 3)]
21377        );
21378        assert!(scratch.dense_words.is_empty());
21379        assert!(scratch.sparse_keys.is_empty());
21380    }
21381
21382    #[test]
21383    fn clean_fast_outcome_dedupe_uses_and_reuses_dense_bitmap() {
21384        let mut scratch = FastOutcomeDedupScratch::default();
21385        let mut outcomes = (100..109)
21386            .flat_map(|index| {
21387                [
21388                    clean_fast_outcome(
21389                        index,
21390                        false,
21391                        u32::try_from(index).expect("test index fits in u32"),
21392                    ),
21393                    clean_fast_outcome(index, false, u32::MAX),
21394                ]
21395            })
21396            .collect();
21397
21398        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
21399
21400        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
21401        assert_eq!(outcomes.len(), 9);
21402        assert_eq!(outcomes[0].nodes, NodeSeqId(100));
21403        let dense_capacity = scratch.dense_words.capacity();
21404
21405        let mut reused = (1_000..1_009)
21406            .map(|index| {
21407                clean_fast_outcome(
21408                    index,
21409                    false,
21410                    u32::try_from(index).expect("test index fits in u32"),
21411                )
21412            })
21413            .collect();
21414        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
21415
21416        assert_eq!(strategy, FastOutcomeDedupStrategy::Dense);
21417        assert_eq!(reused.len(), 9);
21418        assert_eq!(scratch.dense_words.capacity(), dense_capacity);
21419    }
21420
21421    #[test]
21422    fn clean_fast_outcome_dedupe_uses_and_reuses_sparse_hash() {
21423        let mut scratch = FastOutcomeDedupScratch::default();
21424        let sparse_indexes = [
21425            0, 100_000, 200_000, 300_000, 400_000, 500_000, 600_000, 700_000, 800_000,
21426        ];
21427        let mut outcomes = sparse_indexes
21428            .into_iter()
21429            .chain([400_000])
21430            .enumerate()
21431            .map(|(marker, index)| {
21432                clean_fast_outcome(
21433                    index,
21434                    false,
21435                    u32::try_from(marker).expect("test marker fits in u32"),
21436                )
21437            })
21438            .collect();
21439
21440        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
21441
21442        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
21443        assert_eq!(outcomes.len(), sparse_indexes.len());
21444        assert_eq!(outcomes[4].nodes, NodeSeqId(4));
21445        let sparse_capacity = scratch.sparse_keys.capacity();
21446
21447        let mut reused = sparse_indexes
21448            .into_iter()
21449            .map(|index| {
21450                clean_fast_outcome(
21451                    index,
21452                    false,
21453                    u32::try_from(index).expect("test index fits in u32"),
21454                )
21455            })
21456            .collect();
21457        let strategy = dedupe_clean_fast_outcomes(&mut reused, &mut scratch);
21458
21459        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
21460        assert_eq!(reused.len(), sparse_indexes.len());
21461        assert_eq!(scratch.sparse_keys.capacity(), sparse_capacity);
21462    }
21463
21464    #[test]
21465    fn clean_fast_outcome_dedupe_releases_oversized_sparse_hash() {
21466        let mut scratch = FastOutcomeDedupScratch::default();
21467        scratch
21468            .sparse_keys
21469            .reserve(MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS * 2);
21470        assert!(scratch.sparse_keys.capacity() > MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
21471        let mut outcomes = (0..9)
21472            .map(|index| clean_fast_outcome(index * 100_000, false, index as u32))
21473            .collect();
21474
21475        let strategy = dedupe_clean_fast_outcomes(&mut outcomes, &mut scratch);
21476
21477        assert_eq!(strategy, FastOutcomeDedupStrategy::Sparse);
21478        assert!(scratch.sparse_keys.is_empty());
21479        assert!(scratch.sparse_keys.capacity() <= MAX_RETAINED_FAST_OUTCOME_SPARSE_KEYS);
21480    }
21481
21482    #[test]
21483    fn fast_outcome_selection_respects_sll_tie_order() {
21484        let mut arena = RecognitionArena::default();
21485        let first = FastRecognizeOutcome {
21486            index: 1,
21487            consumed_eof: false,
21488            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
21489                line: 1,
21490                column: 0,
21491                message: "mismatched input 'x'".to_owned(),
21492                offending: None,
21493            }]),
21494            deferred_nodes: FastDeferredNodeId::EMPTY,
21495            nodes: NodeSeqId::EMPTY,
21496        };
21497        let second = FastRecognizeOutcome {
21498            index: first.index,
21499            consumed_eof: first.consumed_eof,
21500            diagnostics: DiagnosticSeqId::EMPTY,
21501            deferred_nodes: FastDeferredNodeId::EMPTY,
21502            nodes: NodeSeqId::EMPTY,
21503        };
21504
21505        let selected = select_best_fast_outcome(
21506            [first, second].into_iter(),
21507            PredictionMode::Sll,
21508            None,
21509            |_| panic!("caller-follow token probe should not run"),
21510            &arena,
21511        )
21512        .expect("one outcome should be selected");
21513        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
21514        let eof_second = FastRecognizeOutcome {
21515            index: second.index,
21516            consumed_eof: true,
21517            diagnostics: DiagnosticSeqId::EMPTY,
21518            deferred_nodes: FastDeferredNodeId::EMPTY,
21519            nodes: NodeSeqId::EMPTY,
21520        };
21521        let selected = select_best_fast_outcome(
21522            [first, eof_second].into_iter(),
21523            PredictionMode::Sll,
21524            None,
21525            |_| panic!("caller-follow token probe should not run"),
21526            &arena,
21527        )
21528        .expect("one outcome should be selected");
21529        assert!(!selected.consumed_eof);
21530        let selected = select_best_fast_outcome(
21531            [first, second].into_iter(),
21532            PredictionMode::Ll,
21533            None,
21534            |_| panic!("caller-follow token probe should not run"),
21535            &arena,
21536        )
21537        .expect("one outcome should be selected");
21538        assert!(selected.diagnostics.is_empty());
21539    }
21540
21541    #[test]
21542    fn recovery_fast_outcome_dedupe_uses_selection_rank() {
21543        let mut arena = RecognitionArena::default();
21544        let first = FastRecognizeOutcome {
21545            index: 3,
21546            consumed_eof: false,
21547            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
21548                line: 1,
21549                column: 0,
21550                message: "mismatched input 'x' expecting 'a'".to_owned(),
21551                offending: None,
21552            }]),
21553            deferred_nodes: FastDeferredNodeId::EMPTY,
21554            nodes: NodeSeqId::EMPTY,
21555        };
21556        let same_rank = FastRecognizeOutcome {
21557            index: first.index,
21558            consumed_eof: first.consumed_eof,
21559            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
21560                line: 1,
21561                column: 0,
21562                message: "mismatched input 'x' expecting 'b'".to_owned(),
21563                offending: None,
21564            }]),
21565            deferred_nodes: FastDeferredNodeId::EMPTY,
21566            nodes: NodeSeqId::EMPTY,
21567        };
21568        let better_rank = FastRecognizeOutcome {
21569            index: first.index,
21570            consumed_eof: first.consumed_eof,
21571            diagnostics: arena.diagnostic_sequence([ParserDiagnostic {
21572                line: 1,
21573                column: 0,
21574                message: "missing 'a' at 'x'".to_owned(),
21575                offending: None,
21576            }]),
21577            deferred_nodes: FastDeferredNodeId::EMPTY,
21578            nodes: NodeSeqId::EMPTY,
21579        };
21580        let mut outcomes = vec![first, same_rank, better_rank];
21581
21582        dedupe_fast_outcomes(&mut outcomes, &arena);
21583
21584        assert_eq!(outcomes.len(), 2);
21585        assert_eq!(
21586            arena
21587                .diagnostics(outcomes[0].diagnostics)
21588                .next()
21589                .expect("first diagnostic")
21590                .message,
21591            "mismatched input 'x' expecting 'a'"
21592        );
21593        assert_eq!(
21594            arena
21595                .diagnostics(outcomes[1].diagnostics)
21596                .next()
21597                .expect("second diagnostic")
21598                .message,
21599            "missing 'a' at 'x'"
21600        );
21601    }
21602
21603    #[test]
21604    fn fast_outcome_selection_prefers_generated_caller_follow() {
21605        let arena = RecognitionArena::default();
21606        let earlier = FastRecognizeOutcome {
21607            index: 7,
21608            consumed_eof: false,
21609            diagnostics: DiagnosticSeqId::EMPTY,
21610            deferred_nodes: FastDeferredNodeId::EMPTY,
21611            nodes: NodeSeqId::EMPTY,
21612        };
21613        let later = FastRecognizeOutcome {
21614            index: 8,
21615            consumed_eof: false,
21616            diagnostics: DiagnosticSeqId::EMPTY,
21617            deferred_nodes: FastDeferredNodeId::EMPTY,
21618            nodes: NodeSeqId::EMPTY,
21619        };
21620        let mut follow = TokenBitSet::default();
21621        follow.insert(5);
21622
21623        let selected = select_best_fast_outcome(
21624            [later, earlier].into_iter(),
21625            PredictionMode::Ll,
21626            Some(&follow),
21627            |index| (if index == 7 { 5 } else { TOKEN_EOF }, index == 7, true),
21628            &arena,
21629        )
21630        .expect("one outcome should be selected");
21631        assert_eq!(selected.index, 7);
21632
21633        let selected = select_best_fast_outcome(
21634            [later, earlier].into_iter(),
21635            PredictionMode::Ll,
21636            Some(&follow),
21637            |index| (if index == 7 { 5 } else { TOKEN_EOF }, false, true),
21638            &arena,
21639        )
21640        .expect("one outcome should be selected");
21641        assert_eq!(selected.index, 8);
21642
21643        let indented_next_statement = FastRecognizeOutcome {
21644            index: 9,
21645            consumed_eof: false,
21646            diagnostics: DiagnosticSeqId::EMPTY,
21647            deferred_nodes: FastDeferredNodeId::EMPTY,
21648            nodes: NodeSeqId::EMPTY,
21649        };
21650        let selected = select_best_fast_outcome(
21651            [indented_next_statement, earlier].into_iter(),
21652            PredictionMode::Ll,
21653            Some(&follow),
21654            |index| {
21655                let is_boundary = index == 7;
21656                let is_boundary_gap = matches!(index, 7 | 8);
21657                (
21658                    if index == 7 { 5 } else { TOKEN_EOF },
21659                    is_boundary,
21660                    is_boundary_gap,
21661                )
21662            },
21663            &arena,
21664        )
21665        .expect("one outcome should be selected");
21666        assert_eq!(selected.index, 7);
21667
21668        let continuation = FastRecognizeOutcome {
21669            index: 10,
21670            consumed_eof: false,
21671            diagnostics: DiagnosticSeqId::EMPTY,
21672            deferred_nodes: FastDeferredNodeId::EMPTY,
21673            nodes: NodeSeqId::EMPTY,
21674        };
21675        let selected = select_best_fast_outcome(
21676            [continuation, earlier].into_iter(),
21677            PredictionMode::Ll,
21678            Some(&follow),
21679            |index| {
21680                let is_boundary = matches!(index, 7 | 9);
21681                (
21682                    if index == 7 { 5 } else { TOKEN_EOF },
21683                    is_boundary,
21684                    is_boundary,
21685                )
21686            },
21687            &arena,
21688        )
21689        .expect("one outcome should be selected");
21690        assert_eq!(selected.index, 10);
21691
21692        let selected = select_best_fast_outcome(
21693            [earlier, later].into_iter(),
21694            PredictionMode::Sll,
21695            Some(&follow),
21696            |_| panic!("caller-follow token probe should not run in SLL mode"),
21697            &arena,
21698        )
21699        .expect("one outcome should be selected");
21700        assert_eq!(selected.index, 8);
21701    }
21702
21703    #[test]
21704    fn caller_follow_boundary_text_requires_separator_shape() {
21705        assert!(is_caller_follow_boundary_text(";"));
21706        assert!(is_caller_follow_boundary_text("\n"));
21707        assert!(is_caller_follow_boundary_text("\r\n  "));
21708        assert!(is_caller_follow_boundary_text(";\n"));
21709        assert!(!is_caller_follow_boundary_text("\"\"\"line1\nline2\"\"\""));
21710        assert!(!is_caller_follow_boundary_text("/* line1\nline2 */"));
21711        assert!(!is_caller_follow_boundary_text("identifier"));
21712        assert!(is_caller_follow_boundary_gap_text(" \t "));
21713        assert!(is_caller_follow_boundary_gap_text("\n  "));
21714        assert!(is_caller_follow_boundary_gap_text(";\t"));
21715        assert!(!is_caller_follow_boundary_gap_text(
21716            "\"\"\"line1\nline2\"\"\""
21717        ));
21718        assert!(!is_caller_follow_boundary_gap_text("/* line1\nline2 */"));
21719    }
21720
21721    #[test]
21722    fn caller_follow_token_info_treats_hidden_tokens_as_boundary_gaps() {
21723        let mut parser = mini_parser(vec![
21724            TestToken::new(5).with_text("\n"),
21725            TestToken::new(6)
21726                .with_text("// comment\n")
21727                .with_channel(HIDDEN_CHANNEL),
21728            TestToken::new(1).with_text("x"),
21729            TestToken::eof("parser-test", 1, 2, 0),
21730        ]);
21731
21732        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
21733        assert_eq!(parser.caller_follow_token_info(1), (6, false, true));
21734        assert_eq!(parser.caller_follow_token_info(2), (1, false, false));
21735    }
21736
21737    #[test]
21738    fn caller_follow_token_info_uses_stream_visible_channel() {
21739        let source = Source {
21740            tokens: vec![
21741                TestToken::new(5).with_text("\n").with_channel(2),
21742                TestToken::new(1).with_text("x").with_channel(2),
21743                TestToken::new(6)
21744                    .with_text("// comment\n")
21745                    .with_channel(HIDDEN_CHANNEL),
21746                TestToken::eof("parser-test", 1, 2, 0),
21747            ],
21748            index: 0,
21749        };
21750        let data = RecognizerData::new(
21751            "Mini.g4",
21752            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
21753        );
21754        let mut parser = BaseParser::new(CommonTokenStream::with_channel(source, 2), data);
21755
21756        assert_eq!(parser.caller_follow_token_info(0), (5, true, true));
21757        assert_eq!(parser.caller_follow_token_info(1), (1, false, false));
21758        assert_eq!(parser.caller_follow_token_info(2), (6, false, true));
21759    }
21760
21761    #[test]
21762    fn reset_per_parse_caches_clears_state_expected_token_cache() {
21763        let atn = token_then_eof_atn();
21764        let mut parser = mini_parser(Vec::new());
21765
21766        let _ = parser.cached_state_expected_token_set(&atn, 0);
21767        assert!(!parser.state_expected_token_cache.is_empty());
21768
21769        parser.reset_per_parse_caches();
21770        assert!(parser.state_expected_token_cache.is_empty());
21771    }
21772
21773    #[test]
21774    fn empty_cycle_cache_survives_reset_and_invalidates_for_a_different_atn() {
21775        let cyclic = epsilon_cycle_atn();
21776        let acyclic = token_then_eof_atn();
21777        let mut parser = mini_parser(Vec::new());
21778
21779        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
21780        assert_eq!(
21781            parser.empty_cycle_cache_atn,
21782            Some(SharedAtnCacheKey::for_atn(&cyclic))
21783        );
21784        assert_eq!(parser.empty_cycle_cache[1], Some(true));
21785
21786        parser.reset_per_parse_caches();
21787        assert_eq!(parser.empty_cycle_cache[1], Some(true));
21788        assert!(parser.state_can_reenter_without_consuming(&cyclic, 1));
21789
21790        assert!(!parser.state_can_reenter_without_consuming(&acyclic, 1));
21791        assert_eq!(
21792            parser.empty_cycle_cache_atn,
21793            Some(SharedAtnCacheKey::for_atn(&acyclic))
21794        );
21795        assert_eq!(parser.empty_cycle_cache[1], Some(false));
21796    }
21797
21798    #[test]
21799    fn parser_error_with_empty_expected_set_omits_empty_set_display() {
21800        let source = Source {
21801            tokens: vec![
21802                TestToken::new(1).with_text("x"),
21803                TestToken::eof("parser-test", 1, 1, 1),
21804            ],
21805            index: 0,
21806        };
21807        let data = RecognizerData::new(
21808            "Mini.g4",
21809            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
21810        );
21811        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
21812        let expected = ExpectedTokens {
21813            index: Some(0),
21814            symbols: BTreeSet::new(),
21815            no_viable: None,
21816        };
21817
21818        let (_, message) = parser.expected_error_message(0, 0, &expected);
21819
21820        assert_eq!(message, "mismatched input 'x'");
21821    }
21822
21823    #[test]
21824    fn eof_rule_stop_index_points_at_eof_token() {
21825        let source = Source {
21826            tokens: vec![
21827                TestToken::new(1).with_text("x"),
21828                TestToken::eof("parser-test", 1, 1, 1),
21829            ],
21830            index: 0,
21831        };
21832        let data = RecognizerData::new(
21833            "Mini.g4",
21834            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
21835        );
21836        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
21837
21838        assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
21839        assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
21840    }
21841
21842    #[test]
21843    fn generated_parser_action_uses_current_rule_stop_boundary() {
21844        let mut parser = mini_parser(vec![
21845            TestToken::new(1).with_text("x"),
21846            TestToken::eof("parser-test", 1, 1, 1),
21847        ]);
21848
21849        parser.match_token(1).expect("token should match");
21850        let action = parser.parser_action_at_current(7, 0, 0, false);
21851        assert_eq!(action.source_state(), 7);
21852        assert_eq!(action.rule_index(), 0);
21853        assert_eq!(action.start_index(), 0);
21854        assert_eq!(action.stop_index(), Some(0));
21855
21856        parser.match_eof().expect("EOF should match");
21857        let action = parser.parser_action_at_current(8, 0, 0, true);
21858        assert_eq!(action.stop_index(), Some(1));
21859    }
21860
21861    #[test]
21862    fn folds_left_recursive_boundary_into_rule_node() {
21863        let mut arena = RecognitionArena::default();
21864        let first = arena.push_node(ArenaRecognizedNode::Token {
21865            token: TokenId::try_from(0).expect("test token ID"),
21866        });
21867        let boundary = arena.push_node(ArenaRecognizedNode::LeftRecursiveBoundary {
21868            rule_index: 1,
21869            alt_number: 3,
21870        });
21871        let second = arena.push_node(ArenaRecognizedNode::Token {
21872            token: TokenId::try_from(1).expect("test token ID"),
21873        });
21874        let mut nodes = NodeSeqId::EMPTY;
21875        for node in [first, boundary, second].into_iter().rev() {
21876            nodes = arena.prepend(nodes, node);
21877        }
21878
21879        let folded = arena.fold_left_recursive_boundaries(nodes);
21880        let folded_nodes = arena.iter(folded).collect::<Vec<_>>();
21881
21882        assert_eq!(folded_nodes.len(), 2);
21883        let ArenaRecognizedNode::Rule {
21884            rule_index,
21885            invoking_state,
21886            alt_number,
21887            start_index,
21888            stop_index,
21889            children,
21890            ..
21891        } = arena.node(folded_nodes[0])
21892        else {
21893            panic!("first folded node should be a rule");
21894        };
21895        // The folded rule node's scalar shape (rule/invoking-state/alt/start/stop) is one snapshot;
21896        // child resolution and the sibling identity below stay explicit — a node Debug prints the
21897        // children handle, not the resolved sequence they assert on.
21898        insta::assert_debug_snapshot!(
21899            "folds_left_recursive_boundary_into_rule_node",
21900            (
21901                rule_index,
21902                invoking_state,
21903                alt_number,
21904                start_index,
21905                stop_index
21906            )
21907        );
21908        assert_eq!(arena.iter(children).collect::<Vec<_>>(), [first]);
21909        assert_eq!(arena.node(folded_nodes[1]), arena.node(second));
21910
21911        let stats = arena.stats(folded, DiagnosticSeqId::EMPTY);
21912        assert_eq!(
21913            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
21914            (4, 3, 1)
21915        );
21916        assert_eq!(
21917            (stats.total_links, stats.live_links, stats.dead_links),
21918            (9, 3, 6)
21919        );
21920    }
21921
21922    #[test]
21923    fn recognition_arena_reports_live_dead_and_retained_capacity() {
21924        let mut arena = RecognitionArena::default();
21925        let token = arena.push_node(ArenaRecognizedNode::Token {
21926            token: TokenId::try_from(0).expect("test token ID"),
21927        });
21928        let extra = arena.push_extra(RecognitionExtra::MissingToken {
21929            token_type: 2,
21930            at_index: 1,
21931            text: "<missing X>".to_owned(),
21932        });
21933        let missing = arena.push_node(ArenaRecognizedNode::MissingToken { extra });
21934        let discarded = arena.push_node(ArenaRecognizedNode::ErrorToken {
21935            token: TokenId::try_from(1).expect("test token ID"),
21936        });
21937        let mut live = NodeSeqId::EMPTY;
21938        live = arena.prepend(live, missing);
21939        live = arena.prepend(live, token);
21940        let _discarded_sequence = arena.prepend(NodeSeqId::EMPTY, discarded);
21941        let live_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
21942            line: 1,
21943            column: 0,
21944            message: "missing X".to_owned(),
21945            offending: None,
21946        }]);
21947        let _discarded_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
21948            line: 1,
21949            column: 1,
21950            message: "discarded".to_owned(),
21951            offending: None,
21952        }]);
21953        let deferred_children = arena.deferred_fragment(live);
21954        let _deferred_rule = arena.deferred_rule_node(FastDeferredRule {
21955            rule_index: 0,
21956            invoking_state: -1,
21957            start_index: 0,
21958            stop_index: Some(1),
21959            deferred_children,
21960            children: NodeSeqId::EMPTY,
21961        });
21962
21963        let stats = arena.stats(live, live_diagnostics);
21964
21965        assert_eq!(
21966            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
21967            (3, 2, 1)
21968        );
21969        assert_eq!(
21970            (stats.total_links, stats.live_links, stats.dead_links),
21971            (5, 3, 2)
21972        );
21973        assert_eq!(
21974            (stats.total_extras, stats.live_extras, stats.dead_extras),
21975            (3, 2, 1)
21976        );
21977        assert!(size_of::<SeqLink>() <= 8);
21978        assert!(size_of::<DiagnosticLink>() <= 8);
21979        assert!(size_of::<FastDeferredNode>() <= 12);
21980        assert!(size_of::<FastDeferredRule>() <= 28);
21981        assert!(size_of::<FastRecognizeOutcome>() <= 24);
21982        let capacities = (
21983            stats.node_capacity,
21984            stats.link_capacity,
21985            stats.extra_capacity,
21986        );
21987        let deferred_capacities = (
21988            arena.deferred_nodes.capacity(),
21989            arena.deferred_rules.capacity(),
21990        );
21991
21992        arena.reset();
21993        let reset = arena.stats(NodeSeqId::EMPTY, DiagnosticSeqId::EMPTY);
21994        assert_eq!(
21995            (reset.total_nodes, reset.total_links, reset.total_extras),
21996            (0, 0, 0)
21997        );
21998        assert_eq!(
21999            (
22000                reset.node_capacity,
22001                reset.link_capacity,
22002                reset.extra_capacity,
22003            ),
22004            capacities
22005        );
22006        assert!(arena.deferred_nodes.is_empty());
22007        assert!(arena.deferred_rules.is_empty());
22008        assert_eq!(
22009            (
22010                arena.deferred_nodes.capacity(),
22011                arena.deferred_rules.capacity(),
22012            ),
22013            deferred_capacities
22014        );
22015    }
22016
22017    #[test]
22018    fn parser_computes_recognition_arena_stats_on_demand() {
22019        let mut parser = mini_parser(Vec::new());
22020        let live = parser
22021            .recognition_arena
22022            .push_node(ArenaRecognizedNode::Token {
22023                token: TokenId::try_from(0).expect("test token ID"),
22024            });
22025        let discarded = parser
22026            .recognition_arena
22027            .push_node(ArenaRecognizedNode::ErrorToken {
22028                token: TokenId::try_from(1).expect("test token ID"),
22029            });
22030        let live_root = parser.recognition_arena.prepend(NodeSeqId::EMPTY, live);
22031        let _discarded_root = parser
22032            .recognition_arena
22033            .prepend(NodeSeqId::EMPTY, discarded);
22034        parser.finish_recognition_arena(live_root, DiagnosticSeqId::EMPTY);
22035
22036        let stats = parser.recognition_arena_stats();
22037
22038        assert_eq!(
22039            (stats.total_nodes, stats.live_nodes, stats.dead_nodes),
22040            (2, 1, 1)
22041        );
22042        assert_eq!(
22043            (stats.total_links, stats.live_links, stats.dead_links),
22044            (2, 1, 1)
22045        );
22046    }
22047
22048    #[test]
22049    fn recognition_arena_drops_capacity_above_retention_limit() {
22050        let mut storage = Vec::<u8>::with_capacity(4);
22051        storage.extend([1, 2, 3]);
22052
22053        reset_arena_vec(&mut storage, 3);
22054
22055        assert!(storage.is_empty());
22056        assert_eq!(storage.capacity(), 0);
22057    }
22058
22059    #[test]
22060    fn recognition_arena_concatenates_diagnostics_in_source_order() {
22061        let mut arena = RecognitionArena::default();
22062        let prefix = arena.diagnostic_sequence([
22063            ParserDiagnostic {
22064                line: 1,
22065                column: 0,
22066                message: "first".to_owned(),
22067                offending: None,
22068            },
22069            ParserDiagnostic {
22070                line: 1,
22071                column: 1,
22072                message: "second".to_owned(),
22073                offending: None,
22074            },
22075        ]);
22076        let suffix = arena.diagnostic_sequence([ParserDiagnostic {
22077            line: 1,
22078            column: 2,
22079            message: "third".to_owned(),
22080            offending: None,
22081        }]);
22082        let extras_before = arena.extras.len();
22083
22084        let combined = arena.concat_diagnostics(prefix, suffix);
22085        let messages = arena
22086            .diagnostics(combined)
22087            .map(|diagnostic| diagnostic.message.as_str())
22088            .collect::<Vec<_>>();
22089
22090        assert_eq!(messages, ["first", "second", "third"]);
22091        assert_eq!(arena.extras.len(), extras_before);
22092    }
22093
22094    #[test]
22095    fn outcome_ties_keep_later_non_recursive_alternative() {
22096        let arena = RecognitionArena::default();
22097        let first = RecognizeOutcome {
22098            index: 1,
22099            consumed_eof: false,
22100            alt_number: 0,
22101            member_values: MemberEnv::new(),
22102            return_values: BTreeMap::new(),
22103            diagnostics: DiagnosticSeqId::EMPTY,
22104            decisions: Vec::new(),
22105            actions: vec![ParserAction::new(1, 0, 0, None)],
22106            nodes: NodeSeqId::EMPTY,
22107        };
22108        let second = RecognizeOutcome {
22109            actions: vec![ParserAction::new(2, 0, 0, None)],
22110            ..first.clone()
22111        };
22112
22113        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
22114            .expect("one outcome should be selected");
22115        assert_eq!(selected.actions[0].source_state(), 2);
22116    }
22117
22118    #[test]
22119    fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
22120        let arena = RecognitionArena::default();
22121        let first = RecognizeOutcome {
22122            index: 1,
22123            consumed_eof: false,
22124            alt_number: 0,
22125            member_values: MemberEnv::new(),
22126            return_values: BTreeMap::new(),
22127            diagnostics: DiagnosticSeqId::EMPTY,
22128            decisions: Vec::new(),
22129            actions: vec![ParserAction::new(1, 0, 0, None)],
22130            nodes: NodeSeqId::EMPTY,
22131        };
22132        let second = RecognizeOutcome {
22133            actions: vec![
22134                ParserAction::new(2, 0, 0, None),
22135                ParserAction::new(3, 0, 0, None),
22136            ],
22137            ..first.clone()
22138        };
22139
22140        let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll, &arena)
22141            .expect("one outcome should be selected");
22142        assert_eq!(selected.actions.len(), 2);
22143    }
22144
22145    #[test]
22146    fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
22147        let arena = RecognitionArena::default();
22148        let first = RecognizeOutcome {
22149            index: 7,
22150            consumed_eof: false,
22151            alt_number: 0,
22152            member_values: MemberEnv::new(),
22153            return_values: BTreeMap::new(),
22154            diagnostics: DiagnosticSeqId::EMPTY,
22155            decisions: vec![1, 0],
22156            actions: vec![
22157                ParserAction::new(23, 2, 2, Some(4)),
22158                ParserAction::new(23, 2, 0, Some(6)),
22159            ],
22160            nodes: NodeSeqId::EMPTY,
22161        };
22162        let second = RecognizeOutcome {
22163            decisions: vec![0, 1],
22164            actions: vec![
22165                ParserAction::new(23, 2, 2, Some(6)),
22166                ParserAction::new(23, 2, 0, Some(6)),
22167            ],
22168            ..first.clone()
22169        };
22170
22171        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
22172            .expect("one outcome should be selected");
22173        assert_eq!(selected.actions[0].stop_index(), Some(6));
22174    }
22175
22176    #[test]
22177    fn outcome_ties_keep_first_recursive_tree_shape() {
22178        let mut arena = RecognitionArena::default();
22179        let token = arena.push_node(ArenaRecognizedNode::Token {
22180            token: TokenId::try_from(0).expect("test token ID"),
22181        });
22182        let token_children = arena.prepend(NodeSeqId::EMPTY, token);
22183        let inner = arena.push_node(ArenaRecognizedNode::Rule {
22184            rule_index: 1,
22185            invoking_state: -1,
22186            alt_number: 0,
22187            start_index: 0,
22188            stop_index: Some(0),
22189            return_values: None,
22190            children: token_children,
22191        });
22192        let inner_children = arena.prepend(NodeSeqId::EMPTY, inner);
22193        let outer = arena.push_node(ArenaRecognizedNode::Rule {
22194            rule_index: 1,
22195            invoking_state: -1,
22196            alt_number: 0,
22197            start_index: 0,
22198            stop_index: Some(0),
22199            return_values: None,
22200            children: inner_children,
22201        });
22202        let recursive_nodes = arena.prepend(NodeSeqId::EMPTY, outer);
22203        let first = RecognizeOutcome {
22204            index: 1,
22205            consumed_eof: false,
22206            alt_number: 0,
22207            member_values: MemberEnv::new(),
22208            return_values: BTreeMap::new(),
22209            diagnostics: DiagnosticSeqId::EMPTY,
22210            decisions: Vec::new(),
22211            actions: vec![ParserAction::new(1, 0, 0, None)],
22212            nodes: recursive_nodes,
22213        };
22214        let second = RecognizeOutcome {
22215            index: 1,
22216            consumed_eof: false,
22217            alt_number: 0,
22218            member_values: MemberEnv::new(),
22219            return_values: BTreeMap::new(),
22220            diagnostics: DiagnosticSeqId::EMPTY,
22221            decisions: Vec::new(),
22222            actions: vec![ParserAction::new(2, 0, 0, None)],
22223            nodes: recursive_nodes,
22224        };
22225
22226        let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll, &arena)
22227            .expect("one outcome should be selected");
22228        assert_eq!(selected.actions[0].source_state(), 1);
22229    }
22230
22231    #[test]
22232    fn sll_outcome_selection_keeps_earlier_recovered_alt() {
22233        let mut arena = RecognitionArena::default();
22234        let recovered_diagnostics = arena.diagnostic_sequence([ParserDiagnostic {
22235            line: 1,
22236            column: 3,
22237            message: "missing 'Y' at '<EOF>'".to_owned(),
22238            offending: None,
22239        }]);
22240        let first_alt = RecognizeOutcome {
22241            index: 2,
22242            consumed_eof: true,
22243            alt_number: 0,
22244            member_values: MemberEnv::new(),
22245            return_values: BTreeMap::new(),
22246            diagnostics: recovered_diagnostics,
22247            decisions: vec![0],
22248            actions: vec![ParserAction::new(1, 0, 0, None)],
22249            nodes: NodeSeqId::EMPTY,
22250        };
22251        let second_alt = RecognizeOutcome {
22252            diagnostics: DiagnosticSeqId::EMPTY,
22253            decisions: vec![1],
22254            actions: vec![ParserAction::new(2, 0, 0, None)],
22255            ..first_alt.clone()
22256        };
22257
22258        let selected = select_best_outcome(
22259            [second_alt, first_alt].into_iter(),
22260            PredictionMode::Sll,
22261            &arena,
22262        )
22263        .expect("one outcome should be selected");
22264        assert_eq!(arena.diagnostics_len(selected.diagnostics), 1);
22265        assert_eq!(selected.decisions, [0]);
22266    }
22267}