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