Skip to main content

antlr4_runtime/atn/
parser.rs

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