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