Skip to main content

antlr4_runtime/atn/
bypass.rs

1//! Rule-bypass alternatives for parse-tree pattern matching.
2//!
3//! ANTLR's parse-tree pattern matcher needs to interpret a hybrid token stream
4//! in which an *imaginary* token can stand in for an entire parser rule (the
5//! `<expr>` tag in a pattern like `x = <expr>;`). Upstream implements this by
6//! re-deserializing the grammar ATN with `generateRuleBypassTransitions`
7//! enabled (`ATNDeserializer`): each rule gains a **bypass alternative** so the
8//! ordinary parser interpreter can match one imaginary token as if it were the
9//! whole rule. See `ATNDeserializer.java` (the `isGenerateRuleBypassTransitions`
10//! block) for the reference algorithm this mirrors.
11//!
12//! This runtime stores parser ATNs as a packed, immutable word stream rather
13//! than a mutable object graph, so the transform reads the source ATN through
14//! its borrowing views into an out-adjacency model, applies the rewrite there,
15//! and emits a fresh [`ParserAtn`] through [`ParserAtnBuilder`]. The existing
16//! ATN interpreter then runs over the result unchanged — no hot-path edits.
17//!
18//! ### Why `max_token_type` is left unchanged
19//!
20//! Upstream assigns each rule the imaginary token type `maxTokenType + i + 1`
21//! but never raises `maxTokenType` itself. We keep the same invariant on
22//! purpose: an [`Atom`](ParserTransitionSpec::Atom) transition matches by exact
23//! label equality (no range check), so the bypass edge matches its imaginary
24//! type fine, while grammar wildcards and `~x` negated sets — which the runtime
25//! bounds by `min..=max_token_type` — can never accidentally match an imaginary
26//! token that lives *above* the unchanged maximum.
27
28use std::collections::BTreeSet;
29
30use super::AtnStateKind;
31use super::parser_atn::{
32    ParserAtn, ParserAtnBuilder, ParserAtnError, ParserIntervalSetId, ParserTransitionData,
33    ParserTransitionSpec,
34};
35
36impl ParserAtn {
37    /// Builds a copy of this parser ATN with rule-bypass alternatives added.
38    ///
39    /// Every rule gains an imaginary token type (`max_token_type + rule + 1`)
40    /// and a bypass block so the ATN interpreter can match that single
41    /// imaginary token in place of the whole rule. `max_token_type` is
42    /// unchanged (see the module docs). The returned ATN is otherwise a faithful
43    /// copy: state kinds, transitions, interval sets, decisions, and
44    /// rule/precedence metadata are all preserved.
45    ///
46    /// # Errors
47    ///
48    /// Returns [`ParserAtnError`] if the state/transition/token counts overflow
49    /// the packed compact-index range, if a left-recursive rule's precedence
50    /// prefix cannot be identified, or if the re-emitted stream fails
51    /// validation.
52    pub fn with_bypass_alternatives(&self) -> Result<Self, ParserAtnError> {
53        BypassBuilder::new(self)?.build()
54    }
55
56    /// The imaginary token type reserved for a rule's bypass alternative:
57    /// `max_token_type + rule_index + 1`.
58    ///
59    /// This is the single source of the formula shared by
60    /// [`Self::with_bypass_alternatives`] (which labels the bypass `Atom` edge
61    /// with it) and the pattern matcher (which stamps rule-tag tokens with it),
62    /// so the two can never disagree about a tag's token type.
63    ///
64    /// # Errors
65    ///
66    /// Returns [`ParserAtnError::Overflow`] when the type would exceed `i32`.
67    pub fn bypass_token_type(&self, rule_index: usize) -> Result<i32, ParserAtnError> {
68        imaginary_token_type(self.max_token_type(), rule_index)
69    }
70}
71
72/// Shared formula for a rule's imaginary bypass token type.
73fn imaginary_token_type(max_token_type: i32, rule: usize) -> Result<i32, ParserAtnError> {
74    let overflow = || ParserAtnError::Overflow {
75        field: "bypass imaginary token type",
76        value: rule,
77    };
78    let rule = i32::try_from(rule).map_err(|_| overflow())?;
79    max_token_type
80        .checked_add(rule)
81        .and_then(|value| value.checked_add(1))
82        .ok_or_else(overflow)
83}
84
85/// Mutable working copy of a parser ATN used to apply the bypass rewrite.
86struct BypassBuilder {
87    max_token_type: i32,
88    /// Per-state kind, parallel to state number.
89    kinds: Vec<AtnStateKind>,
90    /// Per-state grammar rule index (`None` for rule-agnostic states).
91    rule_indices: Vec<Option<usize>>,
92    /// Block-start end state, if any.
93    end_states: Vec<Option<usize>>,
94    /// Loop-end loop-back state, if any.
95    loop_back_states: Vec<Option<usize>>,
96    /// Non-greedy decision flag, preserved verbatim.
97    non_greedy: Vec<bool>,
98    /// Left-recursive rule flag, preserved on rule-start states.
99    left_recursive: Vec<bool>,
100    /// Out-adjacency: `out[source]` holds that state's transitions in order.
101    out: Vec<Vec<ParserTransitionSpec>>,
102    /// Interval sets copied verbatim; identities stay valid because order is
103    /// preserved.
104    interval_sets: Vec<Vec<(i32, i32)>>,
105    decisions: Vec<usize>,
106    rule_starts: Vec<usize>,
107    rule_stops: Vec<usize>,
108}
109
110impl BypassBuilder {
111    /// Reads the packed source ATN into the mutable model.
112    fn new(atn: &ParserAtn) -> Result<Self, ParserAtnError> {
113        let state_count = atn.state_count();
114        let mut kinds = Vec::with_capacity(state_count);
115        let mut rule_indices = Vec::with_capacity(state_count);
116        let mut end_states = Vec::with_capacity(state_count);
117        let mut loop_back_states = Vec::with_capacity(state_count);
118        let mut non_greedy = Vec::with_capacity(state_count);
119        let mut left_recursive = Vec::with_capacity(state_count);
120        let mut out = Vec::with_capacity(state_count);
121
122        for number in 0..state_count {
123            let state = atn
124                .state(number)
125                .expect("state index below state_count is in bounds");
126            kinds.push(state.kind());
127            rule_indices.push(state.rule_index());
128            end_states.push(state.end_state());
129            loop_back_states.push(state.loop_back_state());
130            non_greedy.push(state.non_greedy());
131            left_recursive.push(state.left_recursive_rule());
132            out.push(
133                state
134                    .transitions()
135                    .iter()
136                    .map(|transition| data_to_spec(transition.data()))
137                    .collect::<Result<Vec<_>, _>>()?,
138            );
139        }
140
141        let interval_sets = (0..atn.set_count())
142            .map(|index| {
143                atn.token_set(index)
144                    .expect("set index below set_count is in bounds")
145                    .ranges()
146                    .collect::<Vec<_>>()
147            })
148            .collect();
149
150        let decisions = atn.decision_to_state().into_iter().collect();
151        let rule_starts = atn.rule_to_start_state().into_iter().collect();
152        let rule_stops = atn.rule_to_stop_state().into_iter().collect();
153
154        Ok(Self {
155            max_token_type: atn.max_token_type(),
156            kinds,
157            rule_indices,
158            end_states,
159            loop_back_states,
160            non_greedy,
161            left_recursive,
162            out,
163            interval_sets,
164            decisions,
165            rule_starts,
166            rule_stops,
167        })
168    }
169
170    /// Applies the rewrite and emits the packed result.
171    fn build(mut self) -> Result<ParserAtn, ParserAtnError> {
172        let rule_count = self.rule_starts.len();
173        let original_state_count = self.kinds.len();
174
175        // Reserve the three new states per rule up front so their numbers are
176        // known before any edge is rewired. Layout: for rule `i`,
177        // `bypass_start = base`, `bypass_stop = base + 1`, `match = base + 2`.
178        let new_state_base = original_state_count;
179        for rule in 0..rule_count {
180            let bypass_start = new_state_base + rule * 3;
181            let bypass_stop = bypass_start + 1;
182            let match_state = bypass_start + 2;
183            // Bypass start is a decision block start whose end is the stop.
184            self.push_state(
185                AtnStateKind::BlockStart,
186                Some(rule),
187                Some(bypass_stop),
188                None,
189            );
190            self.push_state(AtnStateKind::BlockEnd, Some(rule), None, None);
191            self.push_state(AtnStateKind::Basic, None, None, None);
192            debug_assert_eq!(self.out.len(), match_state + 1);
193        }
194
195        // Retarget/move plan per rule, computed against the *pre-move* graph so
196        // the left-recursive exclude transition is identified correctly. End
197        // states are captured here and reused below — recomputing them after
198        // the move/retarget passes would scan a mutated graph.
199        let mut end_states: Vec<usize> = Vec::with_capacity(rule_count);
200        let mut bypass_stop_for_end: Vec<Option<usize>> = vec![None; self.kinds.len()];
201        let mut excluded: BTreeSet<(usize, usize)> = BTreeSet::new();
202        for rule in 0..rule_count {
203            let bypass_stop = new_state_base + rule * 3 + 1;
204            let (end_state, exclude) = self.rule_end_state(rule)?;
205            end_states.push(end_state);
206            bypass_stop_for_end[end_state] = Some(bypass_stop);
207            if let Some(exclude) = exclude {
208                excluded.insert(exclude);
209            }
210        }
211
212        // Move each rule-start's transitions onto its bypass-start block.
213        for rule in 0..rule_count {
214            let rule_start = self.rule_starts[rule];
215            let bypass_start = new_state_base + rule * 3;
216            let moved = std::mem::take(&mut self.out[rule_start]);
217            self.out[bypass_start] = moved;
218        }
219
220        // Retarget every edge that targeted a rule's end state onto that rule's
221        // bypass stop, skipping the left-recursive exclude edge(s).
222        for (source, transitions) in self.out.iter_mut().enumerate() {
223            for (index, spec) in transitions.iter_mut().enumerate() {
224                if excluded.contains(&(source, index)) {
225                    continue;
226                }
227                if let Some(bypass_stop) = bypass_stop_for_end[spec.target()] {
228                    *spec = spec.with_target(bypass_stop);
229                }
230            }
231        }
232
233        // Add the bypass edges last so the `bypass_stop -> end_state` link
234        // (which targets an end state) is never caught by the retarget pass.
235        for (rule, &end_state) in end_states.iter().enumerate() {
236            let rule_start = self.rule_starts[rule];
237            let bypass_start = new_state_base + rule * 3;
238            let bypass_stop = bypass_start + 1;
239            let match_state = bypass_start + 2;
240            let imaginary = self.imaginary_token_type(rule)?;
241
242            self.out[rule_start].push(ParserTransitionSpec::Epsilon {
243                target: bypass_start,
244            });
245            self.out[bypass_start].push(ParserTransitionSpec::Epsilon {
246                target: match_state,
247            });
248            self.out[bypass_stop].push(ParserTransitionSpec::Epsilon { target: end_state });
249            self.out[match_state].push(ParserTransitionSpec::Atom {
250                target: bypass_stop,
251                label: imaginary,
252            });
253            self.decisions.push(bypass_start);
254        }
255
256        self.emit()
257    }
258
259    /// Appends one new state to the model, keeping every parallel array aligned.
260    fn push_state(
261        &mut self,
262        kind: AtnStateKind,
263        rule_index: Option<usize>,
264        end_state: Option<usize>,
265        loop_back_state: Option<usize>,
266    ) {
267        self.kinds.push(kind);
268        self.rule_indices.push(rule_index);
269        self.end_states.push(end_state);
270        self.loop_back_states.push(loop_back_state);
271        self.non_greedy.push(false);
272        self.left_recursive.push(false);
273        self.out.push(Vec::new());
274    }
275
276    /// Returns `(end_state, exclude_edge)` for a rule.
277    ///
278    /// For an ordinary rule the end state is the rule stop and there is no
279    /// excluded edge. For a left-recursive rule the end state is the
280    /// `StarLoopEntry` that begins the precedence-climbing loop, and the excluded
281    /// edge is the loop-back edge into it (which must keep pointing at the entry
282    /// rather than being diverted to the bypass block). Mirrors the
283    /// `isLeftRecursiveRule` branch in `ATNDeserializer`.
284    fn rule_end_state(
285        &self,
286        rule: usize,
287    ) -> Result<(usize, Option<(usize, usize)>), ParserAtnError> {
288        let rule_start = self.rule_starts[rule];
289        if !self.left_recursive[rule_start] {
290            return Ok((self.rule_stops[rule], None));
291        }
292
293        // Find the StarLoopEntry whose last edge reaches a LoopEnd that
294        // epsilon-transitions to the rule stop: that entry is the precedence
295        // prefix boundary ANTLR wraps.
296        for state in 0..self.kinds.len() {
297            if self.rule_indices[state] != Some(rule)
298                || self.kinds[state] != AtnStateKind::StarLoopEntry
299            {
300                continue;
301            }
302            let Some(last) = self.out[state].last() else {
303                continue;
304            };
305            let loop_end = last.target();
306            if self.kinds.get(loop_end).copied() != Some(AtnStateKind::LoopEnd) {
307                continue;
308            }
309            // Upstream additionally requires the loop end to be epsilon-only
310            // before trusting its first edge (`maybeLoopEndState
311            // .epsilonOnlyTransitions && ... instanceof RuleStopState`).
312            let epsilon_only = self.out[loop_end]
313                .iter()
314                .all(|edge| matches!(edge, ParserTransitionSpec::Epsilon { .. }));
315            let reaches_stop = self.out[loop_end]
316                .first()
317                .is_some_and(|edge| self.kinds.get(edge.target()) == Some(&AtnStateKind::RuleStop));
318            if !epsilon_only || !reaches_stop {
319                continue;
320            }
321            let Some(loop_back) = self.loop_back_states[loop_end] else {
322                continue;
323            };
324            // The loop-back state's first edge is the excluded loop-back into
325            // the entry; verify the structure before trusting it.
326            if self.out[loop_back]
327                .first()
328                .is_some_and(|edge| edge.target() == state)
329            {
330                return Ok((state, Some((loop_back, 0))));
331            }
332        }
333
334        Err(ParserAtnError::InvalidData(format!(
335            "could not identify precedence prefix boundary for left-recursive rule {rule}"
336        )))
337    }
338
339    /// Imaginary token type reserved for a rule's bypass alternative.
340    fn imaginary_token_type(&self, rule: usize) -> Result<i32, ParserAtnError> {
341        imaginary_token_type(self.max_token_type, rule)
342    }
343
344    /// Emits the mutable model as a validated packed [`ParserAtn`].
345    fn emit(self) -> Result<ParserAtn, ParserAtnError> {
346        let mut builder = ParserAtnBuilder::new(self.max_token_type);
347
348        for (index, &kind) in self.kinds.iter().enumerate() {
349            builder.add_state(kind, self.rule_indices[index])?;
350        }
351        for (index, end_state) in self.end_states.iter().enumerate() {
352            if let Some(end_state) = end_state {
353                builder.set_end_state(index, *end_state)?;
354            }
355        }
356        for (index, loop_back) in self.loop_back_states.iter().enumerate() {
357            if let Some(loop_back) = loop_back {
358                builder.set_loop_back_state(index, *loop_back)?;
359            }
360        }
361        for (index, &flag) in self.non_greedy.iter().enumerate() {
362            if flag {
363                builder.set_non_greedy(index)?;
364            }
365        }
366        for (index, &flag) in self.left_recursive.iter().enumerate() {
367            if flag {
368                builder.set_left_recursive_rule(index)?;
369            }
370        }
371        for ranges in &self.interval_sets {
372            builder.add_interval_set(ranges.iter().copied())?;
373        }
374        for (source, transitions) in self.out.iter().enumerate() {
375            for spec in transitions {
376                builder.add_transition(source, *spec)?;
377            }
378        }
379        for &state in &self.decisions {
380            builder.add_decision_state(state)?;
381        }
382        builder.set_rule_to_start_state(self.rule_starts)?;
383        builder.set_rule_to_stop_state(self.rule_stops)?;
384
385        builder.finish()
386    }
387}
388
389/// Converts a borrowing transition view into a builder spec, translating the
390/// borrowed interval set back into its stable identity.
391fn data_to_spec(data: ParserTransitionData<'_>) -> Result<ParserTransitionSpec, ParserAtnError> {
392    Ok(match data {
393        ParserTransitionData::Epsilon { target } => ParserTransitionSpec::Epsilon { target },
394        ParserTransitionData::Atom { target, label } => {
395            ParserTransitionSpec::Atom { target, label }
396        }
397        ParserTransitionData::Range {
398            target,
399            start,
400            stop,
401        } => ParserTransitionSpec::Range {
402            target,
403            start,
404            stop,
405        },
406        ParserTransitionData::Set { target, set } => ParserTransitionSpec::Set {
407            target,
408            set: ParserIntervalSetId::try_from(set.index())?,
409        },
410        ParserTransitionData::NotSet { target, set } => ParserTransitionSpec::NotSet {
411            target,
412            set: ParserIntervalSetId::try_from(set.index())?,
413        },
414        ParserTransitionData::Wildcard { target } => ParserTransitionSpec::Wildcard { target },
415        ParserTransitionData::Rule {
416            target,
417            rule_index,
418            follow_state,
419            precedence,
420        } => ParserTransitionSpec::Rule {
421            target,
422            rule_index,
423            follow_state,
424            precedence,
425        },
426        ParserTransitionData::Predicate {
427            target,
428            rule_index,
429            pred_index,
430            context_dependent,
431        } => ParserTransitionSpec::Predicate {
432            target,
433            rule_index,
434            pred_index,
435            context_dependent,
436        },
437        ParserTransitionData::Action {
438            target,
439            rule_index,
440            action_index,
441            context_dependent,
442        } => ParserTransitionSpec::Action {
443            target,
444            rule_index,
445            action_index,
446            context_dependent,
447        },
448        ParserTransitionData::Precedence { target, precedence } => {
449            ParserTransitionSpec::Precedence { target, precedence }
450        }
451    })
452}
453
454#[cfg(test)]
455#[allow(clippy::disallowed_methods)] // `insta` assertion macros unwrap internal I/O.
456mod tests {
457    use super::*;
458    use crate::atn::parser_atn::{ParserTransition, ParserTransitionKind};
459    use crate::token::{Token, TokenId, TokenSink, TokenSource, TokenSpec, TokenStoreError};
460    use crate::{BaseParser, CommonTokenStream, NodeKind, RecognizerData, Vocabulary};
461
462    /// A token source over a fixed list of specs, ending in EOF — the runtime
463    /// analog of ANTLR's `ListTokenSource` used to feed a hybrid pattern stream.
464    #[derive(Debug)]
465    struct ListSource {
466        specs: Vec<TokenSpec>,
467        index: usize,
468    }
469
470    impl TokenSource for ListSource {
471        fn next_token(&mut self, sink: &mut TokenSink<'_>) -> Result<TokenId, TokenStoreError> {
472            let spec = self
473                .specs
474                .get(self.index)
475                .cloned()
476                .unwrap_or_else(|| TokenSpec::eof(self.index, self.index, 1, self.index));
477            self.index += 1;
478            sink.push(spec)
479        }
480
481        fn line(&self) -> usize {
482            1
483        }
484
485        fn column(&self) -> usize {
486            self.index
487        }
488
489        fn source_name(&self) -> &'static str {
490            "bypass-test"
491        }
492    }
493
494    /// Two-rule grammar `a : X b ; b : Y ;` (tokens `X=1`, `Y=2`).
495    ///
496    /// Deliberately plain (no loops, no left recursion) so the bypass rewrite's
497    /// state growth and edge rewiring are easy to reason about.
498    fn two_rule_atn() -> ParserAtn {
499        let mut atn = ParserAtnBuilder::new(2);
500        for (number, kind, rule) in [
501            (0, AtnStateKind::RuleStart, 0),
502            (1, AtnStateKind::Basic, 0),
503            (2, AtnStateKind::Basic, 0),
504            (3, AtnStateKind::RuleStop, 0),
505            (4, AtnStateKind::RuleStart, 1),
506            (5, AtnStateKind::Basic, 1),
507            (6, AtnStateKind::RuleStop, 1),
508        ] {
509            assert_eq!(
510                atn.add_state(kind, Some(rule)).expect("state").index(),
511                number
512            );
513        }
514        atn.set_rule_to_start_state(vec![0, 4]).expect("starts");
515        atn.set_rule_to_stop_state(vec![3, 6]).expect("stops");
516        // a : X b ;
517        atn.add_transition(
518            0,
519            ParserTransitionSpec::Atom {
520                target: 1,
521                label: 1,
522            },
523        )
524        .expect("edge");
525        atn.add_transition(
526            1,
527            ParserTransitionSpec::Rule {
528                target: 4,
529                rule_index: 1,
530                follow_state: 2,
531                precedence: 0,
532            },
533        )
534        .expect("edge");
535        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
536            .expect("edge");
537        // Synthetic rule-return edge already present in a packed ATN.
538        atn.add_transition(6, ParserTransitionSpec::Epsilon { target: 2 })
539            .expect("edge");
540        // b : Y ;
541        atn.add_transition(
542            4,
543            ParserTransitionSpec::Atom {
544                target: 5,
545                label: 2,
546            },
547        )
548        .expect("edge");
549        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
550            .expect("edge");
551        atn.finish().expect("valid base ATN")
552    }
553
554    /// One left-recursive rule shaped like ANTLR's transformed `e : e '+' e | X`:
555    /// `RuleStart(LR) -> Basic(prefix) -> StarLoopEntry -> {StarBlockStart(body),
556    /// LoopEnd -> RuleStop}`, with the loop body returning through a
557    /// `StarLoopBack` whose sole edge re-enters the entry.
558    fn left_recursive_atn() -> ParserAtn {
559        let mut atn = ParserAtnBuilder::new(2);
560        for (number, kind, rule) in [
561            (0, AtnStateKind::RuleStart, 0),
562            (1, AtnStateKind::Basic, 0), // primary/prefix matcher
563            (2, AtnStateKind::StarLoopEntry, 0),
564            (3, AtnStateKind::Basic, 0), // loop body: '+' e
565            (4, AtnStateKind::StarLoopBack, 0),
566            (5, AtnStateKind::LoopEnd, 0),
567            (6, AtnStateKind::RuleStop, 0),
568        ] {
569            assert_eq!(
570                atn.add_state(kind, Some(rule)).expect("state").index(),
571                number
572            );
573        }
574        atn.set_left_recursive_rule(0).expect("LR flag");
575        atn.set_rule_to_start_state(vec![0]).expect("starts");
576        atn.set_rule_to_stop_state(vec![6]).expect("stops");
577        atn.set_loop_back_state(5, 4).expect("loop back");
578        atn.add_decision_state(2).expect("decision");
579        atn.add_transition(
580            0,
581            ParserTransitionSpec::Atom {
582                target: 1,
583                label: 1,
584            },
585        )
586        .expect("edge");
587        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
588            .expect("edge");
589        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 3 })
590            .expect("edge");
591        atn.add_transition(2, ParserTransitionSpec::Epsilon { target: 5 })
592            .expect("edge");
593        atn.add_transition(
594            3,
595            ParserTransitionSpec::Atom {
596                target: 4,
597                label: 2,
598            },
599        )
600        .expect("edge");
601        // The loop-back edge that must stay pointed at the StarLoopEntry.
602        atn.add_transition(4, ParserTransitionSpec::Epsilon { target: 2 })
603            .expect("edge");
604        atn.add_transition(5, ParserTransitionSpec::Epsilon { target: 6 })
605            .expect("edge");
606        atn.finish().expect("valid left-recursive ATN")
607    }
608
609    /// Pins the left-recursive branch of `rule_end_state`: the bypass block
610    /// wraps the precedence prefix (ending at the `StarLoopEntry`, not the rule
611    /// stop) and the loop-back edge is excluded from retargeting.
612    #[test]
613    fn bypass_wraps_left_recursive_prefix_and_preserves_loop_back() {
614        let base = left_recursive_atn();
615        let bypass = base.with_bypass_alternatives().expect("bypass ATN");
616
617        let star_loop_entry = 2;
618        let star_loop_back = 4;
619        let bypass_start = base.state_count();
620        let bypass_stop = bypass_start + 1;
621
622        // The prefix edge into the StarLoopEntry is retargeted to bypass stop…
623        let prefix_targets: Vec<_> = bypass
624            .state(1)
625            .expect("prefix state")
626            .transitions()
627            .iter()
628            .map(ParserTransition::target)
629            .collect();
630        assert_eq!(prefix_targets, vec![bypass_stop]);
631        // …while the loop-back edge still re-enters the StarLoopEntry.
632        let loop_back_targets: Vec<_> = bypass
633            .state(star_loop_back)
634            .expect("loop-back state")
635            .transitions()
636            .iter()
637            .map(ParserTransition::target)
638            .collect();
639        assert_eq!(loop_back_targets, vec![star_loop_entry]);
640        // The bypass stop rejoins the graph at the StarLoopEntry (the
641        // left-recursive "end state"), not at the rule stop.
642        let stop_targets: Vec<_> = bypass
643            .state(bypass_stop)
644            .expect("bypass stop")
645            .transitions()
646            .iter()
647            .map(ParserTransition::target)
648            .collect();
649        assert_eq!(stop_targets, vec![star_loop_entry]);
650    }
651
652    /// A rule flagged left-recursive whose precedence prefix boundary cannot be
653    /// identified must fail loudly instead of producing a broken bypass ATN.
654    #[test]
655    fn bypass_reports_unrecognizable_left_recursive_structure() {
656        let mut atn = ParserAtnBuilder::new(1);
657        for (number, kind) in [
658            (0, AtnStateKind::RuleStart),
659            (1, AtnStateKind::Basic),
660            (2, AtnStateKind::RuleStop),
661        ] {
662            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), number);
663        }
664        // Flag the rule left-recursive without any StarLoopEntry structure.
665        atn.set_left_recursive_rule(0).expect("LR flag");
666        atn.set_rule_to_start_state(vec![0]).expect("starts");
667        atn.set_rule_to_stop_state(vec![2]).expect("stops");
668        atn.add_transition(
669            0,
670            ParserTransitionSpec::Atom {
671                target: 1,
672                label: 1,
673            },
674        )
675        .expect("edge");
676        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
677            .expect("edge");
678        let base = atn.finish().expect("valid base ATN");
679
680        let error = base
681            .with_bypass_alternatives()
682            .expect_err("missing precedence prefix must be reported");
683        assert!(
684            error.to_string().contains("left-recursive rule 0"),
685            "unexpected error: {error}"
686        );
687    }
688
689    #[test]
690    fn bypass_grows_three_states_per_rule_and_keeps_max_token_type() {
691        let base = two_rule_atn();
692        let bypass = base.with_bypass_alternatives().expect("bypass ATN");
693
694        // Three new states per rule, appended after the originals.
695        assert_eq!(
696            bypass.state_count(),
697            base.state_count() + 3 * base.rule_count()
698        );
699        // Imaginary token types live above the *unchanged* maximum.
700        assert_eq!(bypass.max_token_type(), base.max_token_type());
701    }
702
703    #[test]
704    fn bypass_adds_one_imaginary_atom_per_rule() {
705        let base = two_rule_atn();
706        let bypass = base.with_bypass_alternatives().expect("bypass ATN");
707
708        // Rule i's imaginary token type is max_token_type + i + 1.
709        let mut imaginary_atoms = Vec::new();
710        for state in 0..bypass.state_count() {
711            for transition in bypass.state(state).expect("state").transitions() {
712                if transition.kind() == ParserTransitionKind::Atom {
713                    if let ParserTransitionData::Atom { label, .. } = transition.data() {
714                        if label > base.max_token_type() {
715                            imaginary_atoms.push(label);
716                        }
717                    }
718                }
719            }
720        }
721        imaginary_atoms.sort_unstable();
722        assert_eq!(imaginary_atoms, vec![3, 4]); // max(2) + {1, 2}
723    }
724
725    #[test]
726    fn bypass_preserves_rule_start_and_stop_tables() {
727        let base = two_rule_atn();
728        let bypass = base.with_bypass_alternatives().expect("bypass ATN");
729
730        let base_starts: Vec<_> = base.rule_to_start_state().into_iter().collect();
731        let bypass_starts: Vec<_> = bypass.rule_to_start_state().into_iter().collect();
732        assert_eq!(base_starts, bypass_starts);
733
734        let base_stops: Vec<_> = base.rule_to_stop_state().into_iter().collect();
735        let bypass_stops: Vec<_> = bypass.rule_to_stop_state().into_iter().collect();
736        assert_eq!(base_stops, bypass_stops);
737    }
738
739    #[test]
740    fn rule_start_gains_epsilon_into_bypass_block() {
741        let base = two_rule_atn();
742        let bypass = base.with_bypass_alternatives().expect("bypass ATN");
743
744        // Rule 0's start state (0) should now epsilon into its bypass-start
745        // block (the first appended state), added as the last outgoing edge.
746        let bypass_start_for_rule_0 = base.state_count();
747        let rule_start = bypass.state(0).expect("rule start");
748        let epsilon_targets: Vec<_> = rule_start
749            .transitions()
750            .iter()
751            .filter(|t| t.kind() == ParserTransitionKind::Epsilon)
752            .map(ParserTransition::target)
753            .collect();
754        assert!(
755            epsilon_targets.contains(&bypass_start_for_rule_0),
756            "rule start must epsilon into its bypass block; got {epsilon_targets:?}"
757        );
758    }
759
760    fn two_rule_recognizer_data() -> RecognizerData {
761        RecognizerData::new(
762            "Bypass.g4",
763            Vocabulary::new(
764                [None, Some("'x'"), Some("'y'")],
765                [None, Some("X"), Some("Y")],
766                [None::<&str>, None],
767            ),
768        )
769        .with_rule_names(["a", "b"])
770    }
771
772    /// The load-bearing end-to-end proof: the *unchanged* ATN interpreter, run
773    /// over the bypass ATN, matches a single imaginary token in place of a whole
774    /// rule and renders it as a one-terminal rule subtree — exactly the shape
775    /// ANTLR's `getRuleTagToken` detects.
776    #[test]
777    fn interpreter_matches_imaginary_token_as_whole_rule() {
778        let bypass = two_rule_atn()
779            .with_bypass_alternatives()
780            .expect("bypass ATN");
781        // Rule 1 ("b")'s imaginary token type = max_token_type(2) + 1 + 1 = 4.
782        let imaginary_b = 4;
783        let source = ListSource {
784            specs: vec![
785                TokenSpec::explicit(1, "x"),             // real X token
786                TokenSpec::explicit(imaginary_b, "<b>"), // imaginary rule-b tag
787            ],
788            index: 0,
789        };
790        let mut parser =
791            BaseParser::new(CommonTokenStream::new(source), two_rule_recognizer_data());
792
793        let tree = parser
794            .parse_atn_rule(&bypass, 0)
795            .expect("bypass interpret of `a : X b` with imaginary b");
796
797        let root = parser.node(tree).as_rule().expect("root is rule a");
798        assert_eq!(root.rule_index(), 0);
799        let children: Vec<_> = root.node().children().collect();
800        assert_eq!(children.len(), 2, "a has children [X, b]");
801
802        // First child: the real X terminal.
803        let x = children[0].as_terminal().expect("first child terminal X");
804        assert_eq!(x.symbol().token_type(), 1);
805
806        // Second child: rule b rendered as a single-terminal subtree whose lone
807        // leaf carries the imaginary token type. This is the `(b <b>)` shape.
808        let b = children[1].as_rule().expect("second child rule b");
809        assert_eq!(b.rule_index(), 1);
810        let b_children: Vec<_> = b.node().children().collect();
811        assert_eq!(b_children.len(), 1, "bypassed rule b has exactly one child");
812        assert_eq!(b_children[0].kind(), NodeKind::Terminal);
813        assert_eq!(
814            b_children[0]
815                .as_terminal()
816                .expect("b's lone child is a terminal")
817                .symbol()
818                .token_type(),
819            imaginary_b,
820            "the lone child carries the imaginary bypass token type"
821        );
822    }
823
824    /// The bypass ATN must still parse ordinary input identically: feeding the
825    /// real tokens `X Y` reconstructs `(a X (b Y))` with no imaginary tokens.
826    #[test]
827    fn bypass_atn_still_parses_ordinary_input() {
828        let bypass = two_rule_atn()
829            .with_bypass_alternatives()
830            .expect("bypass ATN");
831        let source = ListSource {
832            specs: vec![TokenSpec::explicit(1, "x"), TokenSpec::explicit(2, "y")],
833            index: 0,
834        };
835        let mut parser =
836            BaseParser::new(CommonTokenStream::new(source), two_rule_recognizer_data());
837
838        let tree = parser
839            .parse_atn_rule(&bypass, 0)
840            .expect("bypass interpret of ordinary `X Y`");
841
842        let root = parser.node(tree).as_rule().expect("root is rule a");
843        let children: Vec<_> = root.node().children().collect();
844        assert_eq!(children.len(), 2);
845        assert_eq!(
846            children[0]
847                .as_terminal()
848                .expect("X terminal")
849                .symbol()
850                .token_type(),
851            1
852        );
853        let b = children[1].as_rule().expect("rule b");
854        let y = b
855            .node()
856            .children()
857            .next()
858            .expect("b child")
859            .as_terminal()
860            .expect("Y terminal");
861        assert_eq!(y.symbol().token_type(), 2);
862        assert_eq!(parser.number_of_syntax_errors(), 0);
863    }
864}