Skip to main content

antlr4_runtime/atn/
parser.rs

1use crate::atn::AtnStateKind;
2use crate::atn::parser_atn::{
3    ParserAtn as Atn, ParserAtnState as AtnState, ParserTransition, ParserTransitionKind,
4};
5#[cfg(test)]
6use crate::atn::parser_atn::{ParserAtnBuilder, ParserTransitionSpec};
7use crate::dfa::{
8    DfaStateBuilder, DfaStateId, NO_DFA_STATE, ParserDfa, ParserDfaStateView, ParserDfaStats,
9};
10use crate::int_stream::IntStream;
11use crate::prediction::{
12    AtnConfig, AtnConfigSet, ContextArena, ContextId, EMPTY_CONTEXT, EMPTY_RETURN_STATE,
13    PredictionContextStats, PredictionFxHasher, PredictionWorkspace, SemanticContext,
14    all_subsets_conflict, all_subsets_equal, conflicting_alt_subsets,
15    has_sll_conflict_terminating_prediction, single_viable_alt,
16};
17use crate::token::TOKEN_EOF;
18use std::cell::RefCell;
19use std::collections::{HashMap, HashSet};
20use std::hash::BuildHasherDefault;
21
22type FxHashSet<T> = HashSet<T, BuildHasherDefault<PredictionFxHasher>>;
23
24#[derive(Debug)]
25pub struct ParserAtnSimulator<'a> {
26    atn: &'a Atn,
27    store: PredictionStore,
28    workspace: PredictionWorkspace,
29    outer_context_cache: Option<CachedOuterContext>,
30    outer_context_cache_hits: usize,
31    outer_context_cache_misses: usize,
32    /// Accept states treated as provisional by the latest direct prediction.
33    /// Generated SLL still uses their stored accept metadata.
34    deferred_accept_states: FxHashSet<(usize, DfaStateId)>,
35    shared_cache_key: Option<usize>,
36    shared_cache_generation: u64,
37    has_trained_decision: bool,
38    measure_adaptive_work: bool,
39    adaptive_calls: usize,
40    adaptive_closure_work: usize,
41    /// Java's `LL_EXACT_AMBIG_DETECTION`: the full-context loop keeps
42    /// consuming past "resolves to one viable alt" conflicts until every
43    /// `(state, context)` subset conflicts over the same alt set.
44    exact_ambig_detection: bool,
45    /// Memoized full-context resolutions. Upstream re-runs the LL simulation
46    /// on every visit to a `requires_full_context` DFA state; grammars with
47    /// keyword/identifier-style true ambiguities (Avro IDL's `nullableType`,
48    /// SQL non-reserved keywords) pay that on every occurrence. Under the
49    /// memo gate — no predicate transitions in the ATN — the LL result is a
50    /// pure function of the decision, precedence, interned caller context,
51    /// and the token window the loop read, so identical occurrences replay
52    /// the recorded resolution. The gate carries that purity claim: general
53    /// ANTLR full-context closure can also consult parser state through
54    /// `predTransition`, which is exactly what the gate excludes.
55    full_context_memo: HashMap<
56        FullContextMemoKey,
57        Vec<FullContextMemoEntry>,
58        BuildHasherDefault<PredictionFxHasher>,
59    >,
60    full_context_memo_len: usize,
61    /// Whether the memo is sound for this ATN: predicates make prediction
62    /// outcomes depend on caller-side evaluation, so any semantic transition
63    /// disables memoization entirely. Computed lazily on first retry.
64    full_context_memo_gate: Option<bool>,
65}
66
67#[derive(Clone, Copy, Debug)]
68struct CachedOuterContext {
69    rule_context_version: usize,
70    context: ContextId,
71}
72
73/// Lookup key for one memoized full-context resolution.
74///
75/// The first window token joins the key so a probe is one hash lookup in
76/// the common case (distinct keyword per resolution) instead of a scan.
77///
78/// `outer_context` is the interned FULL caller stack, which bounds the hit
79/// rate: the same construct at rule-nesting depth 3 and depth 4 has
80/// different `ContextId`s and misses. Flat-ish grammars (Avro IDL: ~155
81/// unique contexts against 2,100 retries) hit almost always; deeply
82/// recursive grammars re-derive once per distinct nesting shape.
83#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
84struct FullContextMemoKey {
85    decision: usize,
86    precedence: i32,
87    outer_context: ContextId,
88    first_symbol: i32,
89}
90
91/// One memoized full-context resolution.
92///
93/// `window_tail` is the visible-token sequence the recorded LL loop read
94/// after the keyed first symbol, so a hit replays only when the upcoming
95/// input matches token-for-token — identical decision + precedence +
96/// interned caller context + read window is literally the same computation.
97///
98/// `prediction.stop_index` holds the RECORDING run's absolute index and is
99/// stale for any other occurrence — the probe unconditionally overwrites it
100/// from the live cursor before returning a replay. Do not read it directly.
101#[derive(Clone, Debug)]
102struct FullContextMemoEntry {
103    window_tail: Vec<i32>,
104    prediction: FullContextPrediction,
105}
106
107/// Memoized windows above this many visible tokens are not recorded: long
108/// ambiguous prefixes are rare, and verifying a hit costs a token compare
109/// per window token.
110const FULL_CONTEXT_MEMO_MAX_WINDOW: usize = 16;
111/// Total memo entries per simulator. Contexts are interned per parse and
112/// real grammars produce a few hundred; the bound only guards adversarial
113/// context churn.
114const FULL_CONTEXT_MEMO_MAX_ENTRIES: usize = 4096;
115
116/// ATN-static memo gate, cached per thread by ATN identity.
117///
118/// The gate is a property of the ATN alone, but generated parsers build a
119/// simulator per parser instance, so a per-simulator cache would rescan the
120/// ATN on the first LL retry of every parse — pure cost for grammars the
121/// gate turns off. Keyed like `SHARED_PREDICTION_STORES`.
122fn atn_has_predicate_transition(atn: &Atn) -> bool {
123    thread_local! {
124        static GATES: RefCell<HashMap<usize, bool, BuildHasherDefault<PredictionFxHasher>>> =
125            RefCell::new(HashMap::default());
126    }
127    let ptr: *const Atn = atn;
128    let key = ptr as usize;
129    GATES.with(|gates| {
130        *gates.borrow_mut().entry(key).or_insert_with(|| {
131            (0..atn.state_count()).any(|state_number| {
132                atn.state(state_number).is_some_and(|state| {
133                    state
134                        .transitions()
135                        .into_iter()
136                        .any(|transition| transition.kind() == ParserTransitionKind::Predicate)
137                })
138            })
139        })
140    })
141}
142
143#[derive(Debug, Default)]
144struct PredictionStore {
145    contexts: ContextArena,
146    decision_to_dfa: Vec<ParserDfa>,
147}
148
149impl PredictionStore {
150    fn new(atn: &Atn) -> Self {
151        Self {
152            contexts: ContextArena::new(),
153            decision_to_dfa: initial_decision_dfas(atn),
154        }
155    }
156}
157
158#[derive(Debug, Default)]
159struct SharedPredictionStore {
160    generation: u64,
161    store: Option<PredictionStore>,
162}
163
164thread_local! {
165    static SHARED_PREDICTION_STORES: RefCell<HashMap<usize, SharedPredictionStore>> =
166        RefCell::new(HashMap::new());
167}
168
169fn clear_shared_prediction_store(key: usize) -> u64 {
170    SHARED_PREDICTION_STORES.with(|cache| {
171        let mut cache = cache.borrow_mut();
172        let shared = cache.entry(key).or_default();
173        shared.generation = shared.generation.wrapping_add(1);
174        shared.store = None;
175        shared.generation
176    })
177}
178
179const ADAPTIVE_ATN_PREFERENCE_MIN_CALLS: usize = 32;
180const ADAPTIVE_ATN_PREFERENCE_MIN_CLOSURE_WORK_PER_CALL: usize = 256;
181const ADAPTIVE_ATN_PREFERENCE_DECISIVE_CLOSURE_WORK_PER_CALL: usize = 512;
182
183const fn adaptive_prediction_has_work_density(
184    calls: usize,
185    closure_work: usize,
186    minimum_closure_work_per_call: usize,
187) -> bool {
188    calls >= ADAPTIVE_ATN_PREFERENCE_MIN_CALLS
189        && closure_work >= calls.saturating_mul(minimum_closure_work_per_call)
190}
191
192#[derive(Clone, Debug, Eq, PartialEq)]
193pub struct ParserAtnPrediction {
194    pub alt: usize,
195    pub requires_full_context: bool,
196    pub has_semantic_context: bool,
197    pub diagnostic: Option<ParserAtnPredictionDiagnostic>,
198}
199
200#[derive(Clone, Debug, Eq, PartialEq)]
201pub struct ParserAtnPredictionDiagnostic {
202    pub kind: ParserAtnPredictionDiagnosticKind,
203    pub start_index: usize,
204    pub sll_stop_index: usize,
205    pub ll_stop_index: usize,
206    pub conflicting_alts: Vec<usize>,
207    /// For [`ParserAtnPredictionDiagnosticKind::Ambiguity`]: whether the
208    /// full-context loop proved an exact ambiguity (Java's `exact` flag —
209    /// the default `DiagnosticErrorListener` only reports exact ones).
210    pub exact: bool,
211}
212
213#[derive(Clone, Copy, Debug, Eq, PartialEq)]
214pub enum ParserAtnPredictionDiagnosticKind {
215    Ambiguity,
216    ContextSensitivity,
217}
218
219#[derive(Clone, Copy)]
220struct PredictionCheck {
221    decision: usize,
222    decision_state: usize,
223    state_number: DfaStateId,
224    start_index: usize,
225    precedence: i32,
226    outer_context: ContextId,
227    force_full_context_retry: bool,
228    sll_probe_only: bool,
229}
230
231#[derive(Clone, Copy)]
232struct AdaptivePredictRequest {
233    decision: usize,
234    precedence: usize,
235    outer_context: ContextId,
236    force_full_context_retry: bool,
237    /// When set, the SLL walk stops at the first full-context-requiring conflict
238    /// and returns the SLL prediction (carrying `requires_full_context = true`)
239    /// WITHOUT running the expensive full-context LL loop. The generated
240    /// two-stage prediction uses only that boolean to decide whether to re-run
241    /// with the real outer context, so the empty-context LL pass this skips is
242    /// discarded work. Mirrors Go's execATN, which returns "needs LL" from the
243    /// SLL stage rather than computing LL twice.
244    sll_probe_only: bool,
245}
246
247#[derive(Clone, Copy)]
248struct DfaEdge {
249    decision: usize,
250    source_state: DfaStateId,
251}
252
253#[derive(Clone, Debug)]
254struct PreviousGoodAlt {
255    alt: usize,
256    configs: Vec<AtnConfig>,
257}
258
259#[derive(Clone, Debug, Eq, PartialEq)]
260struct DfaPredictionInfo {
261    prediction: ParserAtnPrediction,
262    conflicting_alts: Vec<usize>,
263}
264
265#[derive(Clone, Debug, Eq, PartialEq)]
266struct FullContextPrediction {
267    prediction: ParserAtnPrediction,
268    stop_index: usize,
269    resolution: FullContextResolution,
270}
271
272/// How the full-context loop settled, mirroring the two exits of Java's
273/// `execATNWithFullContext`: a truly unique alt (reported as context
274/// sensitivity) or a conflict resolution (reported as ambiguity, exact or
275/// not).
276#[derive(Clone, Debug, Eq, PartialEq)]
277enum FullContextResolution {
278    Unique,
279    Ambiguous { exact: bool, alts: Vec<usize> },
280}
281
282fn full_context_prediction(
283    alt: usize,
284    configs: &AtnConfigSet,
285    stop_index: usize,
286    resolution: FullContextResolution,
287) -> FullContextPrediction {
288    FullContextPrediction {
289        prediction: ParserAtnPrediction {
290            alt,
291            requires_full_context: true,
292            has_semantic_context: configs_have_semantic_context_for_alt(configs, alt),
293            diagnostic: None,
294        },
295        stop_index,
296        resolution,
297    }
298}
299
300#[derive(Clone, Debug, Eq, Hash, PartialEq)]
301struct ClosureConfigKey {
302    state: usize,
303    alt: usize,
304    context: ContextId,
305    semantic_context: SemanticContext,
306    precedence_filter_suppressed: bool,
307}
308
309impl From<&AtnConfig> for ClosureConfigKey {
310    fn from(config: &AtnConfig) -> Self {
311        Self {
312            state: config.state,
313            alt: config.alt,
314            context: config.context,
315            semantic_context: config.semantic_context.clone(),
316            precedence_filter_suppressed: config.precedence_filter_suppressed,
317        }
318    }
319}
320
321/// Reusable scratch buffers for `closure`. ANTLR's reference runtimes allocate a
322/// fresh work stack and "closure busy" visited set per `closure` call (millions
323/// of allocations on large parses); reusing one buffer across the per-config
324/// calls of a single reach/start-state computation removes that churn. Each
325/// `closure` call clears the buffers first, so the visited scope stays per-call
326/// — behaviour-identical to allocating fresh sets.
327#[derive(Default)]
328struct ClosureScratch {
329    /// Work stack of `(config, collect_predicates)`. The per-config
330    /// `collect_predicates` flag mirrors ANTLR's
331    /// `continueCollecting = collectPredicates && !ActionTransition`: once an
332    /// action edge is crossed, predicates on the far side are NOT collected into
333    /// the config's semantic context, so they are deferred to parse time rather
334    /// than evaluated during prediction (the "action hides predicates" rule).
335    stack: Vec<(AtnConfig, bool)>,
336    visited: FxHashSet<ClosureConfigKey>,
337}
338
339/// Per-closure-tree invariants, grouped so `closure` stays within Clippy's
340/// argument-count budget while threading the reusable [`ClosureScratch`].
341#[derive(Clone, Copy)]
342struct ClosureParams {
343    precedence: i32,
344    collect_predicates: bool,
345    treat_eof_as_epsilon: bool,
346}
347
348#[derive(Debug)]
349struct LookaheadIntStream {
350    symbols: Vec<i32>,
351    index: usize,
352}
353
354impl LookaheadIntStream {
355    const fn new(symbols: Vec<i32>) -> Self {
356        Self { symbols, index: 0 }
357    }
358}
359
360impl IntStream for LookaheadIntStream {
361    fn consume(&mut self) {
362        if self.la(1) != TOKEN_EOF {
363            self.index += 1;
364        }
365    }
366
367    fn la(&mut self, offset: isize) -> i32 {
368        if offset <= 0 {
369            return 0;
370        }
371        let offset = offset.cast_unsigned() - 1;
372        self.symbols
373            .get(self.index + offset)
374            .copied()
375            .unwrap_or(TOKEN_EOF)
376    }
377
378    fn index(&self) -> usize {
379        self.index
380    }
381
382    fn seek(&mut self, index: usize) {
383        self.index = index.min(self.symbols.len());
384    }
385
386    fn size(&self) -> usize {
387        self.symbols.len()
388    }
389}
390
391fn initial_decision_dfas(atn: &Atn) -> Vec<ParserDfa> {
392    atn.decision_to_state()
393        .iter()
394        .enumerate()
395        .map(|(decision, state)| {
396            let mut dfa = ParserDfa::with_max_token_type(state, decision, atn.max_token_type());
397            if atn
398                .state(state)
399                .is_some_and(AtnState::precedence_rule_decision)
400            {
401                dfa.set_precedence_dfa(true);
402            }
403            dfa
404        })
405        .collect()
406}
407
408/// Merges a dropping simulator's DFAs into tables that another simulator
409/// checked in first, losslessly. The two evolved independently (the
410/// later-constructed one started cold), so numeric state ids are not
411/// comparable — but DFA states ARE comparable by their ATN config set, the
412/// same identity `ParserDfa::add_state` dedups on. Re-keying `local`'s states into
413/// `shared`'s numbering and unioning edges/starts means overlapping
414/// simulators never lose learned coverage, however it is distributed.
415/// Walking every state is fine here: this only runs on the rare
416/// overlapping-simulators drop path.
417fn union_decision_dfas(shared: &mut Vec<ParserDfa>, local: Vec<ParserDfa>) {
418    if shared.len() != local.len() {
419        *shared = local;
420        return;
421    }
422    for (shared_dfa, local_dfa) in shared.iter_mut().zip(local) {
423        union_decision_dfa(shared_dfa, local_dfa);
424    }
425}
426
427fn union_prediction_stores(
428    shared: &mut PredictionStore,
429    mut local: PredictionStore,
430    workspace: &mut PredictionWorkspace,
431) {
432    let remap = shared.contexts.import_all(&local.contexts, workspace);
433    for dfa in &mut local.decision_to_dfa {
434        dfa.remap_contexts(&remap, &shared.contexts);
435    }
436    union_decision_dfas(&mut shared.decision_to_dfa, local.decision_to_dfa);
437}
438
439fn union_decision_dfa(shared: &mut ParserDfa, local: ParserDfa) {
440    if shared.is_precedence_dfa() != local.is_precedence_dfa() {
441        // A mode flip resets the tables (`set_precedence_dfa`), so the two are
442        // not unionable; keep whichever learned more states.
443        if local.state_count() > shared.state_count() {
444            *shared = local;
445        }
446        return;
447    }
448    // Pass 1: map every local state number to a shared state number by
449    // config-set identity, inserting the states shared has not learned.
450    // Their edges reference local numbering, so they are cleared here and
451    // re-added in pass 2 under the shared numbering.
452    let mut renumber = Vec::with_capacity(local.state_count());
453    for state in local.states() {
454        let configs = local.configs(state.id());
455        let number = shared.state_id_for_configs(configs).unwrap_or_else(|| {
456            let missing = local.clone_state_without_edges(state.id());
457            shared.insert_state(missing)
458        });
459        renumber.push(number);
460    }
461    // Pass 2: union edges, translating targets into shared numbering. The
462    // incumbent's entries win; only gaps are filled. Accept metadata needs no
463    // reconciliation: it is a pure function of the config set, and equal
464    // config sets produced it through the same accept-time computation.
465    for state in local.states() {
466        let mapped = renumber[state.id().index()];
467        for transition in state.transitions() {
468            let Some(&mapped_target) = renumber.get(transition.target.index()) else {
469                continue;
470            };
471            if shared.edge(mapped, transition.symbol).is_none() {
472                shared.add_edge(mapped, transition.symbol, mapped_target);
473            }
474        }
475    }
476    if shared.start_state().is_none()
477        && let Some(start) = local.start_state()
478        && let Some(&mapped) = renumber.get(start.index())
479    {
480        shared.set_start_state(mapped);
481    }
482    for (precedence, start) in local.precedence_start_states().iter().copied().enumerate() {
483        if start == NO_DFA_STATE {
484            continue;
485        }
486        if shared.precedence_start_state(precedence).is_none()
487            && let Some(&mapped) = renumber.get(start.index())
488        {
489            shared.set_precedence_start_state(precedence, mapped);
490        }
491    }
492}
493
494impl Drop for ParserAtnSimulator<'_> {
495    fn drop(&mut self) {
496        let Some(key) = self.shared_cache_key else {
497            return;
498        };
499        #[cfg(feature = "perf-counters")]
500        let publication_started = std::time::Instant::now();
501        #[cfg(feature = "perf-counters")]
502        let published_states = self
503            .store
504            .decision_to_dfa
505            .iter()
506            .map(ParserDfa::state_count)
507            .sum();
508        // Check the DFAs back IN by move. The slot is normally vacant because
509        // `new_shared` checked them out; it is occupied only when another
510        // simulator for the same ATN was created while this one was alive
511        // (that one started cold and checked its copy in first) — then union
512        // the two by config-set identity so neither side's learning is lost.
513        let store = std::mem::take(&mut self.store);
514        let published = SHARED_PREDICTION_STORES.with(|cache| {
515            let mut cache = cache.borrow_mut();
516            let shared = cache.entry(key).or_default();
517            if shared.generation != self.shared_cache_generation {
518                return false;
519            }
520            if let Some(shared_store) = shared.store.as_mut() {
521                union_prediction_stores(shared_store, store, &mut self.workspace);
522            } else {
523                shared.store = Some(store);
524            }
525            true
526        });
527        #[cfg(feature = "perf-counters")]
528        if published {
529            crate::perf::record_dfa_cache_publication(
530                publication_started.elapsed().as_nanos(),
531                published_states,
532            );
533        }
534        #[cfg(not(feature = "perf-counters"))]
535        let _ = published;
536    }
537}
538
539impl<'a> ParserAtnSimulator<'a> {
540    pub fn new(atn: &'a Atn) -> Self {
541        Self {
542            atn,
543            store: PredictionStore::new(atn),
544            workspace: PredictionWorkspace::default(),
545            outer_context_cache: None,
546            outer_context_cache_hits: 0,
547            outer_context_cache_misses: 0,
548            deferred_accept_states: FxHashSet::default(),
549            shared_cache_key: None,
550            shared_cache_generation: 0,
551            has_trained_decision: false,
552            measure_adaptive_work: false,
553            adaptive_calls: 0,
554            adaptive_closure_work: 0,
555            exact_ambig_detection: false,
556            full_context_memo: HashMap::default(),
557            full_context_memo_len: 0,
558            full_context_memo_gate: None,
559        }
560    }
561
562    /// Resets transient simulator state while retaining learned decision DFAs.
563    pub fn reset(&mut self) {
564        self.measure_adaptive_work = false;
565        self.adaptive_calls = 0;
566        self.adaptive_closure_work = 0;
567        self.outer_context_cache = None;
568        self.deferred_accept_states.clear();
569        self.workspace.reset();
570    }
571
572    /// Clears this simulator's learned decision DFAs.
573    ///
574    /// Shared simulators also invalidate the thread-local cache generation so
575    /// an overlapping stale simulator cannot republish pre-clear states later.
576    pub fn clear_dfa(&mut self) {
577        self.store = PredictionStore::new(self.atn);
578        // The memo keys entries by ContextId into the store's arena the
579        // line above just replaced; stale IDs would alias fresh contexts.
580        self.full_context_memo.clear();
581        self.full_context_memo_len = 0;
582        self.reset();
583        self.has_trained_decision = false;
584        if let Some(key) = self.shared_cache_key {
585            self.shared_cache_generation = clear_shared_prediction_store(key);
586        }
587    }
588
589    /// Clears the thread-local learned DFA store for a generated parser ATN.
590    pub fn clear_shared_dfa(atn: &'static Atn) {
591        let ptr: *const Atn = atn;
592        clear_shared_prediction_store(ptr as usize);
593    }
594
595    /// Switches the full-context resolution strategy (Java's
596    /// `LL_EXACT_AMBIG_DETECTION` versus plain `LL`).
597    pub const fn set_exact_ambig_detection(&mut self, exact: bool) {
598        self.exact_ambig_detection = exact;
599    }
600
601    /// Creates a simulator that starts from, and publishes back into, a
602    /// thread-local DFA cache keyed by a generated parser's static ATN.
603    ///
604    /// Generated parsers usually create a fresh parser object per parse. Without
605    /// this cache every parse relearns the same adaptive DFA; with it, later
606    /// parser instances reuse the SLL cache learned by earlier instances while
607    /// still keeping mutable simulator state local to the parser during a parse.
608    ///
609    /// The DFAs are checked OUT of the cache by move (and back in on drop):
610    /// cloning a warm DFA per parser instance costs O(learned states) — ~10%
611    /// of a small parse. A second simulator created for the same ATN while one
612    /// is alive finds the slot empty and starts cold; the drop-time check-in
613    /// then remaps its context IDs and unions both independently learned stores.
614    /// Renders every non-empty learned decision DFA in the format of Java's
615    /// `Parser.dumpDFA()` / `DFASerializer` — `Decision N:` headers followed
616    /// by `s0-'else'->:s1^=>1` edge lines — which the runtime testsuite's
617    /// `showDFA` descriptors byte-compare.
618    pub fn dump_dfa_java_style(&self, vocabulary: &crate::vocabulary::Vocabulary) -> String {
619        use std::fmt::Write as _;
620        let mut out = String::new();
621        let mut seen_one = false;
622        for dfa in &self.store.decision_to_dfa {
623            if dfa.is_empty() {
624                continue;
625            }
626            if seen_one {
627                out.push('\n');
628            }
629            seen_one = true;
630            let _ = writeln!(out, "Decision {}:", dfa.decision());
631            for state in dfa.states() {
632                let source = dfa_state_display(
633                    state,
634                    self.deferred_accept_states
635                        .contains(&(dfa.decision(), state.id())),
636                );
637                for transition in state.transitions() {
638                    let Some(target_state) = dfa.state(transition.target) else {
639                        continue;
640                    };
641                    let label = vocabulary.display_name(transition.symbol);
642                    let target = dfa_state_display(
643                        target_state,
644                        self.deferred_accept_states
645                            .contains(&(dfa.decision(), target_state.id())),
646                    );
647                    let _ = writeln!(out, "{source}-{label}->{target}");
648                }
649            }
650        }
651        out
652    }
653
654    pub fn new_shared(atn: &'static Atn) -> Self {
655        let ptr: *const Atn = atn;
656        let key = ptr as usize;
657        #[cfg(feature = "perf-counters")]
658        let import_started = std::time::Instant::now();
659        let (store, generation) = SHARED_PREDICTION_STORES.with(|cache| {
660            let mut cache = cache.borrow_mut();
661            let shared = cache.entry(key).or_default();
662            (
663                shared
664                    .store
665                    .take()
666                    .unwrap_or_else(|| PredictionStore::new(atn)),
667                shared.generation,
668            )
669        });
670        let has_trained_decision = store.decision_to_dfa.iter().any(|dfa| !dfa.is_empty());
671        #[cfg(feature = "perf-counters")]
672        crate::perf::record_dfa_cache_import(
673            import_started.elapsed().as_nanos(),
674            store
675                .decision_to_dfa
676                .iter()
677                .map(ParserDfa::state_count)
678                .sum(),
679        );
680        Self {
681            atn,
682            store,
683            workspace: PredictionWorkspace::default(),
684            outer_context_cache: None,
685            outer_context_cache_hits: 0,
686            outer_context_cache_misses: 0,
687            deferred_accept_states: FxHashSet::default(),
688            shared_cache_key: Some(key),
689            shared_cache_generation: generation,
690            has_trained_decision,
691            measure_adaptive_work: false,
692            adaptive_calls: 0,
693            adaptive_closure_work: 0,
694            exact_ambig_detection: false,
695            full_context_memo: HashMap::default(),
696            full_context_memo_len: 0,
697            full_context_memo_gate: None,
698        }
699    }
700
701    pub fn decision_dfas(&self) -> &[ParserDfa] {
702        &self.store.decision_to_dfa
703    }
704
705    /// Returns adaptive-call and closure-work counters for stable decisions.
706    ///
707    /// A call contributes only when its decision DFA was already non-empty and
708    /// did not learn states, edges, or start mappings during the call. This
709    /// excludes both first and incremental population from steady-state work.
710    #[doc(hidden)]
711    pub const fn adaptive_prediction_work(&self) -> Option<(usize, usize)> {
712        if self.has_trained_decision {
713            Some((self.adaptive_calls, self.adaptive_closure_work))
714        } else {
715            None
716        }
717    }
718
719    /// Reports whether the adaptive-prediction work between two snapshots is
720    /// expensive enough to justify trying an ATN-recognizer route.
721    #[doc(hidden)]
722    pub const fn adaptive_prediction_delta_is_expensive(
723        before: (usize, usize),
724        after: (usize, usize),
725    ) -> bool {
726        adaptive_prediction_has_work_density(
727            after.0.saturating_sub(before.0),
728            after.1.saturating_sub(before.1),
729            ADAPTIVE_ATN_PREFERENCE_MIN_CLOSURE_WORK_PER_CALL,
730        )
731    }
732
733    /// Reports whether a partial adaptive-prediction window has enough work
734    /// density to justify abandoning generated parsing before the enclosing
735    /// rule invocation completes.
736    #[doc(hidden)]
737    pub const fn adaptive_prediction_delta_is_decisive(
738        before: (usize, usize),
739        after: (usize, usize),
740    ) -> bool {
741        adaptive_prediction_has_work_density(
742            after.0.saturating_sub(before.0),
743            after.1.saturating_sub(before.1),
744            ADAPTIVE_ATN_PREFERENCE_DECISIVE_CLOSURE_WORK_PER_CALL,
745        )
746    }
747
748    /// Returns aggregate learned parser-DFA storage and interning measurements.
749    pub fn parser_dfa_stats(&self) -> ParserDfaStats {
750        let mut stats = ParserDfaStats::default();
751        for dfa in &self.store.decision_to_dfa {
752            stats.add_assign(dfa.stats());
753        }
754        stats
755    }
756
757    /// Returns compact prediction-context allocation and interning totals for
758    /// this simulator's learned store.
759    pub fn prediction_context_stats(&self) -> PredictionContextStats {
760        let mut stats = self.store.contexts.stats();
761        stats.retained_bytes += self.workspace.retained_bytes();
762        stats.workspace_merge_cache_entries = self.workspace.merge_cache_len();
763        stats.workspace_merge_cache_capacity = self.workspace.merge_cache_capacity();
764        stats.workspace_entry_capacity = self.workspace.entry_capacity();
765        stats.outer_context_cache_hits = self.outer_context_cache_hits;
766        stats.outer_context_cache_misses = self.outer_context_cache_misses;
767        stats
768    }
769
770    /// Interns a generated parser's outer call stack in this simulator's
771    /// context arena. Return states must be supplied outermost to innermost,
772    /// and `rule_context_version` must change whenever that stack changes.
773    pub fn intern_prediction_context(
774        &mut self,
775        rule_context_version: usize,
776        return_states: impl IntoIterator<Item = usize>,
777    ) -> ContextId {
778        if let Some(cached) = self.outer_context_cache
779            && cached.rule_context_version == rule_context_version
780        {
781            self.outer_context_cache_hits = self.outer_context_cache_hits.saturating_add(1);
782            return cached.context;
783        }
784        self.outer_context_cache_misses = self.outer_context_cache_misses.saturating_add(1);
785        let mut context = EMPTY_CONTEXT;
786        for return_state in return_states {
787            context = self.store.contexts.singleton(context, return_state);
788        }
789        self.outer_context_cache = Some(CachedOuterContext {
790            rule_context_version,
791            context,
792        });
793        context
794    }
795
796    pub fn adaptive_predict(
797        &mut self,
798        decision: usize,
799        lookahead: impl IntoIterator<Item = i32>,
800    ) -> Result<usize, ParserAtnSimulatorError> {
801        self.adaptive_predict_with_precedence(decision, 0, lookahead)
802    }
803
804    pub fn adaptive_predict_stream<T: IntStream>(
805        &mut self,
806        decision: usize,
807        input: &mut T,
808    ) -> Result<usize, ParserAtnSimulatorError> {
809        self.adaptive_predict_stream_with_precedence(decision, 0, input)
810    }
811
812    pub fn adaptive_predict_stream_with_precedence<T: IntStream>(
813        &mut self,
814        decision: usize,
815        precedence: usize,
816        input: &mut T,
817    ) -> Result<usize, ParserAtnSimulatorError> {
818        self.adaptive_predict_stream_info_with_precedence(decision, precedence, input)
819            .map(|prediction| prediction.alt)
820    }
821
822    pub fn adaptive_predict_stream_info_with_precedence<T: IntStream>(
823        &mut self,
824        decision: usize,
825        precedence: usize,
826        input: &mut T,
827    ) -> Result<ParserAtnPrediction, ParserAtnSimulatorError> {
828        let marker = input.mark();
829        let index = input.index();
830        let mut workspace = std::mem::take(&mut self.workspace);
831        workspace.reset();
832        let result = self.adaptive_predict_stream_inner(
833            AdaptivePredictRequest {
834                decision,
835                precedence,
836                outer_context: EMPTY_CONTEXT,
837                force_full_context_retry: false,
838                sll_probe_only: false,
839            },
840            input,
841            &mut workspace,
842        );
843        self.workspace = workspace;
844        input.seek(index);
845        input.release(marker);
846        result
847    }
848
849    /// SLL-probe variant of [`Self::adaptive_predict_stream_info_with_precedence`].
850    ///
851    /// Identical to the precedence entry except that, when the SLL walk reaches
852    /// a conflict state requiring full context, it returns the SLL prediction
853    /// (carrying `requires_full_context = true`) WITHOUT running the
854    /// full-context LL loop. The generated two-stage prediction calls this for
855    /// stage 1 and only consults `requires_full_context` to decide whether to
856    /// re-run with the real outer context, so the empty-context LL pass this
857    /// skips would be discarded anyway. Avoids the double LL pass per escalation.
858    pub fn adaptive_predict_stream_info_sll_probe<T: IntStream>(
859        &mut self,
860        decision: usize,
861        precedence: usize,
862        input: &mut T,
863    ) -> Result<ParserAtnPrediction, ParserAtnSimulatorError> {
864        let marker = input.mark();
865        let index = input.index();
866        let mut workspace = std::mem::take(&mut self.workspace);
867        workspace.reset();
868        let result = self.adaptive_predict_stream_inner(
869            AdaptivePredictRequest {
870                decision,
871                precedence,
872                outer_context: EMPTY_CONTEXT,
873                force_full_context_retry: false,
874                sll_probe_only: true,
875            },
876            input,
877            &mut workspace,
878        );
879        self.workspace = workspace;
880        input.seek(index);
881        input.release(marker);
882        result
883    }
884
885    pub fn adaptive_predict_stream_info_with_context<T: IntStream>(
886        &mut self,
887        decision: usize,
888        precedence: usize,
889        input: &mut T,
890        outer_context: ContextId,
891    ) -> Result<ParserAtnPrediction, ParserAtnSimulatorError> {
892        self.store.contexts.assert_valid(outer_context);
893        let marker = input.mark();
894        let index = input.index();
895        let mut workspace = std::mem::take(&mut self.workspace);
896        workspace.reset();
897        let result = self.adaptive_predict_stream_inner(
898            AdaptivePredictRequest {
899                decision,
900                precedence,
901                outer_context,
902                force_full_context_retry: true,
903                sll_probe_only: false,
904            },
905            input,
906            &mut workspace,
907        );
908        self.workspace = workspace;
909        input.seek(index);
910        input.release(marker);
911        result
912    }
913
914    pub fn adaptive_predict_with_precedence(
915        &mut self,
916        decision: usize,
917        precedence: usize,
918        lookahead: impl IntoIterator<Item = i32>,
919    ) -> Result<usize, ParserAtnSimulatorError> {
920        self.adaptive_predict_info_with_precedence(decision, precedence, lookahead)
921            .map(|prediction| prediction.alt)
922    }
923
924    pub fn adaptive_predict_info_with_precedence(
925        &mut self,
926        decision: usize,
927        precedence: usize,
928        lookahead: impl IntoIterator<Item = i32>,
929    ) -> Result<ParserAtnPrediction, ParserAtnSimulatorError> {
930        let mut input = LookaheadIntStream::new(lookahead.into_iter().collect());
931        self.adaptive_predict_stream_info_with_precedence(decision, precedence, &mut input)
932    }
933
934    fn adaptive_predict_stream_inner<T: IntStream>(
935        &mut self,
936        request: AdaptivePredictRequest,
937        input: &mut T,
938        merge_cache: &mut PredictionWorkspace,
939    ) -> Result<ParserAtnPrediction, ParserAtnSimulatorError> {
940        let decision = request.decision;
941        let learning_revision = self
942            .store
943            .decision_to_dfa
944            .get(decision)
945            .filter(|dfa| !dfa.is_empty())
946            .map(ParserDfa::learning_revision);
947        let work_start = (self.adaptive_calls, self.adaptive_closure_work);
948        self.measure_adaptive_work = learning_revision.is_some();
949        if self.measure_adaptive_work {
950            self.adaptive_calls = self.adaptive_calls.saturating_add(1);
951        }
952        let result = self.adaptive_predict_stream_inner_impl(request, input, merge_cache);
953        self.measure_adaptive_work = false;
954        if let Some(learning_revision) = learning_revision
955            && self
956                .store
957                .decision_to_dfa
958                .get(decision)
959                .map(ParserDfa::learning_revision)
960                != Some(learning_revision)
961        {
962            (self.adaptive_calls, self.adaptive_closure_work) = work_start;
963        }
964        self.has_trained_decision |= self
965            .store
966            .decision_to_dfa
967            .get(decision)
968            .is_some_and(|dfa| !dfa.is_empty());
969        result
970    }
971
972    fn adaptive_predict_stream_inner_impl<T: IntStream>(
973        &mut self,
974        request: AdaptivePredictRequest,
975        input: &mut T,
976        merge_cache: &mut PredictionWorkspace,
977    ) -> Result<ParserAtnPrediction, ParserAtnSimulatorError> {
978        let AdaptivePredictRequest {
979            decision,
980            precedence,
981            outer_context,
982            force_full_context_retry,
983            sll_probe_only,
984        } = request;
985        self.deferred_accept_states
986            .retain(|(stored_decision, _)| *stored_decision != decision);
987        #[cfg(feature = "perf-counters")]
988        crate::perf::record_adaptive_call(decision, force_full_context_retry);
989        let Some(decision_state) = self.atn.decision_to_state().get(decision) else {
990            return Err(ParserAtnSimulatorError::UnknownDecision(decision));
991        };
992        let start_index = input.index();
993        // Precedence originates from the parser's precedence stack (rule nesting
994        // depth), so it is always small in practice. A value above `i32::MAX`
995        // would be clamped here; the clamp only ever affects pathological inputs
996        // and at worst over-filters precedence transitions, never miscomputing a
997        // real parse.
998        let precedence = i32::try_from(precedence).unwrap_or(i32::MAX);
999        let mut state_number =
1000            self.ensure_start_state(decision, decision_state, precedence, merge_cache)?;
1001        // The direct interpreter API can continue past a completed prefix, but
1002        // generated parsers retain standard SLL early termination.
1003        let track_previous_good_alt = !force_full_context_retry && !sll_probe_only;
1004        let mut previous_good_alt = None;
1005        if let Some(prediction) = self.prediction_or_full_context(
1006            input,
1007            PredictionCheck {
1008                decision,
1009                decision_state,
1010                state_number,
1011                start_index,
1012                precedence,
1013                outer_context,
1014                force_full_context_retry,
1015                sll_probe_only,
1016            },
1017            merge_cache,
1018        )? {
1019            return Ok(prediction);
1020        }
1021        loop {
1022            if track_previous_good_alt {
1023                let finished = self
1024                    .store
1025                    .decision_to_dfa
1026                    .get(decision)
1027                    .map(|dfa| dfa.configs(state_number))
1028                    .and_then(|configs| self.previous_good_alt(configs));
1029                if finished.is_some() {
1030                    previous_good_alt = finished;
1031                }
1032            }
1033            let symbol = input.la(1);
1034            let target = self
1035                .store
1036                .decision_to_dfa
1037                .get(decision)
1038                .and_then(|dfa| dfa.edge(state_number, symbol));
1039            #[cfg(feature = "perf-counters")]
1040            crate::perf::record_dfa_edge_lookup(target.is_some());
1041            if let Some(target) = target {
1042                state_number = target;
1043            } else {
1044                let configs = self
1045                    .store
1046                    .decision_to_dfa
1047                    .get(decision)
1048                    .map(|dfa| dfa.configs(state_number).clone())
1049                    .ok_or(ParserAtnSimulatorError::MissingDfaState(state_number))?;
1050                let edge = DfaEdge {
1051                    decision,
1052                    source_state: state_number,
1053                };
1054                let target = match self.compute_target_state(
1055                    edge,
1056                    &configs,
1057                    symbol,
1058                    precedence,
1059                    merge_cache,
1060                ) {
1061                    Ok(target) => target,
1062                    Err(ParserAtnSimulatorError::NoViableAlt { symbol, .. }) => {
1063                        if let Some(fallback) = previous_good_alt.as_ref() {
1064                            self.add_previous_good_alt_target(edge, symbol, fallback, merge_cache)
1065                        } else {
1066                            return Err(ParserAtnSimulatorError::NoViableAlt {
1067                                symbol,
1068                                index: input.index(),
1069                            });
1070                        }
1071                    }
1072                    Err(error) => return Err(error),
1073                };
1074                state_number = target;
1075            }
1076            if let Some(prediction) = self.prediction_or_full_context(
1077                input,
1078                PredictionCheck {
1079                    decision,
1080                    decision_state,
1081                    state_number,
1082                    start_index,
1083                    precedence,
1084                    outer_context,
1085                    force_full_context_retry,
1086                    sll_probe_only,
1087                },
1088                merge_cache,
1089            )? {
1090                let defer_unique = track_previous_good_alt
1091                    && previous_good_alt.is_some()
1092                    && !prediction.requires_full_context
1093                    && !self.prediction_reached_decision_entry_rule_stop(
1094                        DfaEdge {
1095                            decision,
1096                            source_state: state_number,
1097                        },
1098                        prediction.alt,
1099                        precedence,
1100                        symbol,
1101                        merge_cache,
1102                    );
1103                if !defer_unique {
1104                    return Ok(prediction);
1105                }
1106                self.deferred_accept_states.insert((decision, state_number));
1107            }
1108            if symbol == TOKEN_EOF {
1109                // We ran out of input while still inside the decision and the
1110                // current state is not a clean accept. ANTLR's execATN takes one
1111                // more step on EOF, reaches an empty reach set, and falls back to
1112                // getSynValidOrSemInvalidAltThatFinishedDecisionEntryRule: any alt
1113                // whose configs already reached the decision's rule-stop (i.e. an
1114                // exit alt of a `(...)*`/`(...)+`/precedence loop) is a valid
1115                // prediction, not a syntax error. Mirror that fallback here so we
1116                // exit the loop cleanly instead of reporting a spurious
1117                // "no viable alternative at input '<EOF>'".
1118                if let Some(configs) = self
1119                    .store
1120                    .decision_to_dfa
1121                    .get(decision)
1122                    .map(|dfa| dfa.configs(state_number).clone())
1123                    && let Some(alt) = self.alt_that_finished_decision_entry_rule(&configs)
1124                {
1125                    return Ok(ParserAtnPrediction {
1126                        alt,
1127                        requires_full_context: false,
1128                        has_semantic_context: configs_have_semantic_context_for_alt(&configs, alt),
1129                        diagnostic: None,
1130                    });
1131                }
1132                return Err(ParserAtnSimulatorError::PredictionRequiresMoreLookahead);
1133            }
1134            input.consume();
1135        }
1136    }
1137
1138    fn prediction_or_full_context<T: IntStream>(
1139        &mut self,
1140        input: &mut T,
1141        check: PredictionCheck,
1142        merge_cache: &mut PredictionWorkspace,
1143    ) -> Result<Option<ParserAtnPrediction>, ParserAtnSimulatorError> {
1144        let PredictionCheck {
1145            decision,
1146            decision_state,
1147            state_number,
1148            start_index,
1149            precedence,
1150            outer_context,
1151            force_full_context_retry,
1152            sll_probe_only,
1153        } = check;
1154        if self.store.contexts.is_empty(outer_context)
1155            && let Some(prediction) =
1156                self.non_greedy_exit_prediction(decision, decision_state, state_number)
1157        {
1158            return Ok(Some(prediction));
1159        }
1160        let Some(info) = self.dfa_prediction_info(decision, state_number) else {
1161            return Ok(None);
1162        };
1163        let prediction = info.prediction;
1164        // SLL-probe stage: the caller only needs to know that this conflict
1165        // requires full context; it will re-run with the real outer context.
1166        // Returning the SLL prediction here (with requires_full_context set)
1167        // avoids running the full-context LL loop with the empty probe context,
1168        // whose result the generated two-stage code discards. Mirrors Go's
1169        // execATN, which signals "needs LL" instead of computing LL twice.
1170        if sll_probe_only && prediction.requires_full_context {
1171            return Ok(Some(prediction));
1172        }
1173        if prediction.requires_full_context
1174            && (force_full_context_retry || !prediction.has_semantic_context)
1175        {
1176            #[cfg(feature = "perf-counters")]
1177            crate::perf::record_full_context_retry(decision);
1178            let sll_stop_index = input.index();
1179            input.seek(start_index);
1180            let memo_allowed = self.full_context_memo_allowed();
1181            let memo_key = FullContextMemoKey {
1182                decision,
1183                precedence,
1184                outer_context,
1185                first_symbol: 0,
1186            };
1187            // A memo hit leaves the cursor at the replayed stop index —
1188            // exactly where the fresh LL loop below would have left it.
1189            if memo_allowed
1190                && let Some(full_context) = self.probe_full_context_memo(memo_key, input)
1191            {
1192                #[cfg(feature = "perf-counters")]
1193                crate::perf::record_full_context_memo_hit(decision);
1194                return Ok(Some(Self::full_context_retry_prediction(
1195                    full_context,
1196                    info.conflicting_alts,
1197                    start_index,
1198                    sll_stop_index,
1199                )));
1200            }
1201            let full_context = self.adaptive_predict_full_context(
1202                decision_state,
1203                input,
1204                precedence,
1205                outer_context,
1206                merge_cache,
1207            )?;
1208            if memo_allowed {
1209                self.record_full_context_memo(memo_key, start_index, input, &full_context);
1210            }
1211            return Ok(Some(Self::full_context_retry_prediction(
1212                full_context,
1213                info.conflicting_alts,
1214                start_index,
1215                sll_stop_index,
1216            )));
1217        }
1218        Ok(Some(prediction))
1219    }
1220
1221    /// Builds the prediction (and diagnostic) a full-context retry reports,
1222    /// shared by the fresh LL run and the memoized replay so both produce
1223    /// byte-identical diagnostics.
1224    fn full_context_retry_prediction(
1225        full_context: FullContextPrediction,
1226        sll_conflicting_alts: Vec<usize>,
1227        start_index: usize,
1228        sll_stop_index: usize,
1229    ) -> ParserAtnPrediction {
1230        let (kind, exact, conflicting_alts) = match full_context.resolution {
1231            FullContextResolution::Ambiguous { exact, ref alts } => (
1232                ParserAtnPredictionDiagnosticKind::Ambiguity,
1233                exact,
1234                alts.clone(),
1235            ),
1236            // A unique full-context alt after an SLL conflict is Java's
1237            // reportContextSensitivity; the SLL state's conflicting alts
1238            // describe the conflict that forced the retry.
1239            FullContextResolution::Unique => (
1240                ParserAtnPredictionDiagnosticKind::ContextSensitivity,
1241                false,
1242                sll_conflicting_alts,
1243            ),
1244        };
1245        let mut prediction = full_context.prediction;
1246        if conflicting_alts.len() > 1 {
1247            prediction.diagnostic = Some(ParserAtnPredictionDiagnostic {
1248                kind,
1249                start_index,
1250                sll_stop_index,
1251                ll_stop_index: full_context.stop_index,
1252                conflicting_alts,
1253                exact,
1254            });
1255        }
1256        prediction
1257    }
1258
1259    /// Whether full-context memoization is sound for this ATN.
1260    ///
1261    /// Predicates make prediction outcomes depend on caller-side evaluation
1262    /// state, so any predicate transition disables the memo. Action and
1263    /// precedence transitions do NOT: upstream `ActionTransition.isEpsilon()`
1264    /// is true ("we are to be ignored by analysis 'cept for predicates") and
1265    /// never contributes to an LL outcome, and a precedence transition in
1266    /// full-context mode resolves immediately against the passed precedence
1267    /// (see the `!full_context` guard in `epsilon_target_config`) — which is
1268    /// already part of the memo key. Gating on all semantic transitions would
1269    /// turn the memo off for every grammar with a left-recursive rule.
1270    ///
1271    /// Exact-ambiguity detection changes how far the LL loop consumes, so
1272    /// entries recorded under one mode must not replay under the other —
1273    /// rather than key the mode, the memo simply stays off in the diagnostic
1274    /// mode.
1275    fn full_context_memo_allowed(&mut self) -> bool {
1276        if self.exact_ambig_detection {
1277            return false;
1278        }
1279        let atn = self.atn;
1280        *self
1281            .full_context_memo_gate
1282            .get_or_insert_with(|| !atn_has_predicate_transition(atn))
1283    }
1284
1285    /// Returns the memoized LL resolution whose recorded token window matches
1286    /// the upcoming input exactly, if any. The caller must have positioned
1287    /// `input` at the decision's start index.
1288    ///
1289    /// The compare walks the stream exactly as the recorded LL loop did
1290    /// (`la(1)` at each cursor position, `consume` between), so a hit is
1291    /// literally the same computation replayed: same decision, precedence,
1292    /// interned caller context, and token sequence. On a hit the cursor is
1293    /// left at the replayed stop index — where the fresh LL loop would have
1294    /// left it; on a miss it is restored to the start.
1295    fn probe_full_context_memo<T: IntStream>(
1296        &self,
1297        mut key: FullContextMemoKey,
1298        input: &mut T,
1299    ) -> Option<FullContextPrediction> {
1300        if self.full_context_memo.is_empty() {
1301            return None;
1302        }
1303        let start_index = input.index();
1304        key.first_symbol = input.la(1);
1305        let entries = self.full_context_memo.get(&key)?;
1306        // For the keyword-vs-identifier ambiguity shape, occurrences of the
1307        // same keyword share `first_symbol`, so a hot decision's entries can
1308        // funnel into one bucket — distinguished only by their windows. The
1309        // scan is first-match-wins, which is correct because windows under a
1310        // key are prefix-free: the LL loop's stop position is a function of
1311        // key + consumed prefix, so no recorded window can be a strict prefix
1312        // of another. Each rejected candidate costs its matched-prefix length
1313        // in `consume`/`la` calls before the cursor restore; the global entry
1314        // cap bounds the worst case.
1315        'candidates: for entry in entries {
1316            for &expected in &entry.window_tail {
1317                input.consume();
1318                if input.la(1) != expected {
1319                    input.seek(start_index);
1320                    continue 'candidates;
1321                }
1322            }
1323            let mut replay = entry.prediction.clone();
1324            replay.stop_index = input.index();
1325            return Some(replay);
1326        }
1327        None
1328    }
1329
1330    /// Records a fresh full-context resolution for later replay.
1331    ///
1332    /// The recorded window re-walks the visible tokens the LL loop read
1333    /// (`start_index..=stop_index` in cursor positions); windows longer than
1334    /// [`FULL_CONTEXT_MEMO_MAX_WINDOW`] are skipped, as is everything once
1335    /// the memo holds [`FULL_CONTEXT_MEMO_MAX_ENTRIES`].
1336    fn record_full_context_memo<T: IntStream>(
1337        &mut self,
1338        mut key: FullContextMemoKey,
1339        start_index: usize,
1340        input: &mut T,
1341        full_context: &FullContextPrediction,
1342    ) {
1343        if self.full_context_memo_len >= FULL_CONTEXT_MEMO_MAX_ENTRIES {
1344            #[cfg(feature = "perf-counters")]
1345            crate::perf::record_full_context_memo_declined(key.decision);
1346            return;
1347        }
1348        let current = input.index();
1349        input.seek(start_index);
1350        key.first_symbol = input.la(1);
1351        let mut window_tail = Vec::new();
1352        while input.index() < full_context.stop_index
1353            && window_tail.len() < FULL_CONTEXT_MEMO_MAX_WINDOW
1354        {
1355            input.consume();
1356            window_tail.push(input.la(1));
1357        }
1358        let complete = input.index() >= full_context.stop_index;
1359        input.seek(current);
1360        if !complete {
1361            #[cfg(feature = "perf-counters")]
1362            crate::perf::record_full_context_memo_declined(key.decision);
1363            return;
1364        }
1365        self.full_context_memo
1366            .entry(key)
1367            .or_default()
1368            .push(FullContextMemoEntry {
1369                window_tail,
1370                prediction: full_context.clone(),
1371            });
1372        self.full_context_memo_len += 1;
1373    }
1374
1375    fn non_greedy_exit_prediction(
1376        &self,
1377        decision: usize,
1378        decision_state: usize,
1379        state_number: DfaStateId,
1380    ) -> Option<ParserAtnPrediction> {
1381        if !self
1382            .atn
1383            .state(decision_state)
1384            .is_some_and(AtnState::non_greedy)
1385        {
1386            return None;
1387        }
1388        let configs = &self
1389            .store
1390            .decision_to_dfa
1391            .get(decision)?
1392            .configs(state_number);
1393        let alt = configs
1394            .configs()
1395            .iter()
1396            .filter(|config| {
1397                self.atn
1398                    .state(config.state)
1399                    .is_some_and(AtnState::is_rule_stop)
1400                    && self.store.contexts.has_empty_path(config.context)
1401            })
1402            .map(|config| config.alt)
1403            .min()?;
1404        Some(ParserAtnPrediction {
1405            alt,
1406            requires_full_context: false,
1407            has_semantic_context: configs_have_semantic_context_for_alt(configs, alt),
1408            diagnostic: None,
1409        })
1410    }
1411
1412    fn ensure_start_state(
1413        &mut self,
1414        decision: usize,
1415        decision_state: usize,
1416        precedence: i32,
1417        merge_cache: &mut PredictionWorkspace,
1418    ) -> Result<DfaStateId, ParserAtnSimulatorError> {
1419        if self.store.decision_to_dfa[decision].is_precedence_dfa() {
1420            let precedence_key = usize::try_from(precedence.max(0)).unwrap_or_default();
1421            if let Some(start) =
1422                self.store.decision_to_dfa[decision].precedence_start_state(precedence_key)
1423            {
1424                return Ok(start);
1425            }
1426        } else if let Some(start) = self.store.decision_to_dfa[decision].start_state() {
1427            return Ok(start);
1428        }
1429        let decision_state = self
1430            .atn
1431            .state(decision_state)
1432            .ok_or(ParserAtnSimulatorError::MissingAtnState(decision_state))?;
1433        let configs = self.compute_start_state(decision_state, precedence, merge_cache);
1434        let state_number = self.add_dfa_state(decision, DfaStateBuilder::new(configs));
1435        if self.store.decision_to_dfa[decision].is_precedence_dfa() {
1436            let precedence_key = usize::try_from(precedence.max(0)).unwrap_or_default();
1437            self.store.decision_to_dfa[decision]
1438                .set_precedence_start_state(precedence_key, state_number);
1439        } else {
1440            self.store.decision_to_dfa[decision].set_start_state(state_number);
1441        }
1442        Ok(state_number)
1443    }
1444
1445    fn add_dfa_state(&mut self, decision: usize, state: DfaStateBuilder) -> DfaStateId {
1446        self.store.decision_to_dfa[decision].add_state(state)
1447    }
1448
1449    fn compute_start_state(
1450        &mut self,
1451        decision_state: AtnState<'_>,
1452        precedence: i32,
1453        merge_cache: &mut PredictionWorkspace,
1454    ) -> AtnConfigSet {
1455        self.compute_start_state_with_context(
1456            decision_state,
1457            false,
1458            EMPTY_CONTEXT,
1459            precedence,
1460            merge_cache,
1461        )
1462    }
1463
1464    fn compute_start_state_with_context(
1465        &mut self,
1466        decision_state: AtnState<'_>,
1467        full_context: bool,
1468        initial_context: ContextId,
1469        precedence: i32,
1470        merge_cache: &mut PredictionWorkspace,
1471    ) -> AtnConfigSet {
1472        let mut configs = AtnConfigSet::new_full_context(full_context);
1473        let mut scratch = ClosureScratch::default();
1474        let params = ClosureParams {
1475            precedence,
1476            collect_predicates: true,
1477            treat_eof_as_epsilon: false,
1478        };
1479        for (index, transition) in decision_state.transitions().iter().enumerate() {
1480            let alt = index + 1;
1481            let config = AtnConfig::new(
1482                transition.target(),
1483                alt,
1484                initial_context,
1485                &self.store.contexts,
1486            );
1487            self.closure(config, &mut configs, merge_cache, &mut scratch, params);
1488        }
1489        configs
1490    }
1491
1492    fn adaptive_predict_full_context<T: IntStream>(
1493        &mut self,
1494        decision_state: usize,
1495        input: &mut T,
1496        precedence: i32,
1497        outer_context: ContextId,
1498        merge_cache: &mut PredictionWorkspace,
1499    ) -> Result<FullContextPrediction, ParserAtnSimulatorError> {
1500        let decision_state = self
1501            .atn
1502            .state(decision_state)
1503            .ok_or(ParserAtnSimulatorError::MissingAtnState(decision_state))?;
1504        let mut configs = self.compute_start_state_with_context(
1505            decision_state,
1506            true,
1507            outer_context,
1508            precedence,
1509            merge_cache,
1510        );
1511        // Java's `execATNWithFullContext`: after each reach set a truly
1512        // unique alt resolves as context sensitivity. Otherwise default LL
1513        // mode stops at the first "resolves to just one viable alt" conflict
1514        // — reported as a NON-exact ambiguity, which the exactOnly listener
1515        // suppresses — while LL_EXACT_AMBIG_DETECTION keeps consuming until
1516        // every (state, context) subset conflicts over the same alt set: an
1517        // exact ambiguity.
1518        loop {
1519            if let Some(alt) = configs.unique_alt() {
1520                return Ok(full_context_prediction(
1521                    alt,
1522                    &configs,
1523                    input.index(),
1524                    FullContextResolution::Unique,
1525                ));
1526            }
1527            let symbol = input.la(1);
1528            let reach = self.compute_reach_set(&configs, symbol, true, precedence, merge_cache);
1529            if reach.is_empty() {
1530                return Err(ParserAtnSimulatorError::NoViableAlt {
1531                    symbol,
1532                    index: input.index(),
1533                });
1534            }
1535            configs = reach;
1536            if let Some(alt) = configs.unique_alt() {
1537                return Ok(full_context_prediction(
1538                    alt,
1539                    &configs,
1540                    input.index(),
1541                    FullContextResolution::Unique,
1542                ));
1543            }
1544            if !configs.has_semantic_context() {
1545                let subsets = conflicting_alt_subsets(configs.configs());
1546                if self.exact_ambig_detection {
1547                    let alts: Vec<usize> = configs.alts().into_iter().collect();
1548                    // Both subset checks hold vacuously for an empty list; a
1549                    // real exact ambiguity always carries alternatives, so
1550                    // guard the pick instead of indexing.
1551                    if all_subsets_conflict(&subsets)
1552                        && all_subsets_equal(&subsets)
1553                        && let Some(&alt) = alts.first()
1554                    {
1555                        return Ok(full_context_prediction(
1556                            alt,
1557                            &configs,
1558                            input.index(),
1559                            FullContextResolution::Ambiguous { exact: true, alts },
1560                        ));
1561                    }
1562                } else if let Some(alt) = single_viable_alt(&subsets) {
1563                    let alts: Vec<usize> = configs.alts().into_iter().collect();
1564                    return Ok(full_context_prediction(
1565                        alt,
1566                        &configs,
1567                        input.index(),
1568                        FullContextResolution::Ambiguous { exact: false, alts },
1569                    ));
1570                }
1571            }
1572            if symbol == TOKEN_EOF || self.configs_all_reached_rule_stop(&configs) {
1573                // Safety net Java reaches implicitly: at EOF every surviving
1574                // path sits in a rule-stop config, so the checks above
1575                // resolve; guard against pathological sets instead of
1576                // spinning on an unconsumable EOF.
1577                let alts: Vec<usize> = configs.alts().into_iter().collect();
1578                let alt = *alts
1579                    .first()
1580                    .ok_or(ParserAtnSimulatorError::PredictionRequiresMoreLookahead)?;
1581                let resolution = if alts.len() > 1 {
1582                    FullContextResolution::Ambiguous {
1583                        exact: self.exact_ambig_detection,
1584                        alts,
1585                    }
1586                } else {
1587                    FullContextResolution::Unique
1588                };
1589                return Ok(full_context_prediction(
1590                    alt,
1591                    &configs,
1592                    input.index(),
1593                    resolution,
1594                ));
1595            }
1596            input.consume();
1597        }
1598    }
1599
1600    fn compute_target_state(
1601        &mut self,
1602        edge: DfaEdge,
1603        configs: &AtnConfigSet,
1604        symbol: i32,
1605        precedence: i32,
1606        merge_cache: &mut PredictionWorkspace,
1607    ) -> Result<DfaStateId, ParserAtnSimulatorError> {
1608        let mut reach = self.compute_reach_set(configs, symbol, false, precedence, merge_cache);
1609        if reach.is_empty() {
1610            if let Some(prediction) = self.alt_that_finished_decision_entry_rule(configs) {
1611                let mut dfa_state = DfaStateBuilder::new(configs.clone());
1612                dfa_state.mark_accept(prediction);
1613                // The set-wide flag gates the per-alt scan: if no config in the
1614                // set carries a semantic context, no alt can either.
1615                dfa_state.set_has_semantic_context_for_alt(
1616                    configs.has_semantic_context()
1617                        && configs_have_semantic_context_for_alt(configs, prediction),
1618                );
1619                let target_state = self.add_dfa_state(edge.decision, dfa_state);
1620                self.store.decision_to_dfa[edge.decision].add_edge(
1621                    edge.source_state,
1622                    symbol,
1623                    target_state,
1624                );
1625                return Ok(target_state);
1626            }
1627            return Err(ParserAtnSimulatorError::NoViableAlt { symbol, index: 0 });
1628        }
1629        let prediction = reach.unique_alt();
1630        let conflict_prediction = prediction.or_else(|| {
1631            if !has_sll_conflict_terminating_prediction(&reach, |state| {
1632                self.atn.state(state).is_some_and(AtnState::is_rule_stop)
1633            }) {
1634                return None;
1635            }
1636            reach
1637                .conflicting_alts()
1638                .into_iter()
1639                .next()
1640                .or_else(|| reach.alts().into_iter().next())
1641        });
1642        let requires_full_context = prediction.is_none() && conflict_prediction.is_some();
1643        #[cfg(feature = "perf-counters")]
1644        if requires_full_context {
1645            crate::perf::record_sll_conflict(edge.decision);
1646        }
1647        let conflicting_alts = if requires_full_context {
1648            let alts = reach.conflicting_alts();
1649            if alts.is_empty() { reach.alts() } else { alts }
1650                .into_iter()
1651                .collect()
1652        } else {
1653            Vec::new()
1654        };
1655        let mut dfa_state = DfaStateBuilder::new(reach);
1656        if let Some(prediction) = conflict_prediction {
1657            dfa_state.mark_accept(prediction);
1658            dfa_state.set_requires_full_context(requires_full_context);
1659            dfa_state.set_conflicting_alts(conflicting_alts);
1660            // The set-wide flag gates the per-alt scan: if no config in the set
1661            // carries a semantic context, no alt can either.
1662            dfa_state.set_has_semantic_context_for_alt(
1663                dfa_state.configs.has_semantic_context()
1664                    && configs_have_semantic_context_for_alt(&dfa_state.configs, prediction),
1665            );
1666        }
1667        let target_state = self.add_dfa_state(edge.decision, dfa_state);
1668        self.store.decision_to_dfa[edge.decision].add_edge(edge.source_state, symbol, target_state);
1669        Ok(target_state)
1670    }
1671
1672    fn compute_reach_set(
1673        &mut self,
1674        configs: &AtnConfigSet,
1675        symbol: i32,
1676        full_context: bool,
1677        precedence: i32,
1678        merge_cache: &mut PredictionWorkspace,
1679    ) -> AtnConfigSet {
1680        let mut intermediate = AtnConfigSet::new_full_context(full_context);
1681        let mut skipped_stop_states = Vec::new();
1682        let max_token_type = self.atn.max_token_type();
1683        for config in configs.configs() {
1684            let Some(state) = self.atn.state(config.state) else {
1685                continue;
1686            };
1687            if state.is_rule_stop() {
1688                if full_context || symbol == TOKEN_EOF {
1689                    skipped_stop_states.push(config.clone());
1690                }
1691                continue;
1692            }
1693            for transition in &state.transitions() {
1694                if transition.matches(symbol, 1, max_token_type) {
1695                    let target =
1696                        config.moved_to(transition.target(), config.context, &self.store.contexts);
1697                    intermediate.add(target, &mut self.store.contexts, merge_cache);
1698                }
1699            }
1700        }
1701        let mut reach = if skipped_stop_states.is_empty() && symbol != TOKEN_EOF {
1702            if intermediate.len() == 1 || intermediate.unique_alt().is_some() {
1703                intermediate
1704            } else {
1705                self.close_intermediate_reach_set(
1706                    intermediate,
1707                    full_context,
1708                    precedence,
1709                    symbol,
1710                    merge_cache,
1711                )
1712            }
1713        } else {
1714            self.close_intermediate_reach_set(
1715                intermediate,
1716                full_context,
1717                precedence,
1718                symbol,
1719                merge_cache,
1720            )
1721        };
1722        if symbol == TOKEN_EOF {
1723            reach = self.rule_stop_configs(reach, merge_cache);
1724        }
1725        if !full_context || !self.configs_contain_rule_stop(&reach) {
1726            for config in skipped_stop_states {
1727                reach.add(config, &mut self.store.contexts, merge_cache);
1728            }
1729        }
1730        #[cfg(feature = "perf-counters")]
1731        crate::perf::record_reach_set(full_context, configs.len(), reach.len());
1732        reach
1733    }
1734
1735    fn close_intermediate_reach_set(
1736        &mut self,
1737        intermediate: AtnConfigSet,
1738        full_context: bool,
1739        precedence: i32,
1740        symbol: i32,
1741        merge_cache: &mut PredictionWorkspace,
1742    ) -> AtnConfigSet {
1743        let mut reach = AtnConfigSet::new_full_context(full_context);
1744        let mut scratch = ClosureScratch::default();
1745        let params = ClosureParams {
1746            precedence,
1747            collect_predicates: false,
1748            treat_eof_as_epsilon: symbol == TOKEN_EOF,
1749        };
1750        // `closure` takes `AtnConfig` by value, so drain the intermediate set by
1751        // move instead of cloning each config.
1752        for config in intermediate.into_configs() {
1753            self.closure(config, &mut reach, merge_cache, &mut scratch, params);
1754        }
1755        reach
1756    }
1757
1758    fn alt_that_finished_decision_entry_rule(&self, configs: &AtnConfigSet) -> Option<usize> {
1759        configs
1760            .configs()
1761            .iter()
1762            .filter(|config| self.config_finished_decision_entry_rule(config))
1763            .map(|config| config.alt)
1764            .min()
1765    }
1766
1767    fn previous_good_alt(&self, configs: &AtnConfigSet) -> Option<PreviousGoodAlt> {
1768        let alt = self.alt_that_finished_decision_entry_rule(configs)?;
1769        let configs = configs
1770            .configs()
1771            .iter()
1772            .filter(|config| config.alt == alt && self.config_finished_decision_entry_rule(config))
1773            .cloned()
1774            .collect();
1775        Some(PreviousGoodAlt { alt, configs })
1776    }
1777
1778    fn config_finished_decision_entry_rule(&self, config: &AtnConfig) -> bool {
1779        config.reaches_into_outer_context > 0
1780            || self
1781                .atn
1782                .state(config.state)
1783                .is_some_and(AtnState::is_rule_stop)
1784                && self.store.contexts.has_empty_path(config.context)
1785    }
1786
1787    fn add_previous_good_alt_target(
1788        &mut self,
1789        edge: DfaEdge,
1790        symbol: i32,
1791        fallback: &PreviousGoodAlt,
1792        merge_cache: &mut PredictionWorkspace,
1793    ) -> DfaStateId {
1794        let mut configs = AtnConfigSet::new();
1795        for config in &fallback.configs {
1796            configs.add(config.clone(), &mut self.store.contexts, merge_cache);
1797        }
1798        let has_semantic_context = configs_have_semantic_context_for_alt(&configs, fallback.alt);
1799        let mut state = DfaStateBuilder::new(configs);
1800        state.mark_accept(fallback.alt);
1801        state.set_has_semantic_context_for_alt(has_semantic_context);
1802        let target = self.add_dfa_state(edge.decision, state);
1803        self.store.decision_to_dfa[edge.decision].add_edge(edge.source_state, symbol, target);
1804        target
1805    }
1806
1807    fn prediction_reached_decision_entry_rule_stop(
1808        &mut self,
1809        edge: DfaEdge,
1810        alt: usize,
1811        precedence: i32,
1812        symbol: i32,
1813        merge_cache: &mut PredictionWorkspace,
1814    ) -> bool {
1815        let configs = self.store.decision_to_dfa[edge.decision]
1816            .configs(edge.source_state)
1817            .clone();
1818        if self.alt_that_finished_decision_entry_rule(&configs) == Some(alt) {
1819            return true;
1820        }
1821        let closed =
1822            self.close_intermediate_reach_set(configs, false, precedence, symbol, merge_cache);
1823        self.alt_that_finished_decision_entry_rule(&closed) == Some(alt)
1824    }
1825
1826    fn rule_stop_configs(
1827        &mut self,
1828        configs: AtnConfigSet,
1829        merge_cache: &mut PredictionWorkspace,
1830    ) -> AtnConfigSet {
1831        if configs.configs().iter().all(|config| {
1832            self.atn
1833                .state(config.state)
1834                .is_some_and(AtnState::is_rule_stop)
1835        }) {
1836            return configs;
1837        }
1838        let mut result = AtnConfigSet::new_full_context(configs.full_context());
1839        for config in configs.configs().iter().filter(|config| {
1840            self.atn
1841                .state(config.state)
1842                .is_some_and(AtnState::is_rule_stop)
1843        }) {
1844            result.add(config.clone(), &mut self.store.contexts, merge_cache);
1845        }
1846        result
1847    }
1848
1849    fn configs_all_reached_rule_stop(&self, configs: &AtnConfigSet) -> bool {
1850        configs.configs().iter().all(|config| {
1851            self.atn
1852                .state(config.state)
1853                .is_some_and(AtnState::is_rule_stop)
1854        })
1855    }
1856
1857    fn configs_contain_rule_stop(&self, configs: &AtnConfigSet) -> bool {
1858        configs.configs().iter().any(|config| {
1859            self.atn
1860                .state(config.state)
1861                .is_some_and(AtnState::is_rule_stop)
1862        })
1863    }
1864
1865    fn closure(
1866        &mut self,
1867        config: AtnConfig,
1868        configs: &mut AtnConfigSet,
1869        merge_cache: &mut PredictionWorkspace,
1870        scratch: &mut ClosureScratch,
1871        params: ClosureParams,
1872    ) {
1873        let ClosureParams {
1874            precedence,
1875            collect_predicates,
1876            treat_eof_as_epsilon,
1877        } = params;
1878        let max_token_type = self.atn.max_token_type();
1879        scratch.stack.clear();
1880        scratch.visited.clear();
1881        scratch.stack.push((config, collect_predicates));
1882        while let Some((config, collect_predicates)) = scratch.stack.pop() {
1883            if !scratch.visited.insert(ClosureConfigKey::from(&config)) {
1884                continue;
1885            }
1886            let Some(state) = self.atn.state(config.state) else {
1887                continue;
1888            };
1889            let at_rule_stop = state.is_rule_stop();
1890            if at_rule_stop
1891                && self.closure_at_rule_stop(
1892                    config.clone(),
1893                    collect_predicates,
1894                    configs,
1895                    merge_cache,
1896                    &mut scratch.stack,
1897                )
1898            {
1899                continue;
1900            }
1901            let epsilon_only = state.epsilon_only();
1902            if !epsilon_only {
1903                configs.add(config.clone(), &mut self.store.contexts, merge_cache);
1904            }
1905            for (index, transition) in state.transitions().iter().enumerate() {
1906                if index == 0
1907                    && can_drop_left_recursive_loop_entry_edge(
1908                        self.atn,
1909                        state,
1910                        &self.store.contexts,
1911                        config.context,
1912                    )
1913                {
1914                    continue;
1915                }
1916                let transition_kind = transition.kind();
1917                if matches!(
1918                    transition_kind,
1919                    ParserTransitionKind::Epsilon
1920                        | ParserTransitionKind::Rule
1921                        | ParserTransitionKind::Predicate
1922                        | ParserTransitionKind::Action
1923                        | ParserTransitionKind::Precedence
1924                ) {
1925                    if let Some(mut target) = self.epsilon_target_config(
1926                        &config,
1927                        transition,
1928                        transition_kind,
1929                        precedence,
1930                        collect_predicates,
1931                        configs.full_context(),
1932                    ) {
1933                        if at_rule_stop {
1934                            target.reaches_into_outer_context =
1935                                target.reaches_into_outer_context.saturating_add(1);
1936                        }
1937                        // ANTLR: stop collecting predicates once an action edge is
1938                        // crossed, so a predicate after an action is deferred to
1939                        // parse time rather than evaluated during prediction.
1940                        let target_collect_predicates =
1941                            collect_predicates && transition_kind != ParserTransitionKind::Action;
1942                        scratch.stack.push((target, target_collect_predicates));
1943                    }
1944                } else if treat_eof_as_epsilon
1945                    && transition.matches_kind(transition_kind, TOKEN_EOF, 1, max_token_type)
1946                {
1947                    scratch.stack.push((
1948                        config.moved_to(transition.target(), config.context, &self.store.contexts),
1949                        collect_predicates,
1950                    ));
1951                }
1952            }
1953        }
1954        let closure_work = scratch.visited.len();
1955        if self.measure_adaptive_work {
1956            self.adaptive_closure_work = self.adaptive_closure_work.saturating_add(closure_work);
1957        }
1958        #[cfg(feature = "perf-counters")]
1959        crate::perf::record_closure(closure_work);
1960    }
1961
1962    fn closure_at_rule_stop(
1963        &mut self,
1964        config: AtnConfig,
1965        collect_predicates: bool,
1966        configs: &mut AtnConfigSet,
1967        merge_cache: &mut PredictionWorkspace,
1968        stack: &mut Vec<(AtnConfig, bool)>,
1969    ) -> bool {
1970        if self.store.contexts.is_empty(config.context) {
1971            if configs.full_context() {
1972                configs.add(config, &mut self.store.contexts, merge_cache);
1973                return true;
1974            }
1975            return false;
1976        }
1977        let mut handled_all_paths = true;
1978        for index in 0..self.store.contexts.len(config.context) {
1979            let Some(return_state) = self.store.contexts.return_state(config.context, index) else {
1980                continue;
1981            };
1982            if return_state == EMPTY_RETURN_STATE {
1983                if configs.full_context() {
1984                    let mut empty_context_config = config.clone();
1985                    empty_context_config.set_context(EMPTY_CONTEXT, &self.store.contexts);
1986                    configs.add(empty_context_config, &mut self.store.contexts, merge_cache);
1987                } else {
1988                    handled_all_paths = false;
1989                }
1990                continue;
1991            }
1992            let parent = self
1993                .store
1994                .contexts
1995                .parent(config.context, index)
1996                .unwrap_or(EMPTY_CONTEXT);
1997            let next = config.moved_to(return_state, parent, &self.store.contexts);
1998            stack.push((next, collect_predicates));
1999        }
2000        handled_all_paths
2001    }
2002
2003    #[allow(clippy::too_many_arguments)]
2004    fn epsilon_target_config(
2005        &mut self,
2006        config: &AtnConfig,
2007        transition: ParserTransition<'_>,
2008        transition_kind: ParserTransitionKind,
2009        precedence: i32,
2010        collect_predicates: bool,
2011        full_context: bool,
2012    ) -> Option<AtnConfig> {
2013        let semantic_context = match transition_kind {
2014            ParserTransitionKind::Predicate if collect_predicates => SemanticContext::and(
2015                config.semantic_context.clone(),
2016                SemanticContext::Predicate {
2017                    rule_index: transition.arg0() as usize,
2018                    pred_index: transition.arg1() as usize,
2019                    context_dependent: transition.arg2() != 0,
2020                },
2021            ),
2022            ParserTransitionKind::Precedence
2023                if collect_predicates
2024                    && i32::from_le_bytes(transition.arg0().to_le_bytes()) < precedence =>
2025            {
2026                return None;
2027            }
2028            ParserTransitionKind::Precedence if collect_predicates && !full_context => {
2029                SemanticContext::and(
2030                    config.semantic_context.clone(),
2031                    SemanticContext::Precedence {
2032                        precedence: i32::from_le_bytes(transition.arg0().to_le_bytes()),
2033                    },
2034                )
2035            }
2036            _ => config.semantic_context.clone(),
2037        };
2038        let context = if transition_kind == ParserTransitionKind::Rule {
2039            self.store
2040                .contexts
2041                .singleton(config.context, transition.arg1() as usize)
2042        } else {
2043            config.context
2044        };
2045        let mut target = config.moved_to(transition.target(), context, &self.store.contexts);
2046        target.semantic_context = semantic_context;
2047        Some(target)
2048    }
2049
2050    fn dfa_prediction_info(
2051        &self,
2052        decision: usize,
2053        state_number: DfaStateId,
2054    ) -> Option<DfaPredictionInfo> {
2055        let dfa = self.store.decision_to_dfa.get(decision)?;
2056        let state = dfa.state(state_number)?;
2057        let alt = state.prediction()?;
2058        let requires_full_context = state.requires_full_context();
2059        let conflicting_alts = if requires_full_context {
2060            let stored = dfa.conflicting_alts(state_number);
2061            if stored.is_empty() {
2062                dfa.configs(state_number).alts().into_iter().collect()
2063            } else {
2064                stored.to_vec()
2065            }
2066        } else {
2067            Vec::new()
2068        };
2069        Some(DfaPredictionInfo {
2070            prediction: ParserAtnPrediction {
2071                alt,
2072                requires_full_context,
2073                // Precomputed at accept time (see compute_target_state) so
2074                // warm accept lookup does not rescan the cold config set.
2075                has_semantic_context: state.has_semantic_context(),
2076                diagnostic: None,
2077            },
2078            conflicting_alts,
2079        })
2080    }
2081}
2082
2083/// Reports whether closure should skip the loop-entry branch for a
2084/// left-recursive rule under the current caller context.
2085pub(crate) fn can_drop_left_recursive_loop_entry_edge(
2086    atn: &Atn,
2087    state: AtnState<'_>,
2088    contexts: &ContextArena,
2089    context: ContextId,
2090) -> bool {
2091    if state.kind() != AtnStateKind::StarLoopEntry
2092        || !state.precedence_rule_decision()
2093        || contexts.is_empty(context)
2094        || contexts.has_empty_path(context)
2095    {
2096        return false;
2097    }
2098    let Some(rule_index) = state.rule_index() else {
2099        return false;
2100    };
2101    for index in 0..contexts.len(context) {
2102        let Some(return_state_number) = contexts.return_state(context, index) else {
2103            return false;
2104        };
2105        let Some(return_state) = atn.state(return_state_number) else {
2106            return false;
2107        };
2108        if return_state.rule_index() != Some(rule_index) {
2109            return false;
2110        }
2111    }
2112    let Some(block_end_state_number) = state
2113        .transitions()
2114        .first()
2115        .and_then(|transition| atn.state(transition.target()))
2116        .and_then(AtnState::end_state)
2117    else {
2118        return false;
2119    };
2120    for index in 0..contexts.len(context) {
2121        let return_state_number = contexts
2122            .return_state(context, index)
2123            .expect("return state checked above");
2124        let return_state = atn
2125            .state(return_state_number)
2126            .expect("return state checked above");
2127        if return_state.state_number() == block_end_state_number {
2128            continue;
2129        }
2130        if return_state.transitions().len() != 1
2131            || !return_state
2132                .transitions()
2133                .first()
2134                .is_some_and(ParserTransition::is_epsilon)
2135        {
2136            return false;
2137        }
2138        let return_target = return_state
2139            .transitions()
2140            .first()
2141            .expect("single transition checked above")
2142            .target();
2143        if return_state.kind() == AtnStateKind::BlockEnd && return_target == state.state_number() {
2144            continue;
2145        }
2146        if return_target == block_end_state_number {
2147            continue;
2148        }
2149        let Some(return_target_state) = atn.state(return_target) else {
2150            return false;
2151        };
2152        if return_target_state.kind() == AtnStateKind::BlockEnd
2153            && return_target_state.transitions().len() == 1
2154            && return_target_state
2155                .transitions()
2156                .first()
2157                .is_some_and(ParserTransition::is_epsilon)
2158            && return_target_state
2159                .transitions()
2160                .first()
2161                .is_some_and(|transition| transition.target() == state.state_number())
2162        {
2163            continue;
2164        }
2165        return false;
2166    }
2167    true
2168}
2169
2170fn configs_have_semantic_context_for_alt(configs: &AtnConfigSet, alt: usize) -> bool {
2171    configs
2172        .configs()
2173        .iter()
2174        .any(|config| config.alt == alt && !config.semantic_context.is_none())
2175}
2176
2177#[derive(Clone, Debug, Eq, PartialEq)]
2178pub enum ParserAtnSimulatorError {
2179    MissingAtnState(usize),
2180    MissingDfaState(DfaStateId),
2181    NoViableAlt { symbol: i32, index: usize },
2182    PredictionRequiresMoreLookahead,
2183    UnknownDecision(usize),
2184}
2185
2186/// Java `DFASerializer.getStateString`: `:sN^=>alt` for accept states.
2187fn dfa_state_display(state: ParserDfaStateView<'_>, deferred: bool) -> String {
2188    let mut out = String::new();
2189    let is_accept = state.is_accept_state() && !deferred;
2190    if is_accept {
2191        out.push(':');
2192    }
2193    out.push('s');
2194    out.push_str(&state.id().index().to_string());
2195    if state.requires_full_context() {
2196        out.push('^');
2197    }
2198    if is_accept {
2199        out.push_str("=>");
2200        out.push_str(
2201            &state
2202                .prediction()
2203                .map(|prediction| prediction.to_string())
2204                .unwrap_or_default(),
2205        );
2206    }
2207    out
2208}
2209
2210#[cfg(test)]
2211#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
2212mod tests {
2213    use super::*;
2214    use crate::atn::AtnStateKind;
2215
2216    fn finish_atn(builder: ParserAtnBuilder) -> Atn {
2217        builder.finish().expect("valid packed parser ATN")
2218    }
2219
2220    #[test]
2221    fn union_decision_dfa_preserves_disjoint_coverage() {
2222        fn configs(
2223            atn_state: usize,
2224            arena: &mut ContextArena,
2225            workspace: &mut PredictionWorkspace,
2226        ) -> AtnConfigSet {
2227            let mut set = AtnConfigSet::new();
2228            set.add(
2229                AtnConfig::new(atn_state, 1, EMPTY_CONTEXT, arena),
2230                arena,
2231                workspace,
2232            );
2233            set
2234        }
2235        fn state(
2236            atn_state: usize,
2237            arena: &mut ContextArena,
2238            workspace: &mut PredictionWorkspace,
2239        ) -> DfaStateBuilder {
2240            DfaStateBuilder::new(configs(atn_state, arena, workspace))
2241        }
2242        let mut arena = ContextArena::new();
2243        let mut workspace = PredictionWorkspace::default();
2244
2245        // Two DFAs that evolved independently from the same grammar: equal
2246        // state/edge counts, but disjoint transitions and different state
2247        // numbering for the shared successor.
2248        let mut shared = ParserDfa::with_max_token_type(0, 0, 8);
2249        let shared_root = shared.add_state(state(10, &mut arena, &mut workspace));
2250        let shared_a = shared.add_state(state(11, &mut arena, &mut workspace));
2251        shared.add_edge(shared_root, 1, shared_a);
2252        shared.set_start_state(shared_root);
2253
2254        let mut local = ParserDfa::with_max_token_type(0, 0, 8);
2255        let local_b = local.add_state(state(12, &mut arena, &mut workspace));
2256        let local_root = local.add_state(state(10, &mut arena, &mut workspace));
2257        local.add_edge(local_root, 2, local_b);
2258        local.set_precedence_start_state(3, local_root);
2259
2260        union_decision_dfa(&mut shared, local);
2261
2262        // The root (same config set) gained local's edge without losing its
2263        // own, with the target re-keyed into shared numbering.
2264        assert_eq!(shared.edge(shared_root, 1), Some(shared_a));
2265        let merged_b = shared
2266            .state_id_for_configs(&configs(12, &mut arena, &mut workspace))
2267            .expect("local-only state adopted");
2268        assert_eq!(shared.edge(shared_root, 2), Some(merged_b));
2269        assert_eq!(shared.states().len(), 3);
2270        // Start-state gaps fill from local; incumbents are kept.
2271        assert_eq!(shared.start_state(), Some(shared_root));
2272        assert_eq!(shared.precedence_start_state(3), Some(shared_root));
2273    }
2274
2275    #[test]
2276    fn union_prediction_stores_remaps_context_ids_before_dfa_union() {
2277        let atn = two_token_decision_atn();
2278        let mut shared = PredictionStore::new(&atn);
2279        let mut local = PredictionStore::new(&atn);
2280        let mut workspace = PredictionWorkspace::default();
2281
2282        let distracting = shared.contexts.singleton(EMPTY_CONTEXT, 99);
2283        let local_context = local.contexts.singleton(EMPTY_CONTEXT, 7);
2284        assert_eq!(distracting, local_context, "both stores allocate ID 1");
2285
2286        let mut configs = AtnConfigSet::new();
2287        configs.add(
2288            AtnConfig::new(42, 1, local_context, &local.contexts),
2289            &mut local.contexts,
2290            &mut workspace,
2291        );
2292        local.decision_to_dfa[0].add_state(DfaStateBuilder::new(configs));
2293
2294        union_prediction_stores(&mut shared, local, &mut workspace);
2295
2296        let imported = shared.decision_to_dfa[0]
2297            .states()
2298            .flat_map(|state| shared.decision_to_dfa[0].configs(state.id()).configs())
2299            .find(|config| config.state == 42)
2300            .expect("local DFA config imported");
2301        assert_ne!(imported.context, local_context);
2302        assert_eq!(shared.contexts.return_state(imported.context, 0), Some(7));
2303        imported.assert_store(&shared.contexts);
2304    }
2305
2306    #[test]
2307    fn outer_context_cache_invalidates_with_rule_context_version() {
2308        let atn = two_token_decision_atn();
2309        let mut simulator = ParserAtnSimulator::new(&atn);
2310
2311        let first = simulator.intern_prediction_context(1, [7]);
2312        let cached = simulator.intern_prediction_context(1, [99]);
2313        let refreshed = simulator.intern_prediction_context(2, [99]);
2314
2315        assert_eq!(cached, first);
2316        assert_ne!(refreshed, first);
2317        assert_eq!(
2318            simulator.store.contexts.return_state(refreshed, 0),
2319            Some(99)
2320        );
2321        let stats = simulator.prediction_context_stats();
2322        assert_eq!(stats.outer_context_cache_hits, 1);
2323        assert_eq!(stats.outer_context_cache_misses, 2);
2324    }
2325
2326    #[test]
2327    fn outer_context_cache_is_simulator_local() {
2328        let atn = two_token_decision_atn();
2329        let mut first = ParserAtnSimulator::new(&atn);
2330        let mut second = ParserAtnSimulator::new(&atn);
2331
2332        let first_context = first.intern_prediction_context(1, [7]);
2333        let second_context = second.intern_prediction_context(1, [99]);
2334
2335        assert_eq!(first.store.contexts.return_state(first_context, 0), Some(7));
2336        assert_eq!(
2337            second.store.contexts.return_state(second_context, 0),
2338            Some(99)
2339        );
2340    }
2341
2342    #[test]
2343    fn adaptive_atn_preference_requires_expensive_prediction_delta() {
2344        assert!(!ParserAtnSimulator::adaptive_prediction_delta_is_expensive(
2345            (0, 0),
2346            (ADAPTIVE_ATN_PREFERENCE_MIN_CALLS - 1, usize::MAX),
2347        ));
2348        assert!(!ParserAtnSimulator::adaptive_prediction_delta_is_expensive(
2349            (5, 7),
2350            (
2351                5 + ADAPTIVE_ATN_PREFERENCE_MIN_CALLS,
2352                7 + ADAPTIVE_ATN_PREFERENCE_MIN_CALLS
2353                    * ADAPTIVE_ATN_PREFERENCE_MIN_CLOSURE_WORK_PER_CALL
2354                    - 1,
2355            ),
2356        ));
2357        assert!(ParserAtnSimulator::adaptive_prediction_delta_is_expensive(
2358            (5, 7),
2359            (
2360                5 + ADAPTIVE_ATN_PREFERENCE_MIN_CALLS,
2361                7 + ADAPTIVE_ATN_PREFERENCE_MIN_CALLS
2362                    * ADAPTIVE_ATN_PREFERENCE_MIN_CLOSURE_WORK_PER_CALL,
2363            ),
2364        ));
2365        assert!(!ParserAtnSimulator::adaptive_prediction_delta_is_decisive(
2366            (5, 7),
2367            (
2368                5 + ADAPTIVE_ATN_PREFERENCE_MIN_CALLS,
2369                7 + ADAPTIVE_ATN_PREFERENCE_MIN_CALLS
2370                    * ADAPTIVE_ATN_PREFERENCE_DECISIVE_CLOSURE_WORK_PER_CALL
2371                    - 1,
2372            ),
2373        ));
2374        assert!(ParserAtnSimulator::adaptive_prediction_delta_is_decisive(
2375            (5, 7),
2376            (
2377                5 + ADAPTIVE_ATN_PREFERENCE_MIN_CALLS,
2378                7 + ADAPTIVE_ATN_PREFERENCE_MIN_CALLS
2379                    * ADAPTIVE_ATN_PREFERENCE_DECISIVE_CLOSURE_WORK_PER_CALL,
2380            ),
2381        ));
2382    }
2383
2384    #[test]
2385    fn adaptive_atn_preference_excludes_first_population_per_decision() {
2386        let atn = two_independent_decisions_atn();
2387        let mut simulator = ParserAtnSimulator::new(&atn);
2388
2389        assert_eq!(simulator.adaptive_prediction_work(), None);
2390        assert_eq!(simulator.adaptive_predict(0, [1, 2]), Ok(1));
2391        let after_first_decision = simulator
2392            .adaptive_prediction_work()
2393            .expect("one decision is trained");
2394        assert_eq!(after_first_decision, (0, 0));
2395
2396        assert_eq!(simulator.adaptive_predict(1, [1, 2]), Ok(1));
2397        assert_eq!(
2398            simulator.adaptive_prediction_work(),
2399            Some(after_first_decision),
2400            "cold work for another decision must not enter the routing counters"
2401        );
2402
2403        assert_eq!(simulator.adaptive_predict(1, [1, 2]), Ok(1));
2404        let after_warm_decision = simulator
2405            .adaptive_prediction_work()
2406            .expect("trained decision work is measurable");
2407        assert_eq!(after_warm_decision.0, after_first_decision.0 + 1);
2408    }
2409
2410    #[test]
2411    fn adaptive_atn_preference_excludes_incremental_population_per_decision() {
2412        let atn = two_token_decision_atn();
2413        let mut simulator = ParserAtnSimulator::new(&atn);
2414
2415        assert_eq!(simulator.adaptive_predict(0, [1, 2]), Ok(1));
2416        let after_first_path = simulator
2417            .adaptive_prediction_work()
2418            .expect("the decision is partially trained");
2419        let transitions_after_first_path = simulator.decision_dfas()[0].stats().transitions;
2420
2421        assert_eq!(simulator.adaptive_predict(0, [1, 3]), Ok(2));
2422        assert!(
2423            simulator.decision_dfas()[0].stats().transitions > transitions_after_first_path,
2424            "the second input must extend the partially populated DFA"
2425        );
2426        assert_eq!(
2427            simulator.adaptive_prediction_work(),
2428            Some(after_first_path),
2429            "incremental DFA construction must not enter the routing counters"
2430        );
2431
2432        assert_eq!(simulator.adaptive_predict(0, [1, 3]), Ok(2));
2433        assert_eq!(
2434            simulator
2435                .adaptive_prediction_work()
2436                .expect("the repeated path is stable")
2437                .0,
2438            after_first_path.0 + 1
2439        );
2440    }
2441
2442    #[test]
2443    fn reset_retains_adaptive_training_and_clear_dfa_cools_it() {
2444        let atn = two_token_decision_atn();
2445        let mut simulator = ParserAtnSimulator::new(&atn);
2446        assert_eq!(simulator.adaptive_prediction_work(), None);
2447        assert_eq!(simulator.adaptive_predict(0, [1, 2]), Ok(1));
2448        assert_eq!(simulator.adaptive_prediction_work(), Some((0, 0)));
2449
2450        simulator.reset();
2451        assert_eq!(simulator.adaptive_calls, 0);
2452        assert_eq!(simulator.adaptive_closure_work, 0);
2453        assert_eq!(simulator.adaptive_prediction_work(), Some((0, 0)));
2454
2455        assert_eq!(simulator.adaptive_predict(0, [1, 2]), Ok(1));
2456        assert_eq!(
2457            simulator
2458                .adaptive_prediction_work()
2459                .expect("warmed counters")
2460                .0,
2461            1
2462        );
2463
2464        simulator.clear_dfa();
2465        assert_eq!(simulator.adaptive_calls, 0);
2466        assert_eq!(simulator.adaptive_closure_work, 0);
2467        assert_eq!(simulator.adaptive_prediction_work(), None);
2468    }
2469
2470    #[test]
2471    fn adaptive_predict_reuses_dense_dfa_edges() {
2472        let atn = two_token_decision_atn();
2473        let mut simulator = ParserAtnSimulator::new(&atn);
2474
2475        assert_eq!(simulator.adaptive_predict(0, [1, 2]), Ok(1));
2476        assert_eq!(simulator.adaptive_predict(0, [1, 3]), Ok(2));
2477
2478        let dfa = &simulator.decision_dfas()[0];
2479        let start = dfa.start_state().expect("start state");
2480        let after_first = dfa.state(start).and_then(|state| state.edge(1));
2481        assert!(after_first.is_some());
2482    }
2483
2484    #[test]
2485    fn shared_simulator_reuses_learned_dfa_states() {
2486        let atn = Box::leak(Box::new(two_token_decision_atn()));
2487        let learned_states = {
2488            let mut simulator = ParserAtnSimulator::new_shared(atn);
2489            assert_eq!(simulator.adaptive_predict(0, [1, 2]), Ok(1));
2490            simulator.decision_dfas()[0].states().len()
2491        };
2492
2493        let simulator = ParserAtnSimulator::new_shared(atn);
2494        assert_eq!(simulator.decision_dfas()[0].states().len(), learned_states);
2495    }
2496
2497    #[test]
2498    fn shared_simulator_preserves_and_clears_prediction_training_state() {
2499        let atn = Box::leak(Box::new(two_token_decision_atn()));
2500        {
2501            let mut simulator = ParserAtnSimulator::new_shared(atn);
2502            assert_eq!(simulator.adaptive_predict(0, [1, 2]), Ok(1));
2503        }
2504
2505        {
2506            let simulator = ParserAtnSimulator::new_shared(atn);
2507            assert_eq!(simulator.adaptive_prediction_work(), Some((0, 0)));
2508        }
2509
2510        ParserAtnSimulator::clear_shared_dfa(atn);
2511        let simulator = ParserAtnSimulator::new_shared(atn);
2512        assert_eq!(simulator.adaptive_prediction_work(), None);
2513    }
2514
2515    #[test]
2516    fn overlapping_shared_simulator_treats_an_empty_store_as_cold() {
2517        let atn = Box::leak(Box::new(two_token_decision_atn()));
2518        {
2519            let mut simulator = ParserAtnSimulator::new_shared(atn);
2520            assert_eq!(simulator.adaptive_predict(0, [1, 2]), Ok(1));
2521        }
2522
2523        let warmed = ParserAtnSimulator::new_shared(atn);
2524        assert_eq!(warmed.adaptive_prediction_work(), Some((0, 0)));
2525        let overlapping = ParserAtnSimulator::new_shared(atn);
2526        assert_eq!(overlapping.adaptive_prediction_work(), None);
2527    }
2528
2529    #[test]
2530    fn clear_shared_dfa_drops_learned_states() {
2531        let atn = Box::leak(Box::new(two_token_decision_atn()));
2532        {
2533            let mut simulator = ParserAtnSimulator::new_shared(atn);
2534            assert_eq!(simulator.adaptive_predict(0, [1, 2]), Ok(1));
2535            assert!(!simulator.decision_dfas()[0].is_empty());
2536        }
2537
2538        ParserAtnSimulator::clear_shared_dfa(atn);
2539
2540        let simulator = ParserAtnSimulator::new_shared(atn);
2541        assert!(simulator.decision_dfas()[0].is_empty());
2542    }
2543
2544    #[test]
2545    fn clear_dfa_rejects_stale_overlapping_simulator_publication() {
2546        let atn = Box::leak(Box::new(two_token_decision_atn()));
2547        let mut current = ParserAtnSimulator::new_shared(atn);
2548        let mut stale = ParserAtnSimulator::new_shared(atn);
2549        assert_eq!(stale.adaptive_predict(0, [1, 2]), Ok(1));
2550        assert!(!stale.decision_dfas()[0].is_empty());
2551
2552        current.clear_dfa();
2553        drop(stale);
2554        drop(current);
2555
2556        let simulator = ParserAtnSimulator::new_shared(atn);
2557        assert!(simulator.decision_dfas()[0].is_empty());
2558    }
2559
2560    #[test]
2561    fn adaptive_predict_reports_no_viable_alt() {
2562        let atn = two_token_decision_atn();
2563        let mut simulator = ParserAtnSimulator::new(&atn);
2564
2565        assert_eq!(
2566            simulator.adaptive_predict(0, [4]),
2567            Err(ParserAtnSimulatorError::NoViableAlt {
2568                symbol: 4,
2569                index: 0
2570            })
2571        );
2572    }
2573
2574    #[test]
2575    fn adaptive_predict_marks_sll_conflict_for_full_context() {
2576        let atn = ambiguous_single_token_decision_atn();
2577        let mut simulator = ParserAtnSimulator::new(&atn);
2578
2579        assert_eq!(simulator.adaptive_predict(0, [1]), Ok(1));
2580        let prediction = simulator
2581            .adaptive_predict_info_with_precedence(0, 0, [1])
2582            .expect("prediction");
2583        insta::assert_debug_snapshot!(
2584            "adaptive_predict_marks_sll_conflict_for_full_context",
2585            prediction
2586        );
2587
2588        let dfa = &simulator.decision_dfas()[0];
2589        let start = dfa.start_state().expect("start state");
2590        let target = dfa
2591            .state(start)
2592            .and_then(|state| state.edge(1))
2593            .expect("edge for token 1");
2594        let state = dfa.state(target).expect("target state");
2595        assert!(state.is_accept_state());
2596        assert!(state.requires_full_context());
2597        assert_eq!(state.prediction(), Some(1));
2598    }
2599
2600    #[test]
2601    fn adaptive_predict_keeps_rule_stop_configs_at_eof() {
2602        let atn = optional_token_decision_atn();
2603        let mut simulator = ParserAtnSimulator::new(&atn);
2604
2605        assert_eq!(simulator.adaptive_predict(0, [TOKEN_EOF]), Ok(2));
2606    }
2607
2608    #[test]
2609    fn adaptive_predict_treats_repeated_eof_as_epsilon_after_first_eof() {
2610        let atn = multiple_eof_decision_atn();
2611        let mut simulator = ParserAtnSimulator::new(&atn);
2612
2613        assert_eq!(simulator.adaptive_predict(0, [1, TOKEN_EOF]), Ok(1));
2614    }
2615
2616    #[test]
2617    fn adaptive_predict_uses_finished_entry_rule_alt_on_error_edge() {
2618        let atn = prefix_alt_decision_atn();
2619        let mut simulator = ParserAtnSimulator::new(&atn);
2620
2621        assert_eq!(simulator.adaptive_predict(0, [1, 3]), Ok(1));
2622    }
2623
2624    #[test]
2625    fn adaptive_predict_keeps_prefix_alt_until_longer_alt_finishes() {
2626        let atn = three_token_prefix_alt_decision_atn();
2627        let mut simulator = ParserAtnSimulator::new(&atn);
2628
2629        assert_eq!(simulator.adaptive_predict(0, [1, 2, TOKEN_EOF]), Ok(1));
2630        assert_eq!(simulator.adaptive_predict(0, [1, 2, 1, TOKEN_EOF]), Ok(2));
2631    }
2632
2633    #[test]
2634    fn sll_probe_keeps_unique_alt_early_termination() {
2635        let atn = three_token_prefix_alt_decision_atn();
2636        let mut simulator = ParserAtnSimulator::new(&atn);
2637        let mut input = VecIntStream::new(vec![1, 2, TOKEN_EOF]);
2638
2639        let prediction = simulator
2640            .adaptive_predict_stream_info_sll_probe(0, 0, &mut input)
2641            .expect("SLL prediction should succeed");
2642
2643        assert_eq!(prediction.alt, 2);
2644    }
2645
2646    #[test]
2647    fn adaptive_predict_uses_precedence_dfa_start_states() {
2648        let atn = two_token_decision_atn_with_precedence(true);
2649        let mut simulator = ParserAtnSimulator::new(&atn);
2650
2651        assert_eq!(
2652            simulator.adaptive_predict_with_precedence(0, 3, [1, 2]),
2653            Ok(1)
2654        );
2655        assert_eq!(
2656            simulator.adaptive_predict_with_precedence(0, 7, [1, 3]),
2657            Ok(2)
2658        );
2659
2660        let dfa = &simulator.decision_dfas()[0];
2661        assert!(dfa.is_precedence_dfa());
2662        assert!(dfa.precedence_start_state(3).is_some());
2663        assert!(dfa.precedence_start_state(7).is_some());
2664    }
2665
2666    #[test]
2667    fn adaptive_predict_stream_restores_input_position() {
2668        let atn = two_token_decision_atn();
2669        let mut simulator = ParserAtnSimulator::new(&atn);
2670        let mut input = VecIntStream::new(vec![1, 3, TOKEN_EOF]);
2671
2672        assert_eq!(simulator.adaptive_predict_stream(0, &mut input), Ok(2));
2673        assert_eq!(input.index(), 0);
2674        assert_eq!(input.la(1), 1);
2675    }
2676
2677    #[test]
2678    fn adaptive_predict_stream_retries_full_context_conflict() {
2679        let atn = ambiguous_single_token_decision_atn();
2680        let mut simulator = ParserAtnSimulator::new(&atn);
2681        let mut input = VecIntStream::new(vec![1, TOKEN_EOF]);
2682
2683        let prediction = simulator
2684            .adaptive_predict_stream_info_with_precedence(0, 0, &mut input)
2685            .expect("prediction");
2686
2687        insta::assert_debug_snapshot!(
2688            "adaptive_predict_stream_retries_full_context_conflict",
2689            prediction
2690        );
2691        assert_eq!(input.index(), 0);
2692    }
2693
2694    #[test]
2695    fn full_context_memo_replays_identical_retries() {
2696        let atn = ambiguous_single_token_decision_atn();
2697        let mut simulator = ParserAtnSimulator::new(&atn);
2698
2699        // First occurrence: SLL conflicts, the LL loop runs and its
2700        // resolution is recorded.
2701        let mut input = VecIntStream::new(vec![1, TOKEN_EOF]);
2702        let fresh = simulator
2703            .adaptive_predict_stream_info_with_context(0, 0, &mut input, EMPTY_CONTEXT)
2704            .expect("fresh prediction");
2705        assert_eq!(simulator.full_context_memo_len, 1);
2706        assert_eq!(input.index(), 0, "cursor restored after prediction");
2707
2708        // Second identical occurrence: the memo replays without running the
2709        // LL loop, producing a byte-identical prediction (diagnostic
2710        // included) and identical cursor behavior.
2711        let replayed = simulator
2712            .adaptive_predict_stream_info_with_context(0, 0, &mut input, EMPTY_CONTEXT)
2713            .expect("memoized prediction");
2714        assert_eq!(replayed, fresh);
2715        assert_eq!(simulator.full_context_memo_len, 1, "no duplicate entry");
2716        assert_eq!(input.index(), 0);
2717
2718        // A different upcoming token sequence misses the memo: a fresh LL
2719        // run happens (and records its own entry) instead of a stale replay.
2720        let mut other_input = VecIntStream::new(vec![2, TOKEN_EOF]);
2721        let other = simulator.adaptive_predict_stream_info_with_context(
2722            0,
2723            0,
2724            &mut other_input,
2725            EMPTY_CONTEXT,
2726        );
2727        // Token 2 has no viable alternative in this ATN — the memo must not
2728        // have answered for it.
2729        assert!(other.is_err(), "different window must not replay");
2730
2731        // A different outer context misses the memo as well.
2732        let context = simulator.store.contexts.singleton(EMPTY_CONTEXT, 6);
2733        let mut input = VecIntStream::new(vec![1, TOKEN_EOF]);
2734        let _ = simulator
2735            .adaptive_predict_stream_info_with_context(0, 0, &mut input, context)
2736            .expect("prediction under a different context");
2737        assert_eq!(
2738            simulator.full_context_memo_len, 2,
2739            "distinct context records its own entry"
2740        );
2741    }
2742
2743    #[test]
2744    fn full_context_memo_walks_multi_token_windows() {
2745        let atn = ambiguous_three_token_decision_atn();
2746        let mut simulator = ParserAtnSimulator::new(&atn);
2747
2748        // Fresh LL run consumes the three-token window before resolving; the
2749        // recorded entry carries a non-empty window tail.
2750        let mut input = VecIntStream::new(vec![1, 2, 3, TOKEN_EOF]);
2751        let fresh = simulator
2752            .adaptive_predict_stream_info_with_context(0, 0, &mut input, EMPTY_CONTEXT)
2753            .expect("fresh prediction");
2754        assert_eq!(simulator.full_context_memo_len, 1);
2755        let recorded_window_len = simulator
2756            .full_context_memo
2757            .values()
2758            .next()
2759            .and_then(|entries| entries.first())
2760            .map(|entry| entry.window_tail.len())
2761            .expect("one recorded entry");
2762        assert!(
2763            recorded_window_len >= 1,
2764            "the LL loop consumed tokens, so the window tail must be non-empty"
2765        );
2766
2767        // Identical occurrence replays through the token-for-token compare,
2768        // byte-identical (stop_index recomputed from the live cursor).
2769        let replayed = simulator
2770            .adaptive_predict_stream_info_with_context(0, 0, &mut input, EMPTY_CONTEXT)
2771            .expect("memoized prediction");
2772        assert_eq!(replayed, fresh);
2773        assert_eq!(input.index(), 0, "cursor restored by the caller wrapper");
2774
2775        // Same first symbol, diverging mid-window: the compare must reject
2776        // the entry and restore the cursor to the decision start before the
2777        // fresh LL run happens. Token 9 has no viable alternative, so a
2778        // (wrong) replay would have returned Ok — the Err proves the miss;
2779        // the memo also records nothing for the failed occurrence.
2780        let mut diverging = VecIntStream::new(vec![1, 9, 9, TOKEN_EOF]);
2781        let result = simulator.adaptive_predict_stream_info_with_context(
2782            0,
2783            0,
2784            &mut diverging,
2785            EMPTY_CONTEXT,
2786        );
2787        assert!(result.is_err(), "mid-window divergence must not replay");
2788        assert_eq!(simulator.full_context_memo_len, 1);
2789    }
2790
2791    #[test]
2792    fn full_context_memo_stays_off_for_predicated_atns_and_exact_mode() {
2793        // Exact-ambiguity detection changes how far the LL loop consumes, so
2794        // the memo must not record or replay in that mode.
2795        let atn = ambiguous_single_token_decision_atn();
2796        let mut simulator = ParserAtnSimulator::new(&atn);
2797        simulator.set_exact_ambig_detection(true);
2798        let mut input = VecIntStream::new(vec![1, TOKEN_EOF]);
2799        let _ = simulator
2800            .adaptive_predict_stream_info_with_context(0, 0, &mut input, EMPTY_CONTEXT)
2801            .expect("prediction");
2802        assert_eq!(simulator.full_context_memo_len, 0);
2803
2804        // Predicates make outcomes depend on caller-side evaluation: any
2805        // semantic transition in the ATN disables the memo entirely.
2806        let mut atn = ParserAtnBuilder::new(1);
2807        add_state(&mut atn, 0, AtnStateKind::Basic);
2808        add_state(&mut atn, 1, AtnStateKind::Basic);
2809        atn.add_transition(
2810            0,
2811            ParserTransitionSpec::Predicate {
2812                target: 1,
2813                rule_index: 0,
2814                pred_index: 0,
2815                context_dependent: false,
2816            },
2817        )
2818        .expect("transition");
2819        atn.set_rule_to_start_state(vec![0])
2820            .expect("rule start states");
2821        atn.set_rule_to_stop_state(vec![1])
2822            .expect("rule stop states");
2823        let atn = finish_atn(atn);
2824        let mut simulator = ParserAtnSimulator::new(&atn);
2825        assert!(!simulator.full_context_memo_allowed());
2826    }
2827
2828    #[test]
2829    fn full_context_memo_allows_action_and_precedence_transitions() {
2830        // Actions never affect prediction (upstream ActionTransition is
2831        // epsilon for analysis), and precedence transitions resolve against
2832        // the precedence already in the memo key — neither disables the memo.
2833        // Gating on them would turn the memo off for every grammar with a
2834        // left-recursive rule.
2835        let mut atn = ParserAtnBuilder::new(1);
2836        add_state(&mut atn, 0, AtnStateKind::Basic);
2837        add_state(&mut atn, 1, AtnStateKind::Basic);
2838        add_state(&mut atn, 2, AtnStateKind::Basic);
2839        atn.add_transition(
2840            0,
2841            ParserTransitionSpec::Action {
2842                target: 1,
2843                rule_index: 0,
2844                action_index: Some(0),
2845                context_dependent: false,
2846            },
2847        )
2848        .expect("transition");
2849        atn.add_transition(
2850            1,
2851            ParserTransitionSpec::Precedence {
2852                target: 2,
2853                precedence: 1,
2854            },
2855        )
2856        .expect("transition");
2857        atn.set_rule_to_start_state(vec![0])
2858            .expect("rule start states");
2859        atn.set_rule_to_stop_state(vec![2])
2860            .expect("rule stop states");
2861        let atn = finish_atn(atn);
2862        let mut simulator = ParserAtnSimulator::new(&atn);
2863        assert!(simulator.full_context_memo_allowed());
2864    }
2865
2866    #[test]
2867    fn context_prediction_reports_context_sensitivity_for_dfa_conflict() {
2868        let atn = two_token_decision_atn();
2869        let mut simulator = ParserAtnSimulator::new(&atn);
2870        let mut workspace = PredictionWorkspace::default();
2871        let mut start_configs = AtnConfigSet::new();
2872        start_configs.add(
2873            AtnConfig::new(2, 1, EMPTY_CONTEXT, &simulator.store.contexts),
2874            &mut simulator.store.contexts,
2875            &mut workspace,
2876        );
2877        let start =
2878            simulator.store.decision_to_dfa[0].add_state(DfaStateBuilder::new(start_configs));
2879        simulator.store.decision_to_dfa[0].set_start_state(start);
2880
2881        let mut accept_configs = AtnConfigSet::new();
2882        accept_configs.add(
2883            AtnConfig::new(3, 1, EMPTY_CONTEXT, &simulator.store.contexts).with_semantic_context(
2884                SemanticContext::Predicate {
2885                    rule_index: 0,
2886                    pred_index: 0,
2887                    context_dependent: false,
2888                },
2889            ),
2890            &mut simulator.store.contexts,
2891            &mut workspace,
2892        );
2893        let mut accept_state = DfaStateBuilder::new(accept_configs);
2894        accept_state.mark_accept(1);
2895        accept_state.set_requires_full_context(true);
2896        accept_state.set_conflicting_alts(vec![1, 2]);
2897        let accept = simulator.store.decision_to_dfa[0].add_state(accept_state);
2898        simulator.store.decision_to_dfa[0].add_edge(start, 1, accept);
2899
2900        let mut input = VecIntStream::new(vec![1, 3, TOKEN_EOF]);
2901        let prediction = simulator
2902            .adaptive_predict_stream_info_with_context(0, 0, &mut input, EMPTY_CONTEXT)
2903            .expect("prediction");
2904
2905        insta::assert_debug_snapshot!(
2906            "context_prediction_reports_context_sensitivity_for_dfa_conflict",
2907            prediction
2908        );
2909        assert_eq!(input.index(), 0);
2910    }
2911
2912    #[test]
2913    fn full_context_reach_prefers_longer_match_over_skipped_stop_state() {
2914        let atn = prefix_alt_decision_atn();
2915        let mut simulator = ParserAtnSimulator::new(&atn);
2916        let mut configs = AtnConfigSet::new_full_context(true);
2917        let mut merge_cache = PredictionWorkspace::default();
2918        configs.add(
2919            AtnConfig::new(2, 1, EMPTY_CONTEXT, &simulator.store.contexts),
2920            &mut simulator.store.contexts,
2921            &mut merge_cache,
2922        );
2923        configs.add(
2924            AtnConfig::new(1, 2, EMPTY_CONTEXT, &simulator.store.contexts),
2925            &mut simulator.store.contexts,
2926            &mut merge_cache,
2927        );
2928
2929        let reach = simulator.compute_reach_set(&configs, 2, true, 0, &mut merge_cache);
2930
2931        assert_eq!(reach.alts(), std::iter::once(2).collect());
2932        assert!(simulator.configs_all_reached_rule_stop(&reach));
2933    }
2934
2935    #[test]
2936    fn sll_closure_follows_empty_context_rule_stop_exits() {
2937        let mut atn = ParserAtnBuilder::new(1);
2938        add_state(&mut atn, 0, AtnStateKind::RuleStop);
2939        add_state(&mut atn, 1, AtnStateKind::Basic);
2940        add_state(&mut atn, 2, AtnStateKind::Basic);
2941        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
2942            .expect("transition");
2943        atn.add_transition(
2944            1,
2945            ParserTransitionSpec::Atom {
2946                target: 2,
2947                label: 1,
2948            },
2949        )
2950        .expect("transition");
2951        atn.set_rule_to_start_state(vec![0])
2952            .expect("rule start states");
2953        atn.set_rule_to_stop_state(vec![0])
2954            .expect("rule stop states");
2955        let atn = finish_atn(atn);
2956
2957        let mut simulator = ParserAtnSimulator::new(&atn);
2958        let mut configs = AtnConfigSet::new_full_context(false);
2959        let mut merge_cache = PredictionWorkspace::default();
2960        let mut scratch = ClosureScratch::default();
2961        let config = AtnConfig::new(0, 2, EMPTY_CONTEXT, &simulator.store.contexts);
2962        simulator.closure(
2963            config,
2964            &mut configs,
2965            &mut merge_cache,
2966            &mut scratch,
2967            ClosureParams {
2968                precedence: 0,
2969                collect_predicates: true,
2970                treat_eof_as_epsilon: false,
2971            },
2972        );
2973
2974        assert_eq!(configs.len(), 1);
2975        let config = &configs.configs()[0];
2976        assert_eq!(config.state, 1);
2977        assert_eq!(config.alt, 2);
2978        assert_eq!(config.reaches_into_outer_context, 1);
2979    }
2980
2981    #[test]
2982    fn precedence_contexts_are_collected_only_for_start_closure() {
2983        let mut atn = ParserAtnBuilder::new(1);
2984        add_state(&mut atn, 0, AtnStateKind::Basic);
2985        add_state(&mut atn, 1, AtnStateKind::Basic);
2986        atn.set_rule_to_start_state(vec![0])
2987            .expect("rule start states");
2988        atn.set_rule_to_stop_state(vec![1])
2989            .expect("rule stop states");
2990        atn.add_transition(
2991            0,
2992            ParserTransitionSpec::Precedence {
2993                target: 1,
2994                precedence: 2,
2995            },
2996        )
2997        .expect("precedence transition");
2998        let atn = finish_atn(atn);
2999        let transition = atn
3000            .state(0)
3001            .expect("source state")
3002            .transitions()
3003            .first()
3004            .expect("precedence transition");
3005        let mut simulator = ParserAtnSimulator::new(&atn);
3006        let config = AtnConfig::new(0, 1, EMPTY_CONTEXT, &simulator.store.contexts);
3007
3008        let sll_start = simulator
3009            .epsilon_target_config(&config, transition, transition.kind(), 1, true, false)
3010            .expect("sll start transition");
3011        assert!(matches!(
3012            sll_start.semantic_context,
3013            SemanticContext::Precedence { precedence: 2 }
3014        ));
3015
3016        let full_context_start = simulator
3017            .epsilon_target_config(&config, transition, transition.kind(), 1, true, true)
3018            .expect("full-context start transition");
3019        assert!(full_context_start.semantic_context.is_none());
3020
3021        let reach = simulator
3022            .epsilon_target_config(&config, transition, transition.kind(), 3, false, false)
3023            .expect("reach transition");
3024        assert!(reach.semantic_context.is_none());
3025
3026        assert!(
3027            simulator
3028                .epsilon_target_config(&config, transition, transition.kind(), 3, true, false)
3029                .is_none()
3030        );
3031    }
3032
3033    #[test]
3034    fn closure_stops_collecting_predicates_after_action_edge() {
3035        // ANTLR's `closure_` sets
3036        // `continueCollecting = collectPredicates && !ActionTransition`, so a
3037        // predicate reached *after* an action edge is NOT folded into the
3038        // config's semantic context — it is deferred to parse time (the
3039        // "action hides predicates" rule). Build `0 -Action-> 1 -Pred-> 2` and
3040        // assert the closure config carries NO semantic context.
3041        let mut atn = ParserAtnBuilder::new(1);
3042        add_state(&mut atn, 0, AtnStateKind::Basic);
3043        add_state(&mut atn, 1, AtnStateKind::Basic);
3044        add_state(&mut atn, 2, AtnStateKind::Basic);
3045        add_state(&mut atn, 3, AtnStateKind::Basic);
3046        atn.add_transition(
3047            0,
3048            ParserTransitionSpec::Action {
3049                target: 1,
3050                rule_index: 0,
3051                action_index: Some(0),
3052                context_dependent: false,
3053            },
3054        )
3055        .expect("transition");
3056        atn.add_transition(
3057            1,
3058            ParserTransitionSpec::Predicate {
3059                target: 2,
3060                rule_index: 0,
3061                pred_index: 0,
3062                context_dependent: false,
3063            },
3064        )
3065        .expect("transition");
3066        atn.add_transition(
3067            2,
3068            ParserTransitionSpec::Atom {
3069                target: 3,
3070                label: 1,
3071            },
3072        )
3073        .expect("transition");
3074        atn.set_rule_to_start_state(vec![0])
3075            .expect("rule start states");
3076        atn.set_rule_to_stop_state(vec![3])
3077            .expect("rule stop states");
3078        let atn = finish_atn(atn);
3079
3080        let mut simulator = ParserAtnSimulator::new(&atn);
3081        let mut configs = AtnConfigSet::new();
3082        let mut merge_cache = PredictionWorkspace::default();
3083        let mut scratch = ClosureScratch::default();
3084        let config = AtnConfig::new(0, 1, EMPTY_CONTEXT, &simulator.store.contexts);
3085        simulator.closure(
3086            config,
3087            &mut configs,
3088            &mut merge_cache,
3089            &mut scratch,
3090            ClosureParams {
3091                precedence: 0,
3092                collect_predicates: true,
3093                treat_eof_as_epsilon: false,
3094            },
3095        );
3096
3097        // The config that stops at state 2 (post-predicate, awaiting the atom)
3098        // must NOT carry the predicate — the action edge turned collection off.
3099        let at_two = configs
3100            .configs()
3101            .iter()
3102            .find(|config| config.state == 2)
3103            .expect("config at state 2");
3104        assert!(
3105            at_two.semantic_context.is_none(),
3106            "predicate after an action edge must not be collected during prediction"
3107        );
3108
3109        // Control: the SAME predicate reached WITHOUT an intervening action edge
3110        // IS collected (so the assertion above is about the action edge, not a
3111        // blanket failure to collect predicates).
3112        let direct_config = AtnConfig::new(1, 1, EMPTY_CONTEXT, &simulator.store.contexts);
3113        let direct_transition = atn
3114            .state(1)
3115            .expect("predicate source")
3116            .transitions()
3117            .first()
3118            .expect("predicate transition");
3119        let direct = simulator
3120            .epsilon_target_config(
3121                &direct_config,
3122                direct_transition,
3123                direct_transition.kind(),
3124                0,
3125                true,
3126                false,
3127            )
3128            .expect("predicate transition");
3129        assert!(matches!(
3130            direct.semantic_context,
3131            SemanticContext::Predicate { pred_index: 0, .. }
3132        ));
3133    }
3134
3135    #[test]
3136    fn reach_set_skips_closure_for_unique_intermediate_alt() {
3137        let mut atn = ParserAtnBuilder::new(1);
3138        add_state(&mut atn, 0, AtnStateKind::Basic);
3139        add_state(&mut atn, 1, AtnStateKind::Basic);
3140        add_state(&mut atn, 2, AtnStateKind::Basic);
3141        atn.add_transition(
3142            0,
3143            ParserTransitionSpec::Atom {
3144                target: 1,
3145                label: 7,
3146            },
3147        )
3148        .expect("transition");
3149        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
3150            .expect("transition");
3151        atn.set_rule_to_start_state(vec![0])
3152            .expect("rule start states");
3153        atn.set_rule_to_stop_state(vec![2])
3154            .expect("rule stop states");
3155        let atn = finish_atn(atn);
3156
3157        let mut simulator = ParserAtnSimulator::new(&atn);
3158        let mut configs = AtnConfigSet::new_full_context(false);
3159        let mut merge_cache = PredictionWorkspace::default();
3160        configs.add(
3161            AtnConfig::new(0, 1, EMPTY_CONTEXT, &simulator.store.contexts),
3162            &mut simulator.store.contexts,
3163            &mut merge_cache,
3164        );
3165
3166        let reach = simulator.compute_reach_set(&configs, 7, false, 0, &mut merge_cache);
3167
3168        assert_eq!(reach.len(), 1);
3169        assert_eq!(reach.configs()[0].state, 1);
3170    }
3171
3172    #[test]
3173    fn semantic_context_flag_is_scoped_to_predicted_alt() {
3174        let mut arena = ContextArena::new();
3175        let mut workspace = PredictionWorkspace::default();
3176        let mut configs = AtnConfigSet::new();
3177        configs.add(
3178            AtnConfig::new(1, 1, EMPTY_CONTEXT, &arena),
3179            &mut arena,
3180            &mut workspace,
3181        );
3182        configs.add(
3183            AtnConfig::new(2, 2, EMPTY_CONTEXT, &arena).with_semantic_context(
3184                SemanticContext::Predicate {
3185                    rule_index: 0,
3186                    pred_index: 0,
3187                    context_dependent: false,
3188                },
3189            ),
3190            &mut arena,
3191            &mut workspace,
3192        );
3193
3194        assert!(!configs_have_semantic_context_for_alt(&configs, 1));
3195        assert!(configs_have_semantic_context_for_alt(&configs, 2));
3196    }
3197
3198    #[test]
3199    fn adaptive_predict_prefers_non_greedy_exit_before_consuming() {
3200        let atn = non_greedy_optional_exit_first_atn();
3201        let mut simulator = ParserAtnSimulator::new(&atn);
3202
3203        assert_eq!(simulator.adaptive_predict(0, [1, TOKEN_EOF]), Ok(1));
3204    }
3205
3206    #[test]
3207    fn left_recursive_loop_entry_drop_requires_same_rule_return() {
3208        let atn = left_recursive_loop_entry_atn();
3209        let loop_entry = atn.state(1).expect("loop entry");
3210        let mut contexts = ContextArena::new();
3211        let same_rule_context = contexts.singleton(EMPTY_CONTEXT, 4);
3212        let other_rule_context = contexts.singleton(EMPTY_CONTEXT, 5);
3213
3214        assert!(can_drop_left_recursive_loop_entry_edge(
3215            &atn,
3216            loop_entry,
3217            &contexts,
3218            same_rule_context
3219        ));
3220        assert!(!can_drop_left_recursive_loop_entry_edge(
3221            &atn,
3222            loop_entry,
3223            &contexts,
3224            other_rule_context
3225        ));
3226        assert!(!can_drop_left_recursive_loop_entry_edge(
3227            &atn,
3228            loop_entry,
3229            &contexts,
3230            EMPTY_CONTEXT
3231        ));
3232    }
3233
3234    fn two_token_decision_atn() -> Atn {
3235        two_token_decision_atn_with_precedence(false)
3236    }
3237
3238    fn two_independent_decisions_atn() -> Atn {
3239        let mut atn = ParserAtnBuilder::new(3);
3240        add_two_token_decision_rule(&mut atn, 0, 0);
3241        add_two_token_decision_rule(&mut atn, 8, 1);
3242        atn.set_rule_to_start_state(vec![0, 8])
3243            .expect("rule start states");
3244        atn.set_rule_to_stop_state(vec![7, 15])
3245            .expect("rule stop states");
3246        finish_atn(atn)
3247    }
3248
3249    fn two_token_decision_atn_with_precedence(precedence: bool) -> Atn {
3250        let mut atn = ParserAtnBuilder::new(3);
3251        add_two_token_decision_rule(&mut atn, 0, 0);
3252        atn.set_rule_to_start_state(vec![0])
3253            .expect("rule start states");
3254        atn.set_rule_to_stop_state(vec![7])
3255            .expect("rule stop states");
3256        if precedence {
3257            atn.set_precedence_rule_decision(1)
3258                .expect("precedence decision state");
3259        }
3260        finish_atn(atn)
3261    }
3262
3263    fn add_two_token_decision_rule(atn: &mut ParserAtnBuilder, offset: usize, rule_index: usize) {
3264        assert_eq!(atn.state_count(), offset);
3265        for kind in [
3266            AtnStateKind::RuleStart,
3267            AtnStateKind::BlockStart,
3268            AtnStateKind::Basic,
3269            AtnStateKind::Basic,
3270            AtnStateKind::Basic,
3271            AtnStateKind::Basic,
3272            AtnStateKind::BlockEnd,
3273            AtnStateKind::RuleStop,
3274        ] {
3275            let expected = atn.state_count();
3276            assert_eq!(
3277                atn.add_state(kind, Some(rule_index))
3278                    .expect("state")
3279                    .index(),
3280                expected
3281            );
3282        }
3283        atn.add_decision_state(offset + 1).expect("decision state");
3284        atn.add_transition(offset, ParserTransitionSpec::Epsilon { target: offset + 1 })
3285            .expect("transition");
3286        atn.add_transition(
3287            offset + 1,
3288            ParserTransitionSpec::Epsilon { target: offset + 2 },
3289        )
3290        .expect("transition");
3291        atn.add_transition(
3292            offset + 1,
3293            ParserTransitionSpec::Epsilon { target: offset + 4 },
3294        )
3295        .expect("transition");
3296        atn.add_transition(
3297            offset + 2,
3298            ParserTransitionSpec::Atom {
3299                target: offset + 3,
3300                label: 1,
3301            },
3302        )
3303        .expect("transition");
3304        atn.add_transition(
3305            offset + 3,
3306            ParserTransitionSpec::Atom {
3307                target: offset + 6,
3308                label: 2,
3309            },
3310        )
3311        .expect("transition");
3312        atn.add_transition(
3313            offset + 4,
3314            ParserTransitionSpec::Atom {
3315                target: offset + 5,
3316                label: 1,
3317            },
3318        )
3319        .expect("transition");
3320        atn.add_transition(
3321            offset + 5,
3322            ParserTransitionSpec::Atom {
3323                target: offset + 6,
3324                label: 3,
3325            },
3326        )
3327        .expect("transition");
3328        atn.add_transition(
3329            offset + 6,
3330            ParserTransitionSpec::Epsilon { target: offset + 7 },
3331        )
3332        .expect("transition");
3333    }
3334
3335    fn optional_token_decision_atn() -> Atn {
3336        let mut atn = ParserAtnBuilder::new(1);
3337        add_state(&mut atn, 0, AtnStateKind::RuleStart);
3338        add_state(&mut atn, 1, AtnStateKind::BlockStart);
3339        add_state(&mut atn, 2, AtnStateKind::Basic);
3340        add_state(&mut atn, 3, AtnStateKind::BlockEnd);
3341        add_state(&mut atn, 4, AtnStateKind::RuleStop);
3342        atn.set_rule_to_start_state(vec![0])
3343            .expect("rule start states");
3344        atn.set_rule_to_stop_state(vec![4])
3345            .expect("rule stop states");
3346        atn.add_decision_state(1).expect("decision state");
3347        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
3348            .expect("transition");
3349        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
3350            .expect("transition");
3351        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
3352            .expect("transition");
3353        atn.add_transition(
3354            2,
3355            ParserTransitionSpec::Atom {
3356                target: 3,
3357                label: 1,
3358            },
3359        )
3360        .expect("transition");
3361        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 4 })
3362            .expect("transition");
3363        finish_atn(atn)
3364    }
3365
3366    fn non_greedy_optional_exit_first_atn() -> Atn {
3367        let mut atn = ParserAtnBuilder::new(1);
3368        add_state(&mut atn, 0, AtnStateKind::RuleStart);
3369        add_state(&mut atn, 1, AtnStateKind::BlockStart);
3370        add_state(&mut atn, 2, AtnStateKind::BlockEnd);
3371        add_state(&mut atn, 3, AtnStateKind::Basic);
3372        add_state(&mut atn, 4, AtnStateKind::RuleStop);
3373        atn.set_rule_to_start_state(vec![0])
3374            .expect("rule start states");
3375        atn.set_rule_to_stop_state(vec![4])
3376            .expect("rule stop states");
3377        atn.add_decision_state(1).expect("decision state");
3378        atn.set_non_greedy(1).expect("non-greedy state");
3379        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
3380            .expect("transition");
3381        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
3382            .expect("transition");
3383        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 3 })
3384            .expect("transition");
3385        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 4 })
3386            .expect("transition");
3387        atn.add_transition(
3388            3,
3389            ParserTransitionSpec::Atom {
3390                target: 4,
3391                label: 1,
3392            },
3393        )
3394        .expect("transition");
3395        finish_atn(atn)
3396    }
3397
3398    /// `s : A B C | A B C ;` — both alternatives match the same THREE-token
3399    /// sequence, so the SLL conflict's full-context retry consumes multiple
3400    /// tokens before resolving, exercising the memo's window walk (record and
3401    /// probe) rather than the empty-window fast case.
3402    fn ambiguous_three_token_decision_atn() -> Atn {
3403        let mut atn = ParserAtnBuilder::new(3);
3404        add_state(&mut atn, 0, AtnStateKind::RuleStart);
3405        add_state(&mut atn, 1, AtnStateKind::BlockStart);
3406        // Alternative 1: states 2 -A-> 3 -B-> 4 -C-> 5
3407        add_state(&mut atn, 2, AtnStateKind::Basic);
3408        add_state(&mut atn, 3, AtnStateKind::Basic);
3409        add_state(&mut atn, 4, AtnStateKind::Basic);
3410        add_state(&mut atn, 5, AtnStateKind::Basic);
3411        // Alternative 2: states 6 -A-> 7 -B-> 8 -C-> 9
3412        add_state(&mut atn, 6, AtnStateKind::Basic);
3413        add_state(&mut atn, 7, AtnStateKind::Basic);
3414        add_state(&mut atn, 8, AtnStateKind::Basic);
3415        add_state(&mut atn, 9, AtnStateKind::Basic);
3416        add_state(&mut atn, 10, AtnStateKind::BlockEnd);
3417        add_state(&mut atn, 11, AtnStateKind::RuleStop);
3418        atn.set_rule_to_start_state(vec![0])
3419            .expect("rule start states");
3420        atn.set_rule_to_stop_state(vec![11])
3421            .expect("rule stop states");
3422        atn.add_decision_state(1).expect("decision state");
3423        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
3424            .expect("transition");
3425        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
3426            .expect("transition");
3427        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
3428            .expect("transition");
3429        for (source, target, label) in [
3430            (2, 3, 1),
3431            (3, 4, 2),
3432            (4, 5, 3),
3433            (6, 7, 1),
3434            (7, 8, 2),
3435            (8, 9, 3),
3436        ] {
3437            atn.add_transition(source, ParserTransitionSpec::Atom { target, label })
3438                .expect("transition");
3439        }
3440        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 10 })
3441            .expect("transition");
3442        atn.add_transition(9, ParserTransitionSpec::Epsilon { target: 10 })
3443            .expect("transition");
3444        atn.add_transition(10, ParserTransitionSpec::Epsilon { target: 11 })
3445            .expect("transition");
3446        finish_atn(atn)
3447    }
3448
3449    fn ambiguous_single_token_decision_atn() -> Atn {
3450        let mut atn = ParserAtnBuilder::new(1);
3451        add_state(&mut atn, 0, AtnStateKind::RuleStart);
3452        add_state(&mut atn, 1, AtnStateKind::BlockStart);
3453        add_state(&mut atn, 2, AtnStateKind::Basic);
3454        add_state(&mut atn, 3, AtnStateKind::Basic);
3455        add_state(&mut atn, 4, AtnStateKind::Basic);
3456        add_state(&mut atn, 5, AtnStateKind::Basic);
3457        add_state(&mut atn, 6, AtnStateKind::BlockEnd);
3458        add_state(&mut atn, 7, AtnStateKind::RuleStop);
3459        atn.set_rule_to_start_state(vec![0])
3460            .expect("rule start states");
3461        atn.set_rule_to_stop_state(vec![7])
3462            .expect("rule stop states");
3463        atn.add_decision_state(1).expect("decision state");
3464        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
3465            .expect("transition");
3466        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
3467            .expect("transition");
3468        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
3469            .expect("transition");
3470        atn.add_transition(
3471            2,
3472            ParserTransitionSpec::Atom {
3473                target: 3,
3474                label: 1,
3475            },
3476        )
3477        .expect("transition");
3478        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 6 })
3479            .expect("transition");
3480        atn.add_transition(
3481            4,
3482            ParserTransitionSpec::Atom {
3483                target: 5,
3484                label: 1,
3485            },
3486        )
3487        .expect("transition");
3488        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
3489            .expect("transition");
3490        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
3491            .expect("transition");
3492        finish_atn(atn)
3493    }
3494
3495    fn prefix_alt_decision_atn() -> Atn {
3496        let mut atn = ParserAtnBuilder::new(3);
3497        add_state(&mut atn, 0, AtnStateKind::BlockStart);
3498        add_state(&mut atn, 1, AtnStateKind::Basic);
3499        add_state(&mut atn, 2, AtnStateKind::RuleStop);
3500        atn.set_rule_to_start_state(vec![0])
3501            .expect("rule start states");
3502        atn.set_rule_to_stop_state(vec![2])
3503            .expect("rule stop states");
3504        atn.add_decision_state(0).expect("decision state");
3505        atn.add_transition(
3506            0,
3507            ParserTransitionSpec::Atom {
3508                target: 2,
3509                label: 1,
3510            },
3511        )
3512        .expect("transition");
3513        atn.add_transition(
3514            0,
3515            ParserTransitionSpec::Atom {
3516                target: 1,
3517                label: 1,
3518            },
3519        )
3520        .expect("transition");
3521        atn.add_transition(
3522            1,
3523            ParserTransitionSpec::Atom {
3524                target: 2,
3525                label: 2,
3526            },
3527        )
3528        .expect("transition");
3529        finish_atn(atn)
3530    }
3531
3532    fn three_token_prefix_alt_decision_atn() -> Atn {
3533        let mut atn = ParserAtnBuilder::new(2);
3534        for (state_number, kind) in [
3535            (0, AtnStateKind::BlockStart),
3536            (1, AtnStateKind::Basic),
3537            (2, AtnStateKind::Basic),
3538            (3, AtnStateKind::Basic),
3539            (4, AtnStateKind::Basic),
3540            (5, AtnStateKind::Basic),
3541            (6, AtnStateKind::RuleStop),
3542        ] {
3543            add_state(&mut atn, state_number, kind);
3544        }
3545        atn.set_rule_to_start_state(vec![0])
3546            .expect("rule start states");
3547        atn.set_rule_to_stop_state(vec![6])
3548            .expect("rule stop states");
3549        atn.add_decision_state(0).expect("decision state");
3550        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
3551            .expect("transition");
3552        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 2 })
3553            .expect("transition");
3554        atn.add_transition(
3555            1,
3556            ParserTransitionSpec::Atom {
3557                target: 6,
3558                label: 1,
3559            },
3560        )
3561        .expect("transition");
3562        atn.add_transition(
3563            2,
3564            ParserTransitionSpec::Atom {
3565                target: 3,
3566                label: 1,
3567            },
3568        )
3569        .expect("transition");
3570        atn.add_transition(
3571            3,
3572            ParserTransitionSpec::Atom {
3573                target: 4,
3574                label: 2,
3575            },
3576        )
3577        .expect("transition");
3578        atn.add_transition(
3579            4,
3580            ParserTransitionSpec::Atom {
3581                target: 5,
3582                label: 1,
3583            },
3584        )
3585        .expect("transition");
3586        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
3587            .expect("transition");
3588        finish_atn(atn)
3589    }
3590
3591    fn multiple_eof_decision_atn() -> Atn {
3592        let mut atn = ParserAtnBuilder::new(2);
3593        for state_number in 0..=10 {
3594            let kind = match state_number {
3595                0 => AtnStateKind::RuleStart,
3596                1 => AtnStateKind::BlockStart,
3597                7 => AtnStateKind::BlockEnd,
3598                10 => AtnStateKind::RuleStop,
3599                _ => AtnStateKind::Basic,
3600            };
3601            add_state(&mut atn, state_number, kind);
3602        }
3603        atn.set_rule_to_start_state(vec![0])
3604            .expect("rule start states");
3605        atn.set_rule_to_stop_state(vec![10])
3606            .expect("rule stop states");
3607        atn.add_decision_state(1).expect("decision state");
3608        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
3609            .expect("transition");
3610        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
3611            .expect("transition");
3612        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
3613            .expect("transition");
3614        atn.add_transition(
3615            2,
3616            ParserTransitionSpec::Atom {
3617                target: 3,
3618                label: 1,
3619            },
3620        )
3621        .expect("transition");
3622        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 7 })
3623            .expect("transition");
3624        atn.add_transition(
3625            4,
3626            ParserTransitionSpec::Atom {
3627                target: 5,
3628                label: 1,
3629            },
3630        )
3631        .expect("transition");
3632        atn.add_transition(
3633            5,
3634            ParserTransitionSpec::Atom {
3635                target: 6,
3636                label: 2,
3637            },
3638        )
3639        .expect("transition");
3640        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 7 })
3641            .expect("transition");
3642        atn.add_transition(7, ParserTransitionSpec::Epsilon { target: 8 })
3643            .expect("transition");
3644        atn.add_transition(
3645            8,
3646            ParserTransitionSpec::Atom {
3647                target: 9,
3648                label: TOKEN_EOF,
3649            },
3650        )
3651        .expect("transition");
3652        atn.add_transition(
3653            9,
3654            ParserTransitionSpec::Atom {
3655                target: 10,
3656                label: TOKEN_EOF,
3657            },
3658        )
3659        .expect("transition");
3660        finish_atn(atn)
3661    }
3662
3663    fn left_recursive_loop_entry_atn() -> Atn {
3664        let mut atn = ParserAtnBuilder::new(1);
3665        add_state(&mut atn, 0, AtnStateKind::RuleStart);
3666        add_state(&mut atn, 1, AtnStateKind::StarLoopEntry);
3667        add_state(&mut atn, 2, AtnStateKind::BlockStart);
3668        add_state(&mut atn, 3, AtnStateKind::BlockEnd);
3669        add_state(&mut atn, 4, AtnStateKind::Basic);
3670        assert_eq!(
3671            atn.add_state(AtnStateKind::Basic, Some(1))
3672                .expect("state")
3673                .index(),
3674            5
3675        );
3676        add_state(&mut atn, 6, AtnStateKind::LoopEnd);
3677        add_state(&mut atn, 7, AtnStateKind::RuleStop);
3678        atn.set_rule_to_start_state(vec![0, 5])
3679            .expect("rule start states");
3680        atn.set_rule_to_stop_state(vec![7, 7])
3681            .expect("rule stop states");
3682        atn.set_precedence_rule_decision(1)
3683            .expect("precedence decision state");
3684        atn.set_end_state(2, 3).expect("block end state");
3685        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
3686            .expect("transition");
3687        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 6 })
3688            .expect("transition");
3689        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 3 })
3690            .expect("transition");
3691        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 3 })
3692            .expect("transition");
3693        finish_atn(atn)
3694    }
3695
3696    fn add_state(atn: &mut ParserAtnBuilder, state_number: usize, kind: AtnStateKind) {
3697        assert_eq!(
3698            atn.add_state(kind, Some(0)).expect("state").index(),
3699            state_number
3700        );
3701    }
3702
3703    #[derive(Debug)]
3704    struct VecIntStream {
3705        symbols: Vec<i32>,
3706        index: usize,
3707    }
3708
3709    impl VecIntStream {
3710        fn new(symbols: Vec<i32>) -> Self {
3711            Self { symbols, index: 0 }
3712        }
3713    }
3714
3715    impl IntStream for VecIntStream {
3716        fn consume(&mut self) {
3717            if self.la(1) != TOKEN_EOF {
3718                self.index += 1;
3719            }
3720        }
3721
3722        fn la(&mut self, offset: isize) -> i32 {
3723            if offset <= 0 {
3724                return 0;
3725            }
3726            let offset = offset.cast_unsigned() - 1;
3727            self.symbols
3728                .get(self.index + offset)
3729                .copied()
3730                .unwrap_or(TOKEN_EOF)
3731        }
3732
3733        fn index(&self) -> usize {
3734            self.index
3735        }
3736
3737        fn seek(&mut self, index: usize) {
3738            self.index = index;
3739        }
3740
3741        fn size(&self) -> usize {
3742            self.symbols.len()
3743        }
3744    }
3745}