#[allow(clippy::disallowed_types)]
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::rc::Rc;
#[derive(Clone, Copy, Default)]
struct FxHasher {
hash: u64,
}
const FX_ROT: u32 = 5;
const FX_SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
impl Hasher for FxHasher {
#[inline]
fn write(&mut self, mut bytes: &[u8]) {
while bytes.len() >= 8 {
let (head, rest) = bytes.split_at(8);
let word = u64::from_le_bytes(head.try_into().expect("8-byte chunk"));
self.hash = (self.hash.rotate_left(FX_ROT) ^ word).wrapping_mul(FX_SEED);
bytes = rest;
}
for byte in bytes {
self.hash =
(self.hash.rotate_left(FX_ROT) ^ u64::from(*byte)).wrapping_mul(FX_SEED);
}
}
#[inline]
fn write_u64(&mut self, value: u64) {
self.hash = (self.hash.rotate_left(FX_ROT) ^ value).wrapping_mul(FX_SEED);
}
#[inline]
fn write_usize(&mut self, value: usize) {
self.write_u64(value as u64);
}
#[inline]
fn write_u32(&mut self, value: u32) {
self.write_u64(u64::from(value));
}
#[inline]
fn write_i32(&mut self, value: i32) {
self.write_u64(u64::from(i32::cast_unsigned(value)));
}
#[inline]
fn finish(&self) -> u64 {
self.hash
}
}
type FxBuildHasher = BuildHasherDefault<FxHasher>;
#[allow(clippy::disallowed_types)]
type FxHashMap<K, V> = HashMap<K, V, FxBuildHasher>;
#[allow(clippy::disallowed_types)]
type FxHashSet<K> = HashSet<K, FxBuildHasher>;
use crate::atn::{Atn, AtnState, AtnStateKind, Transition};
use crate::errors::AntlrError;
use crate::int_stream::IntStream;
use crate::recognizer::{Recognizer, RecognizerData};
use crate::token::{CommonToken, TOKEN_EOF, Token, TokenSource, TokenSourceError};
use crate::token_stream::CommonTokenStream;
use crate::tree::{ErrorNode, ParseTree, ParserRuleContext, RuleNode, TerminalNode};
use crate::vocabulary::Vocabulary;
const RECOGNITION_DEPTH_LIMIT: usize = 100_000;
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct ParserAction {
source_state: usize,
rule_index: usize,
start_index: usize,
stop_index: Option<usize>,
rule_init: bool,
expected_state: Option<usize>,
}
impl ParserAction {
pub const fn new(
source_state: usize,
rule_index: usize,
start_index: usize,
stop_index: Option<usize>,
) -> Self {
Self {
source_state,
rule_index,
start_index,
stop_index,
rule_init: false,
expected_state: None,
}
}
pub const fn new_rule_init(
rule_index: usize,
start_index: usize,
expected_state: Option<usize>,
) -> Self {
Self {
source_state: usize::MAX,
rule_index,
start_index,
stop_index: None,
rule_init: true,
expected_state,
}
}
pub const fn source_state(&self) -> usize {
self.source_state
}
pub const fn rule_index(&self) -> usize {
self.rule_index
}
pub const fn start_index(&self) -> usize {
self.start_index
}
pub const fn stop_index(&self) -> Option<usize> {
self.stop_index
}
pub const fn is_rule_init(&self) -> bool {
self.rule_init
}
pub const fn expected_state(&self) -> Option<usize> {
self.expected_state
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum ParserPredicate {
True,
False,
FalseWithMessage {
message: &'static str,
},
Invoke {
value: bool,
},
LookaheadTextEquals {
offset: isize,
text: &'static str,
},
LookaheadNotEquals {
offset: isize,
token_type: i32,
},
LocalIntEquals {
value: i64,
},
LocalIntLessOrEqual {
value: i64,
},
MemberModuloEquals {
member: usize,
modulus: i64,
value: i64,
equals: bool,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PredictionMode {
Ll,
Sll,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct ParserRuleArg {
pub source_state: usize,
pub rule_index: usize,
pub value: i64,
pub inherit_local: bool,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct ParserMemberAction {
pub source_state: usize,
pub member: usize,
pub delta: i64,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct ParserReturnAction {
pub source_state: usize,
pub rule_index: usize,
pub name: &'static str,
pub value: i64,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ParserRuntimeOptions<'a> {
pub init_action_rules: &'a [usize],
pub track_alt_numbers: bool,
pub predicates: &'a [(usize, usize, ParserPredicate)],
pub rule_args: &'a [ParserRuleArg],
pub member_actions: &'a [ParserMemberAction],
pub return_actions: &'a [ParserReturnAction],
}
pub trait Parser: Recognizer {
fn build_parse_trees(&self) -> bool;
fn set_build_parse_trees(&mut self, build: bool);
fn report_diagnostic_errors(&self) -> bool {
false
}
fn set_report_diagnostic_errors(&mut self, _report: bool) {}
fn prediction_mode(&self) -> PredictionMode {
PredictionMode::Ll
}
fn set_prediction_mode(&mut self, _mode: PredictionMode) {}
}
#[derive(Debug)]
pub struct BaseParser<S> {
input: CommonTokenStream<S>,
data: RecognizerData,
build_parse_trees: bool,
report_diagnostic_errors: bool,
prediction_mode: PredictionMode,
prediction_diagnostics: Vec<ParserDiagnostic>,
reported_prediction_diagnostics: BTreeSet<(usize, usize, String)>,
int_members: BTreeMap<usize, i64>,
invoked_predicates: Vec<(usize, usize)>,
first_set_cache: FirstSetCache,
state_expected_cache: FxHashMap<usize, Rc<BTreeSet<i32>>>,
recovery_symbols_intern: FxHashMap<Rc<BTreeSet<i32>>, Rc<BTreeSet<i32>>>,
decision_lookahead_cache: FxHashMap<usize, Rc<DecisionLookahead>>,
empty_recovery_symbols: Rc<BTreeSet<i32>>,
fast_first_set_prefilter: bool,
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
struct RecognizeOutcome {
index: usize,
consumed_eof: bool,
alt_number: usize,
member_values: BTreeMap<usize, i64>,
return_values: BTreeMap<String, i64>,
diagnostics: Vec<ParserDiagnostic>,
decisions: Vec<usize>,
actions: Vec<ParserAction>,
nodes: Vec<RecognizedNode>,
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
enum RecognizedNode {
Token {
index: usize,
},
ErrorToken {
index: usize,
},
MissingToken {
token_type: i32,
at_index: usize,
text: String,
},
Rule {
rule_index: usize,
invoking_state: isize,
alt_number: usize,
start_index: usize,
stop_index: Option<usize>,
return_values: BTreeMap<String, i64>,
children: Vec<Self>,
},
LeftRecursiveBoundary {
rule_index: usize,
},
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
struct FastRecognizeOutcome {
index: usize,
consumed_eof: bool,
diagnostics: Vec<ParserDiagnostic>,
nodes: NodeList,
}
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
enum NodeList {
#[default]
Empty,
Cons {
head: Rc<FastRecognizedNode>,
tail: Rc<Self>,
},
}
impl NodeList {
const fn new() -> Self {
Self::Empty
}
fn cons(self, node: Rc<FastRecognizedNode>) -> Self {
Self::Cons {
head: node,
tail: Rc::new(self),
}
}
fn prepend(&mut self, node: Rc<FastRecognizedNode>) {
let owned = std::mem::take(self);
*self = owned.cons(node);
}
fn to_vec(&self) -> Vec<Rc<FastRecognizedNode>> {
let mut out = Vec::new();
let mut cursor = self;
while let Self::Cons { head, tail } = cursor {
out.push(Rc::clone(head));
cursor = tail.as_ref();
}
out
}
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
enum FastRecognizedNode {
Token {
index: usize,
},
ErrorToken {
index: usize,
},
MissingToken {
token_type: i32,
at_index: usize,
text: String,
},
Rule {
rule_index: usize,
invoking_state: isize,
start_index: usize,
stop_index: Option<usize>,
children: Vec<Rc<Self>>,
},
LeftRecursiveBoundary {
rule_index: usize,
},
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
struct ParserDiagnostic {
line: usize,
column: usize,
message: String,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct ExpectedTokens {
index: Option<usize>,
symbols: BTreeSet<i32>,
no_viable: Option<NoViableAlternative>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct NoViableAlternative {
start_index: usize,
error_index: usize,
}
impl ExpectedTokens {
fn record_transition(&mut self, index: usize, transition: &Transition, max_token_type: i32) {
let symbols = transition_expected_symbols(transition, max_token_type);
match self.index {
Some(current) if index < current => {}
Some(current) if index == current => self.symbols.extend(symbols),
_ => {
self.index = Some(index);
self.symbols = symbols;
}
}
}
const fn record_no_viable(&mut self, start_index: usize, error_index: usize) {
match self.no_viable {
Some(current) if error_index < current.error_index => {}
_ => {
self.no_viable = Some(NoViableAlternative {
start_index,
error_index,
});
}
}
}
}
fn transition_expected_symbols(transition: &Transition, max_token_type: i32) -> BTreeSet<i32> {
let mut symbols = BTreeSet::new();
match transition {
Transition::Atom { label, .. } => {
symbols.insert(*label);
}
Transition::Range { start, stop, .. } => {
symbols.extend(*start..=*stop);
}
Transition::Set { set, .. } => {
for (start, stop) in set.ranges() {
symbols.extend(*start..=*stop);
}
}
Transition::NotSet { set, .. } => {
symbols.extend((1..=max_token_type).filter(|symbol| !set.contains(*symbol)));
}
Transition::Wildcard { .. } => {
symbols.extend(1..=max_token_type);
}
Transition::Epsilon { .. }
| Transition::Rule { .. }
| Transition::Predicate { .. }
| Transition::Action { .. }
| Transition::Precedence { .. } => {}
}
symbols
}
fn state_expected_symbols(atn: &Atn, state_number: usize) -> BTreeSet<i32> {
let mut symbols = BTreeSet::new();
let mut stack = vec![state_number];
let mut visited = BTreeSet::new();
while let Some(current) = stack.pop() {
if !visited.insert(current) {
continue;
}
let Some(state) = atn.state(current) else {
continue;
};
for transition in &state.transitions {
let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
if transition_symbols.is_empty() {
if transition.is_epsilon() {
stack.push(transition.target());
}
} else {
symbols.extend(transition_symbols);
}
}
}
symbols
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct FirstSet {
symbols: BTreeSet<i32>,
nullable: bool,
}
type FirstSetCache = FxHashMap<(usize, usize), Rc<FirstSet>>;
#[derive(Debug, Default)]
struct DecisionLookahead {
transitions: Vec<TransitionLookSet>,
}
#[derive(Clone, Debug, Default)]
struct TransitionLookSet {
symbols: BTreeSet<i32>,
nullable: bool,
}
struct FirstSetCtx<'a> {
cache: &'a mut FirstSetCache,
in_progress: BTreeSet<(usize, usize)>,
hit_cycle: bool,
}
fn rule_first_set(
atn: &Atn,
target: usize,
rule_stop_state: usize,
cache: &mut FirstSetCache,
) -> Rc<FirstSet> {
if let Some(cached) = cache.get(&(target, rule_stop_state)) {
return Rc::clone(cached);
}
let mut ctx = FirstSetCtx {
cache,
in_progress: BTreeSet::new(),
hit_cycle: false,
};
rule_first_set_cached(atn, target, rule_stop_state, &mut ctx)
}
fn rule_first_set_cached(
atn: &Atn,
target: usize,
rule_stop_state: usize,
ctx: &mut FirstSetCtx<'_>,
) -> Rc<FirstSet> {
let key = (target, rule_stop_state);
if let Some(cached) = ctx.cache.get(&key) {
return Rc::clone(cached);
}
if !ctx.in_progress.insert(key) {
return Rc::new(FirstSet::default());
}
let saved_hit_cycle = ctx.hit_cycle;
ctx.hit_cycle = false;
let mut first = FirstSet::default();
let mut visited = BTreeSet::new();
rule_first_set_inner(atn, target, rule_stop_state, ctx, &mut visited, &mut first);
ctx.in_progress.remove(&key);
let entry = Rc::new(first);
if !ctx.hit_cycle {
ctx.cache.insert(key, Rc::clone(&entry));
}
ctx.hit_cycle = saved_hit_cycle || ctx.hit_cycle;
entry
}
fn transition_first_set(
atn: &Atn,
transition: &Transition,
rule_stop_state: usize,
cache: &mut FirstSetCache,
) -> TransitionLookSet {
match transition {
Transition::Atom { label, .. } => TransitionLookSet {
symbols: BTreeSet::from([*label]),
nullable: false,
},
Transition::Range { start, stop, .. } => TransitionLookSet {
symbols: (*start..=*stop).collect(),
nullable: false,
},
Transition::Set { set, .. } => TransitionLookSet {
symbols: set.ranges()
.iter()
.flat_map(|(start, stop)| *start..=*stop)
.collect(),
nullable: false,
},
Transition::NotSet { set, .. } => {
let max = atn.max_token_type();
TransitionLookSet {
symbols: (1..=max).filter(|symbol| !set.contains(*symbol)).collect(),
nullable: false,
}
}
Transition::Wildcard { .. } => TransitionLookSet {
symbols: (1..=atn.max_token_type()).collect(),
nullable: false,
},
Transition::Epsilon { target }
| Transition::Action { target, .. }
| Transition::Predicate { target, .. }
| Transition::Precedence { target, .. } => {
let first = rule_first_set(atn, *target, rule_stop_state, cache);
TransitionLookSet {
symbols: first.symbols.clone(),
nullable: first.nullable,
}
}
Transition::Rule {
target,
rule_index,
follow_state,
..
} => {
let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied() else {
return TransitionLookSet::default();
};
let child = rule_first_set(atn, *target, child_stop, cache);
let mut symbols = child.symbols.clone();
let nullable = if child.nullable {
let follow = rule_first_set(atn, *follow_state, rule_stop_state, cache);
symbols.extend(follow.symbols.iter().copied());
follow.nullable
} else {
false
};
TransitionLookSet { symbols, nullable }
}
}
}
fn should_skip_via_lookahead(
transition: &Transition,
transition_index: usize,
lookahead_filter: Option<&(i32, Rc<DecisionLookahead>)>,
index: usize,
expected: &mut ExpectedTokens,
) -> bool {
let prune_non_consuming = matches!(
transition,
Transition::Epsilon { .. }
| Transition::Action { .. }
| Transition::Predicate { .. }
| Transition::Rule { .. }
| Transition::Precedence { .. }
);
if !prune_non_consuming {
return false;
}
let Some((symbol, entry)) = lookahead_filter else {
return false;
};
let Some(set) = entry.transitions.get(transition_index) else {
return false;
};
if set.symbols.contains(symbol) || set.nullable {
return false;
}
if !set.symbols.is_empty() {
record_pruned_transition_expected(set, index, expected);
}
true
}
fn record_pruned_transition_expected(
set: &TransitionLookSet,
index: usize,
expected: &mut ExpectedTokens,
) {
match expected.index {
Some(current) if index < current => {}
Some(current) if index == current => {
expected.symbols.extend(set.symbols.iter().copied());
}
_ => {
expected.index = Some(index);
expected.symbols.clone_from(&set.symbols);
}
}
}
fn rule_first_set_inner(
atn: &Atn,
state_number: usize,
rule_stop_state: usize,
ctx: &mut FirstSetCtx<'_>,
visited: &mut BTreeSet<usize>,
first: &mut FirstSet,
) {
if !visited.insert(state_number) {
return;
}
if state_number == rule_stop_state {
first.nullable = true;
return;
}
let Some(state) = atn.state(state_number) else {
return;
};
for transition in &state.transitions {
let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
if !transition_symbols.is_empty() {
first.symbols.extend(transition_symbols);
continue;
}
match transition {
Transition::Epsilon { target }
| Transition::Action { target, .. }
| Transition::Predicate { target, .. }
| Transition::Precedence { target, .. } => {
rule_first_set_inner(atn, *target, rule_stop_state, ctx, visited, first);
}
Transition::Rule {
target,
rule_index,
follow_state,
..
} => {
let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied() else {
continue;
};
let child_key = (*target, child_stop);
if ctx.in_progress.contains(&child_key) && !ctx.cache.contains_key(&child_key) {
ctx.hit_cycle = true;
}
let child = rule_first_set_cached(atn, *target, child_stop, ctx);
first.symbols.extend(child.symbols.iter().copied());
if child.nullable {
rule_first_set_inner(atn, *follow_state, rule_stop_state, ctx, visited, first);
}
}
Transition::Atom { .. }
| Transition::Range { .. }
| Transition::Set { .. }
| Transition::NotSet { .. }
| Transition::Wildcard { .. } => {}
}
}
}
fn state_sync_symbols(atn: &Atn, state_number: usize, stop_state: usize) -> BTreeSet<i32> {
let mut symbols = BTreeSet::new();
state_sync_symbols_inner(
atn,
state_number,
stop_state,
&mut BTreeSet::new(),
&mut symbols,
);
symbols
}
fn state_sync_symbols_inner(
atn: &Atn,
state_number: usize,
stop_state: usize,
visited: &mut BTreeSet<usize>,
symbols: &mut BTreeSet<i32>,
) {
if !visited.insert(state_number) {
return;
}
if state_number == stop_state {
symbols.insert(TOKEN_EOF);
return;
}
let Some(state) = atn.state(state_number) else {
return;
};
for transition in &state.transitions {
let transition_symbols = transition_expected_symbols(transition, atn.max_token_type());
if transition_symbols.is_empty() {
match transition {
Transition::Rule { target, .. }
| Transition::Epsilon { target }
| Transition::Action { target, .. }
| Transition::Predicate { target, .. }
| Transition::Precedence { target, .. } => {
state_sync_symbols_inner(atn, *target, stop_state, visited, symbols);
}
Transition::Atom { .. }
| Transition::Range { .. }
| Transition::Set { .. }
| Transition::NotSet { .. }
| Transition::Wildcard { .. } => {}
}
} else {
symbols.extend(transition_symbols);
}
}
}
fn next_recovery_context(
atn: &Atn,
state: &AtnState,
inherited: &BTreeSet<i32>,
inherited_state: Option<usize>,
) -> (BTreeSet<i32>, Option<usize>) {
let state_symbols = state_expected_symbols(atn, state.state_number);
if state.transitions.len() > 1 && !state_symbols.is_empty() {
let mut symbols = state_symbols;
symbols.extend(inherited.iter().copied());
return (symbols, Some(state.state_number));
}
(inherited.clone(), inherited_state)
}
fn recovery_expected_symbols(
atn: &Atn,
state_number: usize,
inherited: &BTreeSet<i32>,
) -> BTreeSet<i32> {
let mut symbols = state_expected_symbols(atn, state_number);
symbols.extend(inherited.iter().copied());
symbols
}
fn fast_next_recovery_context<S>(
parser: &mut BaseParser<S>,
atn: &Atn,
state: &AtnState,
inherited: &Rc<BTreeSet<i32>>,
inherited_state: Option<usize>,
) -> (Rc<BTreeSet<i32>>, Option<usize>)
where
S: TokenSource,
{
if state.transitions.len() <= 1 {
return (Rc::clone(inherited), inherited_state);
}
let state_symbols = parser.cached_state_expected_symbols(atn, state.state_number);
if state_symbols.is_empty() {
return (Rc::clone(inherited), inherited_state);
}
if inherited.is_empty() {
return (state_symbols, Some(state.state_number));
}
if Rc::ptr_eq(&state_symbols, inherited) {
return (state_symbols, Some(state.state_number));
}
let mut combined = (*state_symbols).clone();
combined.extend(inherited.iter().copied());
(parser.intern_recovery_symbols(combined), Some(state.state_number))
}
fn fast_recovery_expected_symbols<S>(
parser: &mut BaseParser<S>,
atn: &Atn,
state_number: usize,
inherited: &Rc<BTreeSet<i32>>,
) -> Rc<BTreeSet<i32>>
where
S: TokenSource,
{
let cached = parser.cached_state_expected_symbols(atn, state_number);
if inherited.is_empty() {
return cached;
}
if cached.is_empty() {
return Rc::clone(inherited);
}
if Rc::ptr_eq(&cached, inherited) {
return cached;
}
let mut combined = (*cached).clone();
combined.extend(inherited.iter().copied());
parser.intern_recovery_symbols(combined)
}
fn apply_member_actions(
source_state: usize,
actions: &[ParserMemberAction],
values: &mut BTreeMap<usize, i64>,
) {
for action in actions
.iter()
.filter(|action| action.source_state == source_state)
{
*values.entry(action.member).or_default() += action.delta;
}
}
fn member_values_after_action(
source_state: usize,
actions: &[ParserMemberAction],
values: &BTreeMap<usize, i64>,
) -> BTreeMap<usize, i64> {
let mut values = values.clone();
apply_member_actions(source_state, actions, &mut values);
values
}
fn return_values_after_action(
source_state: usize,
rule_index: usize,
actions: &[ParserReturnAction],
values: &BTreeMap<String, i64>,
) -> BTreeMap<String, i64> {
let mut values = values.clone();
for action in actions
.iter()
.filter(|action| action.source_state == source_state && action.rule_index == rule_index)
{
values.insert(action.name.to_owned(), action.value);
}
values
}
fn rule_local_int_arg(
rule_args: &[ParserRuleArg],
source_state: usize,
rule_index: usize,
local_int_arg: Option<(usize, i64)>,
) -> Option<(usize, i64)> {
rule_args
.iter()
.find(|arg| arg.source_state == source_state && arg.rule_index == rule_index)
.map(|arg| {
let value = if arg.inherit_local {
local_int_arg.map_or(arg.value, |(_, value)| value)
} else {
arg.value
};
(rule_index, value)
})
}
fn stop_outcome(
index: usize,
consumed_eof: bool,
rule_alt_number: usize,
member_values: BTreeMap<usize, i64>,
return_values: BTreeMap<String, i64>,
) -> Vec<RecognizeOutcome> {
vec![RecognizeOutcome {
index,
consumed_eof,
alt_number: rule_alt_number,
member_values,
return_values,
diagnostics: Vec::new(),
decisions: Vec::new(),
actions: Vec::new(),
nodes: Vec::new(),
}]
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct RecognizeRequest<'a> {
state_number: usize,
stop_state: usize,
index: usize,
rule_start_index: usize,
decision_start_index: Option<usize>,
init_action_rules: &'a BTreeSet<usize>,
predicates: &'a [(usize, usize, ParserPredicate)],
rule_args: &'a [ParserRuleArg],
member_actions: &'a [ParserMemberAction],
return_actions: &'a [ParserReturnAction],
local_int_arg: Option<(usize, i64)>,
member_values: BTreeMap<usize, i64>,
return_values: BTreeMap<String, i64>,
rule_alt_number: usize,
track_alt_numbers: bool,
consumed_eof: bool,
precedence: i32,
depth: usize,
recovery_symbols: BTreeSet<i32>,
recovery_state: Option<usize>,
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
struct RecognizeKey {
state_number: usize,
stop_state: usize,
index: usize,
rule_start_index: usize,
decision_start_index: Option<usize>,
local_int_arg: Option<(usize, i64)>,
member_values: BTreeMap<usize, i64>,
return_values: BTreeMap<String, i64>,
rule_alt_number: usize,
track_alt_numbers: bool,
consumed_eof: bool,
precedence: i32,
recovery_symbols: BTreeSet<i32>,
recovery_state: Option<usize>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct EpsilonActionStep {
source_state: usize,
target: usize,
action_rule_index: Option<usize>,
left_recursive_boundary: Option<usize>,
decision: Option<usize>,
decision_start_index: Option<usize>,
alt_number: usize,
recovery_symbols: BTreeSet<i32>,
recovery_state: Option<usize>,
}
struct RecognizeScratch<'a> {
visiting: &'a mut BTreeSet<RecognizeKey>,
memo: &'a mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
expected: &'a mut ExpectedTokens,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct FastRecognizeRequest {
state_number: usize,
stop_state: usize,
index: usize,
rule_start_index: usize,
decision_start_index: Option<usize>,
precedence: i32,
depth: usize,
recovery_symbols: Rc<BTreeSet<i32>>,
recovery_state: Option<usize>,
}
#[derive(Clone, Debug)]
struct FastRecognizeKey {
state_number: usize,
stop_state: usize,
index: usize,
rule_start_index: usize,
decision_start_index: Option<usize>,
precedence: i32,
recovery_symbols: Rc<BTreeSet<i32>>,
recovery_state: Option<usize>,
}
impl PartialEq for FastRecognizeKey {
fn eq(&self, other: &Self) -> bool {
if self.state_number != other.state_number
|| self.stop_state != other.stop_state
|| self.index != other.index
|| self.rule_start_index != other.rule_start_index
|| self.decision_start_index != other.decision_start_index
|| self.precedence != other.precedence
|| self.recovery_state != other.recovery_state
{
return false;
}
debug_assert!(
Rc::ptr_eq(&self.recovery_symbols, &other.recovery_symbols)
|| self.recovery_symbols != other.recovery_symbols,
"FastRecognizeKey: recovery_symbols compared by content; interner invariant violated",
);
Rc::ptr_eq(&self.recovery_symbols, &other.recovery_symbols)
}
}
impl Eq for FastRecognizeKey {}
impl Hash for FastRecognizeKey {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.state_number.hash(hasher);
self.stop_state.hash(hasher);
self.index.hash(hasher);
self.rule_start_index.hash(hasher);
self.decision_start_index.hash(hasher);
self.precedence.hash(hasher);
self.recovery_state.hash(hasher);
(Rc::as_ptr(&self.recovery_symbols) as usize).hash(hasher);
}
}
struct FastRecoveryRequest<'a, 'b> {
atn: &'a Atn,
transition: &'a Transition,
expected_symbols: Rc<BTreeSet<i32>>,
target: usize,
request: FastRecognizeRequest,
visiting: &'b mut FxHashSet<FastRecognizeKey>,
memo: &'b mut FxHashMap<FastRecognizeKey, Vec<FastRecognizeOutcome>>,
expected: &'b mut ExpectedTokens,
}
struct FastCurrentTokenDeletionRequest<'a, 'b> {
atn: &'a Atn,
expected_symbols: Rc<BTreeSet<i32>>,
request: FastRecognizeRequest,
visiting: &'b mut FxHashSet<FastRecognizeKey>,
memo: &'b mut FxHashMap<FastRecognizeKey, Vec<FastRecognizeOutcome>>,
expected: &'b mut ExpectedTokens,
}
struct RecoveryRequest<'a, 'b> {
atn: &'a Atn,
transition: &'a Transition,
expected_symbols: BTreeSet<i32>,
target: usize,
request: RecognizeRequest<'a>,
visiting: &'b mut BTreeSet<RecognizeKey>,
memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
expected: &'b mut ExpectedTokens,
}
struct CurrentTokenDeletionRequest<'a, 'b> {
atn: &'a Atn,
expected_symbols: BTreeSet<i32>,
request: RecognizeRequest<'a>,
visiting: &'b mut BTreeSet<RecognizeKey>,
memo: &'b mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
expected: &'b mut ExpectedTokens,
}
struct ConsumingFailureFallback<'a> {
atn: &'a Atn,
target: usize,
request: RecognizeRequest<'a>,
symbol: i32,
expected_symbols: BTreeSet<i32>,
decision_start_index: Option<usize>,
decision: Option<usize>,
}
struct ChildRuleFailureRecovery<'a> {
atn: &'a Atn,
rule_index: usize,
start_index: usize,
follow_state: usize,
stop_state: usize,
member_values: BTreeMap<usize, i64>,
expected: &'a ExpectedTokens,
}
#[derive(Clone, Copy, Debug)]
struct PredicateEval<'a> {
index: usize,
rule_index: usize,
pred_index: usize,
predicates: &'a [(usize, usize, ParserPredicate)],
local_int_arg: Option<(usize, i64)>,
member_values: &'a BTreeMap<usize, i64>,
}
struct PredicateFailureRecovery<'a> {
rule_index: usize,
index: usize,
message: &'a str,
member_values: BTreeMap<usize, i64>,
return_values: BTreeMap<String, i64>,
rule_alt_number: usize,
}
impl<S> BaseParser<S>
where
S: TokenSource,
{
pub fn new(input: CommonTokenStream<S>, data: RecognizerData) -> Self {
Self {
input,
data,
build_parse_trees: true,
report_diagnostic_errors: false,
prediction_mode: PredictionMode::Ll,
prediction_diagnostics: Vec::new(),
reported_prediction_diagnostics: BTreeSet::new(),
int_members: BTreeMap::new(),
invoked_predicates: Vec::new(),
first_set_cache: FxHashMap::default(),
state_expected_cache: FxHashMap::default(),
recovery_symbols_intern: FxHashMap::default(),
decision_lookahead_cache: FxHashMap::default(),
empty_recovery_symbols: Rc::new(BTreeSet::new()),
fast_first_set_prefilter: true,
}
}
pub const fn input(&mut self) -> &mut CommonTokenStream<S> {
&mut self.input
}
pub fn la(&mut self, offset: isize) -> i32 {
self.input.la_token(offset)
}
pub fn consume(&mut self) {
IntStream::consume(&mut self.input);
}
pub fn set_int_member(&mut self, member: usize, value: i64) {
self.int_members.insert(member, value);
}
pub fn int_member(&self, member: usize) -> Option<i64> {
self.int_members.get(&member).copied()
}
pub fn add_int_member(&mut self, member: usize, delta: i64) -> i64 {
let value = self.int_members.entry(member).or_default();
*value += delta;
*value
}
pub fn match_token(&mut self, token_type: i32) -> Result<ParseTree, AntlrError> {
let current = self
.input
.lt(1)
.cloned()
.ok_or_else(|| AntlrError::ParserError {
line: 0,
column: 0,
message: "missing current token".to_owned(),
})?;
if current.token_type() == token_type {
self.consume();
Ok(ParseTree::Terminal(TerminalNode::new(current)))
} else {
Err(AntlrError::MismatchedInput {
expected: self.vocabulary().display_name(token_type),
found: self.vocabulary().display_name(current.token_type()),
})
}
}
pub fn match_eof(&mut self) -> Result<ParseTree, AntlrError> {
self.match_token(TOKEN_EOF)
}
pub const fn rule_node(&self, context: ParserRuleContext) -> ParseTree {
ParseTree::Rule(RuleNode::new(context))
}
pub fn parse_atn_rule(
&mut self,
atn: &Atn,
rule_index: usize,
) -> Result<ParseTree, AntlrError> {
let start_state = atn
.rule_to_start_state()
.get(rule_index)
.copied()
.ok_or_else(|| {
AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
})?;
let stop_state = atn
.rule_to_stop_state()
.get(rule_index)
.copied()
.filter(|state| *state != usize::MAX)
.ok_or_else(|| {
AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
})?;
let start_index = self.current_visible_index();
self.clear_prediction_diagnostics();
self.reset_per_parse_caches();
let first_pass = self.fast_recognize_top(atn, start_state, stop_state, start_index);
let needs_retry = match &first_pass {
Err(_) => true,
Ok((outcome, _)) => !outcome.diagnostics.is_empty(),
};
let (outcome, _expected) = if needs_retry {
self.fast_first_set_prefilter = false;
let retry = self.fast_recognize_top(atn, start_state, stop_state, start_index);
self.fast_first_set_prefilter = true;
select_better_top_outcome(first_pass, retry).map_err(|expected| {
let error = self.recognition_error(rule_index, start_index, &expected);
report_token_source_errors(&self.input.drain_source_errors());
error
})?
} else {
first_pass.expect("first_pass is Ok in the no-retry branch")
};
report_parser_diagnostics(&self.prediction_diagnostics);
report_parser_diagnostics(&outcome.diagnostics);
report_token_source_errors(&self.input.drain_source_errors());
let mut context = ParserRuleContext::new(rule_index, self.state());
if let Some(token) = self.token_at(start_index) {
context.set_start(token);
}
if let Some(token) = self.rule_stop_token(outcome.index, outcome.consumed_eof) {
context.set_stop(token);
}
if self.build_parse_trees {
let folded = fold_fast_left_recursive_boundaries(outcome.nodes.to_vec());
for node in &folded {
context.add_child(self.fast_recognized_node_tree(node.as_ref())?);
}
}
self.input.seek(outcome.index);
Ok(self.rule_node(context))
}
fn fast_recognize_top(
&mut self,
atn: &Atn,
start_state: usize,
stop_state: usize,
start_index: usize,
) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
let mut visiting = FxHashSet::default();
let mut memo = FxHashMap::default();
let mut expected = ExpectedTokens::default();
let empty_recovery = self.empty_recovery_symbols();
let outcomes = self.recognize_state_fast(
atn,
FastRecognizeRequest {
state_number: start_state,
stop_state,
index: start_index,
rule_start_index: start_index,
decision_start_index: None,
precedence: 0,
depth: 0,
recovery_symbols: empty_recovery,
recovery_state: None,
},
&mut visiting,
&mut memo,
&mut expected,
);
match select_best_fast_outcome(outcomes.into_iter(), self.prediction_mode) {
Some(outcome) => Ok((outcome, expected)),
None => Err(expected),
}
}
fn fast_recognized_node_tree(
&mut self,
node: &FastRecognizedNode,
) -> Result<ParseTree, AntlrError> {
match node {
FastRecognizedNode::Token { index } => {
let token =
self.input
.get(*index)
.cloned()
.ok_or_else(|| AntlrError::ParserError {
line: 0,
column: 0,
message: format!("missing token at index {index}"),
})?;
Ok(ParseTree::Terminal(TerminalNode::new(token)))
}
FastRecognizedNode::ErrorToken { index } => {
let token =
self.input
.get(*index)
.cloned()
.ok_or_else(|| AntlrError::ParserError {
line: 0,
column: 0,
message: format!("missing error token at index {index}"),
})?;
Ok(ParseTree::Error(ErrorNode::new(token)))
}
FastRecognizedNode::MissingToken {
token_type,
at_index,
text,
} => {
let current = self.token_at(*at_index);
let token = CommonToken::new(*token_type)
.with_text(text)
.with_span(usize::MAX, usize::MAX)
.with_position(
current.as_ref().map(Token::line).unwrap_or_default(),
current.as_ref().map(Token::column).unwrap_or_default(),
);
Ok(ParseTree::Error(ErrorNode::new(token)))
}
FastRecognizedNode::Rule {
rule_index,
invoking_state,
start_index,
stop_index,
children,
} => {
let mut context = ParserRuleContext::new(*rule_index, *invoking_state);
if let Some(token) = self.token_at(*start_index) {
context.set_start(token);
}
if let Some(token) = stop_index.and_then(|index| self.token_at(index)) {
context.set_stop(token);
}
for child in children {
context.add_child(self.fast_recognized_node_tree(child.as_ref())?);
}
Ok(self.rule_node(context))
}
FastRecognizedNode::LeftRecursiveBoundary { rule_index } => Err(AntlrError::Unsupported(
format!("unfolded left-recursive boundary for rule {rule_index}"),
)),
}
}
pub fn parse_atn_rule_with_actions(
&mut self,
atn: &Atn,
rule_index: usize,
) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
self.parse_atn_rule_with_action_options(atn, rule_index, &[], false)
}
pub fn parse_atn_rule_with_action_inits(
&mut self,
atn: &Atn,
rule_index: usize,
init_action_rules: &[usize],
) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
self.parse_atn_rule_with_action_options(atn, rule_index, init_action_rules, false)
}
pub fn parse_atn_rule_with_action_options(
&mut self,
atn: &Atn,
rule_index: usize,
init_action_rules: &[usize],
track_alt_numbers: bool,
) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
self.parse_atn_rule_with_runtime_options(
atn,
rule_index,
ParserRuntimeOptions {
init_action_rules,
track_alt_numbers,
..ParserRuntimeOptions::default()
},
)
}
pub fn parse_atn_rule_with_runtime_options(
&mut self,
atn: &Atn,
rule_index: usize,
options: ParserRuntimeOptions<'_>,
) -> Result<(ParseTree, Vec<ParserAction>), AntlrError> {
let ParserRuntimeOptions {
init_action_rules,
track_alt_numbers,
predicates,
rule_args,
member_actions,
return_actions,
} = options;
let start_state = atn
.rule_to_start_state()
.get(rule_index)
.copied()
.ok_or_else(|| {
AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
})?;
let stop_state = atn
.rule_to_stop_state()
.get(rule_index)
.copied()
.filter(|state| *state != usize::MAX)
.ok_or_else(|| {
AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
})?;
let start_index = self.current_visible_index();
self.clear_prediction_diagnostics();
self.reset_per_parse_caches();
let init_action_rules = init_action_rules.iter().copied().collect::<BTreeSet<_>>();
let mut visiting = BTreeSet::new();
let mut memo = BTreeMap::new();
let mut expected = ExpectedTokens::default();
let member_values = self.int_members.clone();
let return_values = BTreeMap::new();
let outcomes = self.recognize_state(
atn,
RecognizeRequest {
state_number: start_state,
stop_state,
index: start_index,
rule_start_index: start_index,
decision_start_index: None,
init_action_rules: &init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg: None,
member_values,
return_values,
rule_alt_number: 0,
track_alt_numbers,
consumed_eof: false,
precedence: 0,
depth: 0,
recovery_symbols: BTreeSet::new(),
recovery_state: None,
},
&mut visiting,
&mut memo,
&mut expected,
);
let Some(outcome) = select_best_outcome(outcomes.into_iter(), self.prediction_mode) else {
let error = self.recognition_error(rule_index, start_index, &expected);
report_token_source_errors(&self.input.drain_source_errors());
return Err(error);
};
report_parser_diagnostics(&self.prediction_diagnostics);
report_parser_diagnostics(&outcome.diagnostics);
report_token_source_errors(&self.input.drain_source_errors());
let mut actions = outcome.actions;
if init_action_rules.contains(&rule_index) {
actions.insert(
0,
ParserAction::new_rule_init(rule_index, start_index, Some(start_state)),
);
}
let mut context = ParserRuleContext::new(rule_index, self.state());
if track_alt_numbers {
context.set_alt_number(outcome.alt_number);
}
for (name, value) in outcome.return_values {
context.set_int_return(name, value);
}
if let Some(token) = self.token_at(start_index) {
context.set_start(token);
}
if let Some(token) = self.rule_stop_token(outcome.index, outcome.consumed_eof) {
context.set_stop(token);
}
if self.build_parse_trees {
let nodes = fold_left_recursive_boundaries(outcome.nodes);
for node in &nodes {
context.add_child(self.recognized_node_tree(node, track_alt_numbers)?);
}
}
self.input.seek(outcome.index);
Ok((self.rule_node(context), actions))
}
pub fn parse_interpreted_rule(&mut self, rule_index: usize) -> Result<ParseTree, AntlrError> {
let mut context = ParserRuleContext::new(rule_index, self.state());
while self.la(1) != TOKEN_EOF {
let token_type = self.la(1);
let child = self.match_token(token_type)?;
if self.build_parse_trees {
context.add_child(child);
}
}
if self.build_parse_trees {
context.add_child(self.match_eof()?);
}
Ok(self.rule_node(context))
}
fn recognition_error(
&mut self,
rule_index: usize,
start_index: usize,
expected: &ExpectedTokens,
) -> AntlrError {
let (index, message) = self.expected_error_message(rule_index, start_index, expected);
self.input.seek(index);
let current = self.input.lt(1).cloned();
let line = current.as_ref().map(Token::line).unwrap_or_default();
let column = current.as_ref().map(Token::column).unwrap_or_default();
AntlrError::ParserError {
line,
column,
message,
}
}
fn expected_error_message(
&mut self,
rule_index: usize,
start_index: usize,
expected: &ExpectedTokens,
) -> (usize, String) {
let index = expected
.index
.or_else(|| expected.no_viable.map(|no_viable| no_viable.error_index))
.unwrap_or_else(|| self.input.index());
self.input.seek(index);
let current = self.input.lt(1).cloned();
let message = if expected
.no_viable
.as_ref()
.is_some_and(|no_viable| no_viable.error_index == index)
{
let start = expected
.no_viable
.as_ref()
.map_or(start_index, |no_viable| no_viable.start_index);
let text = display_input_text(&self.input.text(start, index));
format!("no viable alternative at input '{text}'")
} else if expected.symbols.is_empty() {
if expected.index.is_some() {
let found = current
.as_ref()
.map_or_else(|| "'<EOF>'".to_owned(), token_input_display);
if current
.as_ref()
.is_some_and(|token| token.token_type() == TOKEN_EOF)
{
format!(
"missing {} at {found}",
self.expected_symbols_display(&expected.symbols)
)
} else {
format!("mismatched input {found}")
}
} else {
format!("no viable alternative while parsing rule {rule_index}")
}
} else {
format!(
"mismatched input {} expecting {}",
current
.as_ref()
.map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
self.expected_symbols_display(&expected.symbols)
)
};
(index, message)
}
fn child_rule_failure_recovery(
&mut self,
rule_index: usize,
start_index: usize,
sync_symbols: &BTreeSet<i32>,
member_values: BTreeMap<usize, i64>,
expected: &ExpectedTokens,
) -> Option<RecognizeOutcome> {
let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
let token = self.token_at(error_index);
let mut next_index = error_index;
loop {
let symbol = self.token_type_at(next_index);
if sync_symbols.contains(&symbol) {
if next_index == error_index {
return None;
}
break;
}
if symbol == TOKEN_EOF {
break;
}
let after = self.consume_index(next_index, symbol);
if after == next_index {
break;
}
next_index = after;
}
Some(RecognizeOutcome {
index: next_index,
consumed_eof: false,
alt_number: 0,
member_values,
return_values: BTreeMap::new(),
diagnostics: vec![diagnostic_for_token(token.as_ref(), message)],
decisions: Vec::new(),
actions: Vec::new(),
nodes: vec![RecognizedNode::ErrorToken { index: error_index }],
})
}
fn child_rule_failure_recovery_outcomes(
&mut self,
request: ChildRuleFailureRecovery<'_>,
) -> Vec<RecognizeOutcome> {
let sync_symbols =
state_sync_symbols(request.atn, request.follow_state, request.stop_state);
self.child_rule_failure_recovery(
request.rule_index,
request.start_index,
&sync_symbols,
request.member_values,
request.expected,
)
.into_iter()
.collect()
}
fn expected_symbols_display(&self, symbols: &BTreeSet<i32>) -> String {
expected_symbols_display(symbols, self.vocabulary())
}
fn single_token_deletion(
&mut self,
transition: &Transition,
index: usize,
max_token_type: i32,
expected_symbols: &BTreeSet<i32>,
) -> Option<(ParserDiagnostic, usize, i32)> {
let current_symbol = self.token_type_at(index);
if current_symbol == TOKEN_EOF {
return None;
}
let next_index = self.consume_index(index, current_symbol);
if next_index == index {
return None;
}
let next_symbol = self.token_type_at(next_index);
if !transition.matches(next_symbol, 1, max_token_type) {
return None;
}
let transition_expected = transition_expected_symbols(transition, max_token_type);
let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
&transition_expected
} else {
expected_symbols
});
let current = self.token_at(index);
let message = format!(
"extraneous input {} expecting {expected_display}",
current
.as_ref()
.map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
);
Some((
diagnostic_for_token(current.as_ref(), message),
next_index,
next_symbol,
))
}
fn current_token_deletion(
&mut self,
index: usize,
expected_symbols: &BTreeSet<i32>,
) -> Option<(ParserDiagnostic, usize, Vec<usize>)> {
if expected_symbols.is_empty() {
return None;
}
let current_symbol = self.token_type_at(index);
if current_symbol == TOKEN_EOF {
return None;
}
let current = self.token_at(index);
let message = format!(
"extraneous input {} expecting {}",
current
.as_ref()
.map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
self.expected_symbols_display(expected_symbols)
);
let diagnostic = diagnostic_for_token(current.as_ref(), message);
let mut skipped = Vec::new();
let mut cursor = index;
loop {
let symbol = self.token_type_at(cursor);
if symbol == TOKEN_EOF {
return None;
}
skipped.push(cursor);
let next_index = self.consume_index(cursor, symbol);
if next_index == cursor {
return None;
}
let next_symbol = self.token_type_at(next_index);
if expected_symbols.contains(&next_symbol) {
return Some((diagnostic, next_index, skipped));
}
cursor = next_index;
}
}
fn single_token_insertion(
&mut self,
transition: &Transition,
index: usize,
max_token_type: i32,
expected_symbols: &BTreeSet<i32>,
follow_symbols: &BTreeSet<i32>,
) -> Option<(ParserDiagnostic, i32, String)> {
let current_symbol = self.token_type_at(index);
if !follow_symbols.contains(¤t_symbol) {
return None;
}
let transition_expected = transition_expected_symbols(transition, max_token_type);
let token_type = transition_expected.iter().next().copied()?;
let expected_display = self.expected_symbols_display(if expected_symbols.is_empty() {
&transition_expected
} else {
expected_symbols
});
let mut token_symbols = BTreeSet::new();
token_symbols.insert(token_type);
let missing_token_display = self.expected_symbols_display(&token_symbols);
let current = self.token_at(index);
let message = format!(
"missing {expected_display} at {}",
current
.as_ref()
.map_or_else(|| "'<EOF>'".to_owned(), token_input_display)
);
let text = format!("<missing {missing_token_display}>");
Some((
diagnostic_for_token(current.as_ref(), message),
token_type,
text,
))
}
fn fast_single_token_deletion_recovery(
&mut self,
recovery: FastRecoveryRequest<'_, '_>,
) -> Vec<FastRecognizeOutcome> {
let FastRecoveryRequest {
atn,
transition,
expected_symbols,
target,
request,
visiting,
memo,
expected,
} = recovery;
let FastRecognizeRequest {
stop_state,
index,
rule_start_index,
decision_start_index,
precedence,
depth,
..
} = request;
let Some((diagnostic, next_index, next_symbol)) =
self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
else {
return Vec::new();
};
let after_next = self.consume_index(next_index, next_symbol);
let empty_recovery = self.empty_recovery_symbols();
self.recognize_state_fast(
atn,
FastRecognizeRequest {
state_number: target,
stop_state,
index: after_next,
rule_start_index,
decision_start_index,
precedence,
depth: depth + 1,
recovery_symbols: empty_recovery,
recovery_state: None,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
outcome.consumed_eof |= next_symbol == TOKEN_EOF;
outcome.diagnostics.insert(0, diagnostic.clone());
outcome
.nodes
.prepend(Rc::new(FastRecognizedNode::Token { index: next_index }));
outcome
.nodes
.prepend(Rc::new(FastRecognizedNode::ErrorToken { index }));
outcome
})
.collect()
}
fn fast_single_token_insertion_recovery(
&mut self,
recovery: FastRecoveryRequest<'_, '_>,
) -> Vec<FastRecognizeOutcome> {
let FastRecoveryRequest {
atn,
transition,
expected_symbols,
target,
request,
visiting,
memo,
expected,
} = recovery;
let FastRecognizeRequest {
stop_state,
index,
rule_start_index,
decision_start_index,
precedence,
depth,
..
} = request;
let follow_symbols = self.cached_state_expected_symbols(atn, transition.target());
let Some((diagnostic, token_type, text)) = self.single_token_insertion(
transition,
index,
atn.max_token_type(),
&expected_symbols,
&follow_symbols,
) else {
return Vec::new();
};
let empty_recovery = self.empty_recovery_symbols();
self.recognize_state_fast(
atn,
FastRecognizeRequest {
state_number: target,
stop_state,
index,
rule_start_index,
decision_start_index,
precedence,
depth: depth + 1,
recovery_symbols: empty_recovery,
recovery_state: None,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
outcome.diagnostics.insert(0, diagnostic.clone());
outcome.nodes.prepend(Rc::new(FastRecognizedNode::MissingToken {
token_type,
at_index: index,
text: text.clone(),
}));
outcome
})
.collect()
}
fn fast_current_token_deletion_recovery(
&mut self,
recovery: FastCurrentTokenDeletionRequest<'_, '_>,
) -> Vec<FastRecognizeOutcome> {
let FastCurrentTokenDeletionRequest {
atn,
expected_symbols,
mut request,
visiting,
memo,
expected,
} = recovery;
if request.index == request.rule_start_index {
return Vec::new();
}
let Some((diagnostic, next_index, skipped)) =
self.current_token_deletion(request.index, &expected_symbols)
else {
return Vec::new();
};
request.state_number = request.recovery_state.unwrap_or(request.state_number);
request.index = next_index;
request.depth += 1;
request.recovery_state = None;
self.recognize_state_fast(atn, request, visiting, memo, expected)
.into_iter()
.map(|mut outcome| {
outcome.diagnostics.insert(0, diagnostic.clone());
for index in skipped.iter().rev() {
outcome
.nodes
.prepend(Rc::new(FastRecognizedNode::ErrorToken { index: *index }));
}
outcome
})
.collect()
}
fn recognize_state_fast(
&mut self,
atn: &Atn,
request: FastRecognizeRequest,
visiting: &mut FxHashSet<FastRecognizeKey>,
memo: &mut FxHashMap<FastRecognizeKey, Vec<FastRecognizeOutcome>>,
expected: &mut ExpectedTokens,
) -> Vec<FastRecognizeOutcome> {
let FastRecognizeRequest {
state_number,
stop_state,
index,
rule_start_index,
decision_start_index,
precedence,
depth,
recovery_symbols,
recovery_state,
} = request;
if depth > RECOGNITION_DEPTH_LIMIT {
return Vec::new();
}
if state_number == stop_state {
return vec![FastRecognizeOutcome {
index,
consumed_eof: false,
diagnostics: Vec::new(),
nodes: NodeList::new(),
}];
}
let key = FastRecognizeKey {
state_number,
stop_state,
index,
rule_start_index,
decision_start_index,
precedence,
recovery_symbols: Rc::clone(&recovery_symbols),
recovery_state,
};
if let Some(outcomes) = memo.get(&key) {
return outcomes.clone();
}
let visit_key = key.clone();
if !visiting.insert(visit_key.clone()) {
return Vec::new();
}
let Some(state) = atn.state(state_number) else {
visiting.remove(&visit_key);
return Vec::new();
};
let next_decision_start_index = if starts_prediction_decision(state) {
Some(index)
} else {
decision_start_index
};
let (epsilon_recovery_symbols, epsilon_recovery_state) =
fast_next_recovery_context(self, atn, state, &recovery_symbols, recovery_state);
let lookahead_filter = if state.transitions.len() > 1
&& self.fast_first_set_prefilter
&& !state.precedence_rule_decision
&& state.kind != AtnStateKind::RuleStart
{
state.rule_index.and_then(|rule_index| {
atn.rule_to_stop_state().get(rule_index).copied()
}).map(|rule_stop| {
let symbol = self.token_type_at(index);
let entry = self.cached_decision_lookahead(atn, state, rule_stop);
(symbol, entry)
})
} else {
None
};
let lookahead_filter = lookahead_filter.as_ref();
let transition_count = state.transitions.len();
let mut outcomes = Vec::with_capacity(transition_count);
for (transition_index, transition) in state.transitions.iter().enumerate() {
if should_skip_via_lookahead(
transition,
transition_index,
lookahead_filter,
index,
expected,
) {
continue;
}
match transition {
Transition::Epsilon { target }
| Transition::Predicate { target, .. }
| Transition::Action { target, .. } => {
let boundary = left_recursive_boundary(atn, state, *target);
outcomes.extend(
self.recognize_state_fast(
atn,
FastRecognizeRequest {
state_number: *target,
stop_state,
index,
rule_start_index,
decision_start_index: next_decision_start_index,
precedence,
depth: depth + 1,
recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
recovery_state: epsilon_recovery_state,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
if let Some(rule_index) = boundary {
outcome.nodes.prepend(Rc::new(
FastRecognizedNode::LeftRecursiveBoundary { rule_index },
));
}
outcome
}),
);
}
Transition::Precedence {
target,
precedence: transition_precedence,
} => {
if *transition_precedence >= precedence {
let boundary = left_recursive_boundary(atn, state, *target);
outcomes.extend(
self.recognize_state_fast(
atn,
FastRecognizeRequest {
state_number: *target,
stop_state,
index,
rule_start_index,
decision_start_index: next_decision_start_index,
precedence,
depth: depth + 1,
recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
recovery_state: epsilon_recovery_state,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
if let Some(rule_index) = boundary {
outcome.nodes.prepend(Rc::new(
FastRecognizedNode::LeftRecursiveBoundary {
rule_index,
},
));
}
outcome
}),
);
}
}
Transition::Rule {
target,
rule_index,
follow_state,
precedence: rule_precedence,
..
} => {
let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
else {
continue;
};
let symbol = self.token_type_at(index);
let first =
rule_first_set(atn, *target, child_stop, &mut self.first_set_cache);
if self.fast_first_set_prefilter
&& !first.nullable
&& !first.symbols.contains(&symbol)
{
if !first.symbols.is_empty() {
match expected.index {
Some(current) if index < current => {}
Some(current) if index == current => {
expected.symbols.extend(first.symbols.iter().copied());
}
_ => {
expected.index = Some(index);
expected.symbols.clone_from(&first.symbols);
}
}
}
continue;
}
let expected_before_child = expected.clone();
let children = self.recognize_state_fast(
atn,
FastRecognizeRequest {
state_number: *target,
stop_state: child_stop,
index,
rule_start_index: index,
decision_start_index: None,
precedence: *rule_precedence,
depth: depth + 1,
recovery_symbols: Rc::clone(&epsilon_recovery_symbols),
recovery_state: epsilon_recovery_state,
},
visiting,
memo,
expected,
);
if children
.iter()
.any(|child| child.diagnostics.is_empty() && child.index > index)
{
*expected = expected_before_child;
}
for child in children {
let child_index = child.index;
let child_consumed_eof = child.consumed_eof;
let child_diagnostics = child.diagnostics;
let child_node = Rc::new(FastRecognizedNode::Rule {
rule_index: *rule_index,
invoking_state: invoking_state_number(state_number),
start_index: index,
stop_index: self.rule_stop_token_index(child_index, child_consumed_eof),
children: fold_fast_left_recursive_boundaries(child.nodes.to_vec()),
});
let empty_recovery = self.empty_recovery_symbols();
outcomes.extend(
self.recognize_state_fast(
atn,
FastRecognizeRequest {
state_number: *follow_state,
stop_state,
index: child_index,
rule_start_index,
decision_start_index: next_decision_start_index,
precedence,
depth: depth + 1,
recovery_symbols: empty_recovery,
recovery_state: None,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
outcome.consumed_eof |= child_consumed_eof;
let mut diagnostics = child_diagnostics.clone();
diagnostics.append(&mut outcome.diagnostics);
outcome.diagnostics = diagnostics;
outcome.nodes.prepend(Rc::clone(&child_node));
outcome
}),
);
}
}
Transition::Atom { target, .. }
| Transition::Range { target, .. }
| Transition::Set { target, .. }
| Transition::NotSet { target, .. }
| Transition::Wildcard { target, .. } => {
let symbol = self.token_type_at(index);
if transition.matches(symbol, 1, atn.max_token_type()) {
let next_index = self.consume_index(index, symbol);
let empty_recovery = self.empty_recovery_symbols();
outcomes.extend(
self.recognize_state_fast(
atn,
FastRecognizeRequest {
state_number: *target,
stop_state,
index: next_index,
rule_start_index,
decision_start_index: next_decision_start_index,
precedence,
depth: depth + 1,
recovery_symbols: empty_recovery,
recovery_state: None,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
outcome.consumed_eof |= symbol == TOKEN_EOF;
outcome
.nodes
.prepend(Rc::new(FastRecognizedNode::Token { index }));
outcome
}),
);
} else {
let expected_symbols = fast_recovery_expected_symbols(
self,
atn,
state.state_number,
&recovery_symbols,
);
if expected_symbols.contains(&symbol) {
continue;
}
expected.record_transition(index, transition, atn.max_token_type());
record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
outcomes.extend(self.fast_single_token_deletion_recovery(
FastRecoveryRequest {
atn,
transition,
expected_symbols: Rc::clone(&expected_symbols),
target: *target,
request: FastRecognizeRequest {
state_number,
stop_state,
index,
rule_start_index,
decision_start_index,
precedence,
depth,
recovery_symbols: Rc::clone(&recovery_symbols),
recovery_state,
},
visiting,
memo,
expected,
},
));
if !state_is_left_recursive_rule(atn, state) {
outcomes.extend(self.fast_single_token_insertion_recovery(
FastRecoveryRequest {
atn,
transition,
expected_symbols: Rc::clone(&expected_symbols),
target: *target,
request: FastRecognizeRequest {
state_number,
stop_state,
index,
rule_start_index,
decision_start_index,
precedence,
depth,
recovery_symbols: Rc::clone(&recovery_symbols),
recovery_state,
},
visiting,
memo,
expected,
},
));
}
outcomes.extend(self.fast_current_token_deletion_recovery(
FastCurrentTokenDeletionRequest {
atn,
expected_symbols,
request: FastRecognizeRequest {
state_number,
stop_state,
index,
rule_start_index,
decision_start_index,
precedence,
depth,
recovery_symbols: Rc::clone(&recovery_symbols),
recovery_state,
},
visiting,
memo,
expected,
},
));
}
}
}
}
visiting.remove(&visit_key);
if self.prediction_mode == PredictionMode::Ll {
discard_recovered_fast_outcomes_if_clean_path_exists(&mut outcomes);
}
dedupe_fast_outcomes(&mut outcomes);
memo.insert(key, outcomes.clone());
outcomes
}
fn single_token_deletion_recovery(
&mut self,
recovery: RecoveryRequest<'_, '_>,
) -> Vec<RecognizeOutcome> {
let RecoveryRequest {
atn,
transition,
expected_symbols,
target,
request,
visiting,
memo,
expected,
} = recovery;
let RecognizeRequest {
stop_state,
index,
rule_start_index,
decision_start_index,
init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg,
member_values,
return_values,
rule_alt_number,
track_alt_numbers,
consumed_eof,
precedence,
depth,
..
} = request;
let Some((diagnostic, next_index, next_symbol)) =
self.single_token_deletion(transition, index, atn.max_token_type(), &expected_symbols)
else {
return Vec::new();
};
let after_next = self.consume_index(next_index, next_symbol);
self.recognize_state(
atn,
RecognizeRequest {
state_number: target,
stop_state,
index: after_next,
rule_start_index,
decision_start_index,
init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg,
member_values,
return_values,
rule_alt_number,
track_alt_numbers,
consumed_eof: consumed_eof || next_symbol == TOKEN_EOF,
precedence,
depth: depth + 1,
recovery_symbols: BTreeSet::new(),
recovery_state: None,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
outcome.consumed_eof |= next_symbol == TOKEN_EOF;
outcome.diagnostics.insert(0, diagnostic.clone());
outcome
.nodes
.insert(0, RecognizedNode::Token { index: next_index });
outcome
.nodes
.insert(0, RecognizedNode::ErrorToken { index });
outcome
})
.collect()
}
fn current_token_deletion_recovery(
&mut self,
recovery: CurrentTokenDeletionRequest<'_, '_>,
) -> Vec<RecognizeOutcome> {
let CurrentTokenDeletionRequest {
atn,
expected_symbols,
mut request,
visiting,
memo,
expected,
} = recovery;
let error_index = request.index;
if error_index == request.rule_start_index {
return Vec::new();
}
let Some((diagnostic, next_index, skipped)) =
self.current_token_deletion(error_index, &expected_symbols)
else {
return Vec::new();
};
request.state_number = request.recovery_state.unwrap_or(request.state_number);
request.index = next_index;
request.depth += 1;
request.recovery_state = None;
self.recognize_state(atn, request, visiting, memo, expected)
.into_iter()
.map(|mut outcome| {
outcome.diagnostics.insert(0, diagnostic.clone());
for index in skipped.iter().rev() {
outcome
.nodes
.insert(0, RecognizedNode::ErrorToken { index: *index });
}
outcome
})
.collect()
}
fn consuming_failure_fallback(
&mut self,
fallback: ConsumingFailureFallback<'_>,
visiting: &mut BTreeSet<RecognizeKey>,
memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
expected: &mut ExpectedTokens,
) -> Vec<RecognizeOutcome> {
if fallback.expected_symbols.is_empty() {
return Vec::new();
}
if fallback.symbol == TOKEN_EOF {
return self.eof_consuming_failure_fallback(fallback, expected);
}
self.non_eof_consuming_failure_fallback(fallback, visiting, memo, expected)
}
fn non_eof_consuming_failure_fallback(
&mut self,
fallback: ConsumingFailureFallback<'_>,
visiting: &mut BTreeSet<RecognizeKey>,
memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
expected: &mut ExpectedTokens,
) -> Vec<RecognizeOutcome> {
let ConsumingFailureFallback {
atn,
target,
request,
symbol,
expected_symbols,
decision_start_index,
decision,
} = fallback;
let error_index = request.index;
let diagnostic =
self.recovery_failure_diagnostic(error_index, decision_start_index, &expected_symbols);
let next_index = self.consume_index(error_index, symbol);
self.recognize_state(
atn,
RecognizeRequest {
state_number: target,
stop_state: request.stop_state,
index: next_index,
rule_start_index: request.rule_start_index,
decision_start_index,
init_action_rules: request.init_action_rules,
predicates: request.predicates,
rule_args: request.rule_args,
member_actions: request.member_actions,
return_actions: request.return_actions,
local_int_arg: request.local_int_arg,
member_values: request.member_values,
return_values: request.return_values,
rule_alt_number: request.rule_alt_number,
track_alt_numbers: request.track_alt_numbers,
consumed_eof: request.consumed_eof,
precedence: request.precedence,
depth: request.depth + 1,
recovery_symbols: BTreeSet::new(),
recovery_state: None,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
prepend_decision(&mut outcome, decision);
outcome.diagnostics.insert(0, diagnostic.clone());
outcome
.nodes
.insert(0, RecognizedNode::ErrorToken { index: error_index });
outcome
})
.collect()
}
fn eof_consuming_failure_fallback(
&mut self,
fallback: ConsumingFailureFallback<'_>,
expected: &ExpectedTokens,
) -> Vec<RecognizeOutcome> {
let request = fallback.request;
if request.index == request.rule_start_index {
return Vec::new();
}
let diagnostic =
self.eof_rule_recovery_diagnostic(request.index, &fallback.expected_symbols, expected);
vec![RecognizeOutcome {
index: request.index,
consumed_eof: request.consumed_eof,
alt_number: request.rule_alt_number,
member_values: request.member_values,
return_values: request.return_values,
diagnostics: vec![diagnostic],
decisions: Vec::new(),
actions: Vec::new(),
nodes: Vec::new(),
}]
}
fn single_token_insertion_recovery(
&mut self,
recovery: RecoveryRequest<'_, '_>,
) -> Vec<RecognizeOutcome> {
let RecoveryRequest {
atn,
transition,
expected_symbols,
target,
request,
visiting,
memo,
expected,
} = recovery;
let RecognizeRequest {
stop_state,
index,
rule_start_index,
decision_start_index,
init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg,
member_values,
return_values,
rule_alt_number,
track_alt_numbers,
consumed_eof,
precedence,
depth,
..
} = request;
let follow_symbols = state_expected_symbols(atn, transition.target());
let Some((diagnostic, token_type, text)) = self.single_token_insertion(
transition,
index,
atn.max_token_type(),
&expected_symbols,
&follow_symbols,
) else {
return Vec::new();
};
self.recognize_state(
atn,
RecognizeRequest {
state_number: target,
stop_state,
index,
rule_start_index,
decision_start_index,
init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg,
member_values,
return_values,
rule_alt_number,
track_alt_numbers,
consumed_eof,
precedence,
depth: depth + 1,
recovery_symbols: BTreeSet::new(),
recovery_state: None,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
outcome.diagnostics.insert(0, diagnostic.clone());
outcome.nodes.insert(
0,
RecognizedNode::MissingToken {
token_type,
at_index: index,
text: text.clone(),
},
);
outcome
})
.collect()
}
fn recognize_state(
&mut self,
atn: &Atn,
request: RecognizeRequest<'_>,
visiting: &mut BTreeSet<RecognizeKey>,
memo: &mut BTreeMap<RecognizeKey, Vec<RecognizeOutcome>>,
expected: &mut ExpectedTokens,
) -> Vec<RecognizeOutcome> {
let request_template = request.clone();
let RecognizeRequest {
state_number,
stop_state,
index,
rule_start_index,
decision_start_index,
init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg,
member_values,
return_values,
rule_alt_number,
track_alt_numbers,
consumed_eof,
precedence,
depth,
recovery_symbols,
recovery_state,
} = request;
if depth > RECOGNITION_DEPTH_LIMIT {
return Vec::new();
}
if state_number == stop_state {
return stop_outcome(
index,
consumed_eof,
rule_alt_number,
member_values,
return_values,
);
}
let key = RecognizeKey {
state_number,
stop_state,
index,
rule_start_index,
decision_start_index,
local_int_arg,
member_values: member_values.clone(),
return_values: return_values.clone(),
rule_alt_number,
track_alt_numbers,
consumed_eof,
precedence,
recovery_symbols: recovery_symbols.clone(),
recovery_state,
};
if let Some(outcomes) = memo.get(&key) {
return outcomes.clone();
}
let visit_key = key.clone();
if !visiting.insert(visit_key.clone()) {
return Vec::new();
}
let Some(state) = atn.state(state_number) else {
visiting.remove(&visit_key);
return Vec::new();
};
let next_decision_start_index = if starts_prediction_decision(state) {
Some(index)
} else {
decision_start_index
};
let (epsilon_recovery_symbols, epsilon_recovery_state) =
next_recovery_context(atn, state, &recovery_symbols, recovery_state);
let mut outcomes = Vec::new();
for (transition_index, transition) in state.transitions.iter().enumerate() {
let decision = transition_decision(atn, state, transition_index, predicates);
let next_alt_number =
next_alt_number(state, transition_index, rule_alt_number, track_alt_numbers);
match transition {
Transition::Epsilon { target } | Transition::Action { target, .. } => {
let action_rule_index = match transition {
Transition::Action { rule_index, .. } => Some(*rule_index),
_ => None,
};
outcomes.extend(self.recognize_epsilon_or_action_step(
atn,
&request_template,
EpsilonActionStep {
source_state: state_number,
target: *target,
action_rule_index,
left_recursive_boundary: left_recursive_boundary(atn, state, *target),
decision,
decision_start_index: next_decision_start_index,
alt_number: next_alt_number,
recovery_symbols: epsilon_recovery_symbols.clone(),
recovery_state: epsilon_recovery_state,
},
RecognizeScratch {
visiting,
memo,
expected,
},
));
}
Transition::Predicate {
target,
rule_index,
pred_index,
..
} => {
let predicate = PredicateEval {
index,
rule_index: *rule_index,
pred_index: *pred_index,
predicates,
local_int_arg,
member_values: &member_values,
};
if self.parser_predicate_matches(predicate) {
let left_recursive_boundary = left_recursive_boundary(atn, state, *target);
outcomes.extend(
self.recognize_state(
atn,
RecognizeRequest {
state_number: *target,
stop_state,
index,
rule_start_index,
decision_start_index: next_decision_start_index,
init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg,
member_values: member_values.clone(),
return_values: return_values.clone(),
rule_alt_number: next_alt_number,
track_alt_numbers,
consumed_eof,
precedence,
depth: depth + 1,
recovery_symbols: epsilon_recovery_symbols.clone(),
recovery_state: epsilon_recovery_state,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
prepend_decision(&mut outcome, decision);
if let Some(rule_index) = left_recursive_boundary {
outcome.nodes.insert(
0,
RecognizedNode::LeftRecursiveBoundary { rule_index },
);
}
outcome
}),
);
} else if let Some(message) =
self.parser_predicate_failure_message(*rule_index, *pred_index, predicates)
{
outcomes.push(self.predicate_failure_recovery(PredicateFailureRecovery {
rule_index: *rule_index,
index,
message,
member_values: member_values.clone(),
return_values: return_values.clone(),
rule_alt_number,
}));
} else {
record_predicate_no_viable(expected, next_decision_start_index, index);
}
}
Transition::Precedence {
target,
precedence: transition_precedence,
} => {
if *transition_precedence >= precedence {
outcomes.extend(
self.recognize_state(
atn,
RecognizeRequest {
state_number: *target,
stop_state,
index,
rule_start_index,
decision_start_index: next_decision_start_index,
init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg,
member_values: member_values.clone(),
return_values: return_values.clone(),
rule_alt_number: next_alt_number,
track_alt_numbers,
consumed_eof,
precedence,
depth: depth + 1,
recovery_symbols: epsilon_recovery_symbols.clone(),
recovery_state: epsilon_recovery_state,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
prepend_decision(&mut outcome, decision);
outcome
}),
);
}
}
Transition::Rule {
target,
rule_index,
follow_state,
precedence: rule_precedence,
..
} => {
let Some(child_stop) = atn.rule_to_stop_state().get(*rule_index).copied()
else {
continue;
};
let child_local_int_arg =
rule_local_int_arg(rule_args, state_number, *rule_index, local_int_arg);
let expected_before_child = expected.clone();
let children = self.recognize_state(
atn,
RecognizeRequest {
state_number: *target,
stop_state: child_stop,
index,
rule_start_index: index,
decision_start_index: None,
init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg: child_local_int_arg,
member_values: member_values.clone(),
return_values: BTreeMap::new(),
rule_alt_number: 0,
track_alt_numbers,
consumed_eof: false,
precedence: *rule_precedence,
depth: depth + 1,
recovery_symbols: epsilon_recovery_symbols.clone(),
recovery_state: epsilon_recovery_state,
},
visiting,
memo,
expected,
);
let children = if children.is_empty() {
self.child_rule_failure_recovery_outcomes(ChildRuleFailureRecovery {
atn,
rule_index: *rule_index,
start_index: index,
follow_state: *follow_state,
stop_state,
member_values: member_values.clone(),
expected,
})
} else {
children
};
let preserve_child_expected =
self.child_expected_reaches_clean_eof(&children, expected);
restore_expected(
&children,
index,
expected,
expected_before_child,
preserve_child_expected,
);
for child in children {
let child_node = RecognizedNode::Rule {
rule_index: *rule_index,
invoking_state: invoking_state_number(state_number),
alt_number: child.alt_number,
start_index: index,
stop_index: self.rule_stop_token_index(child.index, child.consumed_eof),
return_values: child.return_values.clone(),
children: fold_left_recursive_boundaries(child.nodes.clone()),
};
outcomes.extend(
self.recognize_state(
atn,
RecognizeRequest {
state_number: *follow_state,
stop_state,
index: child.index,
rule_start_index,
decision_start_index: next_decision_start_index,
init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg,
member_values: child.member_values.clone(),
return_values: return_values.clone(),
rule_alt_number,
track_alt_numbers,
consumed_eof: consumed_eof || child.consumed_eof,
precedence,
depth: depth + 1,
recovery_symbols: BTreeSet::new(),
recovery_state: None,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
outcome.consumed_eof |= child.consumed_eof;
let mut diagnostics = child.diagnostics.clone();
diagnostics.append(&mut outcome.diagnostics);
outcome.diagnostics = diagnostics;
let mut decisions = child.decisions.clone();
decisions.append(&mut outcome.decisions);
outcome.decisions = decisions;
prepend_decision(&mut outcome, decision);
let mut actions = child.actions.clone();
if init_action_rules.contains(rule_index) {
actions.insert(
0,
ParserAction::new_rule_init(
*rule_index,
index,
Some(*follow_state),
),
);
}
actions.append(&mut outcome.actions);
outcome.actions = actions;
outcome.nodes.insert(0, child_node.clone());
outcome
}),
);
}
}
Transition::Atom { target, .. }
| Transition::Range { target, .. }
| Transition::Set { target, .. }
| Transition::NotSet { target, .. }
| Transition::Wildcard { target, .. } => {
let symbol = self.token_type_at(index);
if transition.matches(symbol, 1, atn.max_token_type()) {
let next_index = self.consume_index(index, symbol);
outcomes.extend(
self.recognize_state(
atn,
RecognizeRequest {
state_number: *target,
stop_state,
index: next_index,
rule_start_index,
decision_start_index: next_decision_start_index,
init_action_rules,
predicates,
rule_args,
member_actions,
return_actions,
local_int_arg,
member_values: member_values.clone(),
return_values: return_values.clone(),
rule_alt_number: next_alt_number,
track_alt_numbers,
consumed_eof: consumed_eof || symbol == TOKEN_EOF,
precedence,
depth: depth + 1,
recovery_symbols: BTreeSet::new(),
recovery_state: None,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
prepend_decision(&mut outcome, decision);
outcome.consumed_eof |= symbol == TOKEN_EOF;
outcome.nodes.insert(0, RecognizedNode::Token { index });
outcome
}),
);
} else {
let expected_symbols =
recovery_expected_symbols(atn, state.state_number, &recovery_symbols);
if expected_symbols.contains(&symbol) {
continue;
}
expected.record_transition(index, transition, atn.max_token_type());
record_no_viable_if_ambiguous(expected, next_decision_start_index, index);
let before_recovery = outcomes.len();
let recovery_request = request_template.clone();
outcomes.extend(
self.single_token_deletion_recovery(RecoveryRequest {
atn,
transition,
expected_symbols: expected_symbols.clone(),
target: *target,
request: recovery_request.clone(),
visiting,
memo,
expected,
})
.into_iter()
.map(|mut outcome| {
prepend_decision(&mut outcome, decision);
outcome
}),
);
if !state_is_left_recursive_rule(atn, state) {
outcomes.extend(
self.single_token_insertion_recovery(RecoveryRequest {
atn,
transition,
expected_symbols: expected_symbols.clone(),
target: *target,
request: recovery_request.clone(),
visiting,
memo,
expected,
})
.into_iter()
.map(|mut outcome| {
prepend_decision(&mut outcome, decision);
outcome
}),
);
}
outcomes.extend(self.current_token_deletion_recovery(
CurrentTokenDeletionRequest {
atn,
expected_symbols: expected_symbols.clone(),
request: recovery_request.clone(),
visiting,
memo,
expected,
},
));
if outcomes.len() == before_recovery {
outcomes.extend(self.consuming_failure_fallback(
ConsumingFailureFallback {
atn,
target: *target,
request: recovery_request,
symbol,
expected_symbols,
decision_start_index: next_decision_start_index,
decision,
},
visiting,
memo,
expected,
));
}
}
}
}
}
visiting.remove(&visit_key);
self.record_prediction_diagnostics(atn, state, index, &outcomes);
if self.prediction_mode == PredictionMode::Ll {
discard_recovered_outcomes_if_clean_path_exists(&mut outcomes);
}
dedupe_outcomes(&mut outcomes);
memo.insert(key, outcomes.clone());
outcomes
}
fn recognize_epsilon_or_action_step(
&mut self,
atn: &Atn,
request: &RecognizeRequest<'_>,
step: EpsilonActionStep,
scratch: RecognizeScratch<'_>,
) -> Vec<RecognizeOutcome> {
let RecognizeScratch {
visiting,
memo,
expected,
} = scratch;
let action = step.action_rule_index.map(|rule_index| {
ParserAction::new(
step.source_state,
rule_index,
request.rule_start_index,
self.rule_stop_token_index(request.index, request.consumed_eof),
)
});
let next_member_values = if action.is_some() {
member_values_after_action(
step.source_state,
request.member_actions,
&request.member_values,
)
} else {
request.member_values.clone()
};
let next_return_values = action.map_or_else(
|| request.return_values.clone(),
|action| {
return_values_after_action(
step.source_state,
action.rule_index(),
request.return_actions,
&request.return_values,
)
},
);
self.recognize_state(
atn,
RecognizeRequest {
state_number: step.target,
stop_state: request.stop_state,
index: request.index,
rule_start_index: request.rule_start_index,
decision_start_index: step.decision_start_index,
init_action_rules: request.init_action_rules,
predicates: request.predicates,
rule_args: request.rule_args,
member_actions: request.member_actions,
return_actions: request.return_actions,
local_int_arg: request.local_int_arg,
member_values: next_member_values,
return_values: next_return_values,
rule_alt_number: step.alt_number,
track_alt_numbers: request.track_alt_numbers,
consumed_eof: request.consumed_eof,
precedence: request.precedence,
depth: request.depth + 1,
recovery_symbols: step.recovery_symbols,
recovery_state: step.recovery_state,
},
visiting,
memo,
expected,
)
.into_iter()
.map(|mut outcome| {
prepend_decision(&mut outcome, step.decision);
if let Some(rule_index) = step.left_recursive_boundary {
outcome
.nodes
.insert(0, RecognizedNode::LeftRecursiveBoundary { rule_index });
}
if let Some(action) = action {
outcome.actions.insert(0, action);
}
outcome
})
.collect()
}
fn token_type_at(&mut self, index: usize) -> i32 {
self.input.token_type_at_index(index)
}
fn cached_state_expected_symbols(
&mut self,
atn: &Atn,
state_number: usize,
) -> Rc<BTreeSet<i32>> {
if let Some(cached) = self.state_expected_cache.get(&state_number) {
return Rc::clone(cached);
}
let symbols = state_expected_symbols(atn, state_number);
let entry = self.intern_recovery_symbols(symbols);
self.state_expected_cache
.insert(state_number, Rc::clone(&entry));
entry
}
fn empty_recovery_symbols(&self) -> Rc<BTreeSet<i32>> {
Rc::clone(&self.empty_recovery_symbols)
}
fn intern_recovery_symbols(&mut self, set: BTreeSet<i32>) -> Rc<BTreeSet<i32>> {
if set.is_empty() {
return Rc::clone(&self.empty_recovery_symbols);
}
let candidate = Rc::new(set);
match self.recovery_symbols_intern.get(&candidate) {
Some(existing) => Rc::clone(existing),
None => {
self.recovery_symbols_intern
.insert(Rc::clone(&candidate), Rc::clone(&candidate));
candidate
}
}
}
fn cached_decision_lookahead(
&mut self,
atn: &Atn,
state: &AtnState,
rule_stop_state: usize,
) -> Rc<DecisionLookahead> {
if let Some(cached) = self.decision_lookahead_cache.get(&state.state_number) {
return Rc::clone(cached);
}
let mut entry = DecisionLookahead {
transitions: Vec::with_capacity(state.transitions.len()),
};
for transition in &state.transitions {
entry.transitions.push(transition_first_set(
atn,
transition,
rule_stop_state,
&mut self.first_set_cache,
));
}
let entry = Rc::new(entry);
self.decision_lookahead_cache
.insert(state.state_number, Rc::clone(&entry));
entry
}
fn token_at(&mut self, index: usize) -> Option<CommonToken> {
self.input.get(index).cloned()
}
fn current_visible_index(&mut self) -> usize {
let index = self.input.index();
self.input.seek(index);
self.input.index()
}
fn child_expected_reaches_clean_eof(
&mut self,
children: &[RecognizeOutcome],
expected: &ExpectedTokens,
) -> bool {
let Some(index) = expected.index else {
return false;
};
self.token_type_at(index) == TOKEN_EOF
&& children
.iter()
.any(|child| child.diagnostics.is_empty() && child.index == index)
}
fn previous_token_index(&mut self, index: usize) -> Option<usize> {
self.input.previous_visible_token_index(index)
}
fn rule_stop_token_index(&mut self, index: usize, consumed_eof: bool) -> Option<usize> {
if consumed_eof && self.token_type_at(index) == TOKEN_EOF {
Some(index)
} else {
self.previous_token_index(index)
}
}
fn rule_stop_token(&mut self, index: usize, consumed_eof: bool) -> Option<CommonToken> {
self.rule_stop_token_index(index, consumed_eof)
.and_then(|token_index| self.token_at(token_index))
}
fn predicate_failure_recovery(
&mut self,
request: PredicateFailureRecovery<'_>,
) -> RecognizeOutcome {
let PredicateFailureRecovery {
rule_index,
index,
message,
member_values,
return_values,
rule_alt_number,
} = request;
let rule_name = self
.rule_names()
.get(rule_index)
.map_or_else(|| rule_index.to_string(), Clone::clone);
let diagnostic = diagnostic_for_token(
self.token_at(index).as_ref(),
format!("rule {rule_name} {message}"),
);
let mut nodes = Vec::new();
let mut next_index = index;
loop {
let symbol = self.token_type_at(next_index);
if symbol == TOKEN_EOF {
break;
}
nodes.push(RecognizedNode::ErrorToken { index: next_index });
let after = self.consume_index(next_index, symbol);
if after == next_index {
break;
}
next_index = after;
}
RecognizeOutcome {
index: next_index,
consumed_eof: false,
alt_number: rule_alt_number,
member_values,
return_values,
diagnostics: vec![diagnostic],
decisions: Vec::new(),
actions: Vec::new(),
nodes,
}
}
fn parser_predicate_matches(&mut self, eval: PredicateEval<'_>) -> bool {
let PredicateEval {
index,
rule_index,
pred_index,
predicates,
local_int_arg,
member_values,
} = eval;
let Some((_, _, predicate)) = predicates
.iter()
.find(|(rule, pred, _)| *rule == rule_index && *pred == pred_index)
else {
return true;
};
self.input.seek(index);
match predicate {
ParserPredicate::True => true,
ParserPredicate::False => false,
ParserPredicate::FalseWithMessage { .. } => false,
ParserPredicate::Invoke { value } => {
let key = (rule_index, pred_index);
if !self.invoked_predicates.contains(&key) {
self.invoked_predicates.push(key);
use std::io::Write as _;
let mut stdout = std::io::stdout().lock();
let _ = writeln!(stdout, "eval={value}");
}
*value
}
ParserPredicate::LookaheadTextEquals { offset, text } => {
self.input.lt(*offset).and_then(Token::text) == Some(*text)
}
ParserPredicate::LookaheadNotEquals { offset, token_type } => {
self.la(*offset) != *token_type
}
ParserPredicate::LocalIntEquals { value } => {
local_int_arg.is_none_or(|(_, actual)| actual == *value)
}
ParserPredicate::LocalIntLessOrEqual { value } => {
local_int_arg.is_none_or(|(_, actual)| actual <= *value)
}
ParserPredicate::MemberModuloEquals {
member,
modulus,
value,
equals,
} => {
if *modulus == 0 {
return false;
}
let actual = member_values.get(member).copied().unwrap_or_default() % *modulus;
(actual == *value) == *equals
}
}
}
fn parser_predicate_failure_message(
&self,
rule_index: usize,
pred_index: usize,
predicates: &[(usize, usize, ParserPredicate)],
) -> Option<&'static str> {
predicates
.iter()
.find_map(|(rule, pred, predicate)| match predicate {
ParserPredicate::FalseWithMessage { message }
if *rule == rule_index && *pred == pred_index =>
{
Some(*message)
}
_ => None,
})
}
fn consume_index(&mut self, index: usize, symbol: i32) -> usize {
if symbol == TOKEN_EOF {
return index;
}
self.input.next_visible_after(index)
}
fn no_viable_alternative(
&mut self,
start_index: usize,
error_index: usize,
) -> ParserDiagnostic {
let text = display_input_text(&self.input.text(start_index, error_index));
diagnostic_for_token(
self.token_at(error_index).as_ref(),
format!("no viable alternative at input '{text}'"),
)
}
fn recovery_failure_diagnostic(
&mut self,
index: usize,
decision_start_index: Option<usize>,
expected_symbols: &BTreeSet<i32>,
) -> ParserDiagnostic {
if expected_symbols.len() > 1 {
if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
return self.no_viable_alternative(decision_start, index);
}
}
diagnostic_for_token(
self.token_at(index).as_ref(),
format!(
"mismatched input {} expecting {}",
self.token_at(index)
.as_ref()
.map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
self.expected_symbols_display(expected_symbols)
),
)
}
fn eof_rule_recovery_diagnostic(
&mut self,
index: usize,
expected_symbols: &BTreeSet<i32>,
expected: &ExpectedTokens,
) -> ParserDiagnostic {
let symbols = if expected.index == Some(index) && !expected.symbols.is_empty() {
&expected.symbols
} else {
expected_symbols
};
diagnostic_for_token(
self.token_at(index).as_ref(),
format!(
"mismatched input {} expecting {}",
self.token_at(index)
.as_ref()
.map_or_else(|| "'<EOF>'".to_owned(), token_input_display),
self.expected_symbols_display(symbols)
),
)
}
pub fn text_interval(&mut self, start: usize, stop: Option<usize>) -> String {
let Some(stop) = stop else {
return String::new();
};
let stop = if self
.token_at(stop)
.is_some_and(|token| token.token_type() == TOKEN_EOF)
{
let Some(previous) = self.previous_token_index(stop) else {
return String::new();
};
previous
} else {
stop
};
self.input.text(start, stop)
}
fn clear_prediction_diagnostics(&mut self) {
self.prediction_diagnostics.clear();
self.reported_prediction_diagnostics.clear();
}
fn reset_per_parse_caches(&mut self) {
self.first_set_cache.clear();
self.decision_lookahead_cache.clear();
self.recovery_symbols_intern.clear();
self.state_expected_cache.clear();
}
fn record_prediction_diagnostics(
&mut self,
atn: &Atn,
state: &AtnState,
start_index: usize,
outcomes: &[RecognizeOutcome],
) {
if !self.report_diagnostic_errors || state.transitions.len() < 2 {
return;
}
let Some(decision) = atn
.decision_to_state()
.iter()
.position(|state_number| *state_number == state.state_number)
else {
return;
};
let Some(rule_index) = state.rule_index else {
return;
};
let mut alts_by_end = BTreeMap::<usize, BTreeSet<usize>>::new();
for outcome in outcomes
.iter()
.filter(|outcome| outcome.diagnostics.is_empty())
{
let Some(alt) = outcome.decisions.first() else {
continue;
};
alts_by_end
.entry(outcome.index)
.or_default()
.insert(alt + 1);
}
let Some((&end_index, ambig_alts)) = alts_by_end
.iter()
.filter(|(_, alts)| alts.len() > 1)
.max_by_key(|(end, _)| *end)
else {
return;
};
let rule_name = self
.rule_names()
.get(rule_index)
.map_or_else(|| "<unknown>".to_owned(), Clone::clone);
let stop_index = self.previous_token_index(end_index).unwrap_or(start_index);
let input = display_input_text(&self.input.text(start_index, stop_index));
let alts = ambig_alts
.iter()
.map(usize::to_string)
.collect::<Vec<_>>()
.join(", ");
let key = (decision, start_index, format!("{alts}:{input}"));
if !self.reported_prediction_diagnostics.insert(key) {
return;
}
let start_token = self.token_at(start_index);
let stop_token = self.token_at(stop_index);
self.prediction_diagnostics.push(diagnostic_for_token(
start_token.as_ref(),
format!("reportAttemptingFullContext d={decision} ({rule_name}), input='{input}'"),
));
self.prediction_diagnostics.push(diagnostic_for_token(
stop_token.as_ref(),
format!(
"reportAmbiguity d={decision} ({rule_name}): ambigAlts={{{alts}}}, input='{input}'"
),
));
}
pub fn expected_tokens_at_state(&self, atn: &Atn, state_number: usize) -> String {
expected_symbols_display(
&state_expected_symbols(atn, state_number),
self.vocabulary(),
)
}
pub fn token_display_at(&mut self, index: usize) -> Option<String> {
self.token_at(index).map(|token| format!("{token}"))
}
fn recognized_node_tree(
&mut self,
node: &RecognizedNode,
track_alt_numbers: bool,
) -> Result<ParseTree, AntlrError> {
match node {
RecognizedNode::Token { index } => {
let token =
self.input
.get(*index)
.cloned()
.ok_or_else(|| AntlrError::ParserError {
line: 0,
column: 0,
message: format!("missing token at index {index}"),
})?;
Ok(ParseTree::Terminal(TerminalNode::new(token)))
}
RecognizedNode::ErrorToken { index } => {
let token =
self.input
.get(*index)
.cloned()
.ok_or_else(|| AntlrError::ParserError {
line: 0,
column: 0,
message: format!("missing error token at index {index}"),
})?;
Ok(ParseTree::Error(ErrorNode::new(token)))
}
RecognizedNode::MissingToken {
token_type,
at_index,
text,
} => {
let current = self.token_at(*at_index);
let token = CommonToken::new(*token_type)
.with_text(text)
.with_span(usize::MAX, usize::MAX)
.with_position(
current.as_ref().map(Token::line).unwrap_or_default(),
current.as_ref().map(Token::column).unwrap_or_default(),
);
Ok(ParseTree::Error(ErrorNode::new(token)))
}
RecognizedNode::Rule {
rule_index,
invoking_state,
alt_number,
start_index,
stop_index,
return_values,
children,
} => {
let mut context = ParserRuleContext::new(*rule_index, *invoking_state);
if track_alt_numbers {
context.set_alt_number(*alt_number);
}
for (name, value) in return_values {
context.set_int_return(name.clone(), *value);
}
if let Some(token) = self.token_at(*start_index) {
context.set_start(token);
}
if let Some(token) = stop_index.and_then(|index| self.token_at(index)) {
context.set_stop(token);
}
for child in children {
context.add_child(self.recognized_node_tree(child, track_alt_numbers)?);
}
Ok(self.rule_node(context))
}
RecognizedNode::LeftRecursiveBoundary { rule_index } => Err(AntlrError::Unsupported(
format!("unfolded left-recursive boundary for rule {rule_index}"),
)),
}
}
}
fn left_recursive_boundary(atn: &Atn, state: &AtnState, target: usize) -> Option<usize> {
if !state.precedence_rule_decision {
return None;
}
let target_state = atn.state(target)?;
if target_state.kind == AtnStateKind::LoopEnd {
return None;
}
state.rule_index
}
const fn next_alt_number(
state: &AtnState,
transition_index: usize,
current_alt_number: usize,
track_alt_numbers: bool,
) -> usize {
if !track_alt_numbers || current_alt_number != 0 || state.transitions.len() <= 1 {
return current_alt_number;
}
if matches!(
state.kind,
AtnStateKind::Basic
| AtnStateKind::BlockStart
| AtnStateKind::PlusBlockStart
| AtnStateKind::StarBlockStart
| AtnStateKind::StarLoopEntry
) && !state.precedence_rule_decision
{
return transition_index + 1;
}
current_alt_number
}
fn fold_left_recursive_boundaries(nodes: Vec<RecognizedNode>) -> Vec<RecognizedNode> {
let mut folded = Vec::new();
for node in nodes {
match node {
RecognizedNode::LeftRecursiveBoundary { rule_index } => {
if !folded.is_empty() {
let children = std::mem::take(&mut folded);
let start_index = recognized_nodes_start_index(&children).unwrap_or_default();
let stop_index = recognized_nodes_stop_index(&children);
folded.push(RecognizedNode::Rule {
rule_index,
invoking_state: -1,
alt_number: 0,
start_index,
stop_index,
return_values: BTreeMap::new(),
children,
});
}
}
node => folded.push(node),
}
}
folded
}
fn fold_fast_left_recursive_boundaries(
nodes: Vec<Rc<FastRecognizedNode>>,
) -> Vec<Rc<FastRecognizedNode>> {
if !nodes
.iter()
.any(|node| matches!(node.as_ref(), FastRecognizedNode::LeftRecursiveBoundary { .. }))
{
return nodes;
}
let mut folded: Vec<Rc<FastRecognizedNode>> = Vec::with_capacity(nodes.len());
for node in nodes {
match node.as_ref() {
FastRecognizedNode::LeftRecursiveBoundary { rule_index } => {
if !folded.is_empty() {
let children = std::mem::take(&mut folded);
let start_index =
fast_recognized_nodes_start_index(&children).unwrap_or_default();
let stop_index = fast_recognized_nodes_stop_index(&children);
folded.push(Rc::new(FastRecognizedNode::Rule {
rule_index: *rule_index,
invoking_state: -1,
start_index,
stop_index,
children,
}));
}
}
_ => folded.push(node),
}
}
folded
}
fn fast_recognized_nodes_start_index(nodes: &[Rc<FastRecognizedNode>]) -> Option<usize> {
nodes
.iter()
.find_map(|node| fast_recognized_node_start_index(node.as_ref()))
}
const fn fast_recognized_node_start_index(node: &FastRecognizedNode) -> Option<usize> {
match node {
FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
Some(*index)
}
FastRecognizedNode::MissingToken { at_index, .. } => Some(*at_index),
FastRecognizedNode::Rule { start_index, .. } => Some(*start_index),
FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
}
}
fn fast_recognized_nodes_stop_index(nodes: &[Rc<FastRecognizedNode>]) -> Option<usize> {
nodes
.iter()
.rev()
.find_map(|node| fast_recognized_node_stop_index(node.as_ref()))
}
const fn fast_recognized_node_stop_index(node: &FastRecognizedNode) -> Option<usize> {
match node {
FastRecognizedNode::Token { index } | FastRecognizedNode::ErrorToken { index } => {
Some(*index)
}
FastRecognizedNode::MissingToken { at_index, .. } => at_index.checked_sub(1),
FastRecognizedNode::Rule { stop_index, .. } => *stop_index,
FastRecognizedNode::LeftRecursiveBoundary { .. } => None,
}
}
fn recognized_nodes_start_index(nodes: &[RecognizedNode]) -> Option<usize> {
nodes.iter().find_map(recognized_node_start_index)
}
const fn recognized_node_start_index(node: &RecognizedNode) -> Option<usize> {
match node {
RecognizedNode::Token { index } | RecognizedNode::ErrorToken { index } => Some(*index),
RecognizedNode::MissingToken { at_index, .. } => Some(*at_index),
RecognizedNode::Rule { start_index, .. } => Some(*start_index),
RecognizedNode::LeftRecursiveBoundary { .. } => None,
}
}
fn recognized_nodes_stop_index(nodes: &[RecognizedNode]) -> Option<usize> {
nodes.iter().rev().find_map(recognized_node_stop_index)
}
fn invoking_state_number(state_number: usize) -> isize {
isize::try_from(state_number).unwrap_or(isize::MAX)
}
const fn recognized_node_stop_index(node: &RecognizedNode) -> Option<usize> {
match node {
RecognizedNode::Token { index } | RecognizedNode::ErrorToken { index } => Some(*index),
RecognizedNode::MissingToken { at_index, .. } => at_index.checked_sub(1),
RecognizedNode::Rule { stop_index, .. } => *stop_index,
RecognizedNode::LeftRecursiveBoundary { .. } => None,
}
}
fn token_input_display(token: &impl Token) -> String {
format!("'{}'", token.text().unwrap_or("<EOF>"))
}
fn display_input_text(text: &str) -> String {
let mut out = String::new();
for ch in text.chars() {
match ch {
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
other => out.push(other),
}
}
out
}
fn diagnostic_for_token(token: Option<&impl Token>, message: String) -> ParserDiagnostic {
ParserDiagnostic {
line: token.map(Token::line).unwrap_or_default(),
column: token.map(Token::column).unwrap_or_default(),
message,
}
}
#[allow(clippy::print_stderr)]
fn report_parser_diagnostics(diagnostics: &[ParserDiagnostic]) {
for diagnostic in diagnostics {
eprintln!(
"line {}:{} {}",
diagnostic.line, diagnostic.column, diagnostic.message
);
}
}
#[allow(clippy::print_stderr)]
fn report_token_source_errors(errors: &[TokenSourceError]) {
for error in errors {
eprintln!("line {}:{} {}", error.line, error.column, error.message);
}
}
fn expected_symbols_display(symbols: &BTreeSet<i32>, vocabulary: &Vocabulary) -> String {
let items = symbols
.iter()
.map(|symbol| expected_symbol_display(*symbol, vocabulary))
.collect::<Vec<_>>();
if let [single] = items.as_slice() {
return single.clone();
}
format!("{{{}}}", items.join(", "))
}
fn expected_symbol_display(symbol: i32, vocabulary: &Vocabulary) -> String {
if symbol == TOKEN_EOF {
return "<EOF>".to_owned();
}
vocabulary.display_name(symbol)
}
fn state_is_left_recursive_rule(atn: &Atn, state: &AtnState) -> bool {
let Some(rule_index) = state.rule_index else {
return false;
};
atn.rule_to_start_state()
.get(rule_index)
.and_then(|state_number| atn.state(*state_number))
.is_some_and(|rule_start| rule_start.left_recursive_rule)
}
fn select_better_top_outcome(
first: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
second: Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens>,
) -> Result<(FastRecognizeOutcome, ExpectedTokens), ExpectedTokens> {
match (first, second) {
(Ok(first), Ok(second)) => {
if first.0.diagnostics.is_empty() {
Ok(first)
} else {
Ok(second)
}
}
(Ok(first), Err(_)) => Ok(first),
(Err(_), Ok(second)) => Ok(second),
(Err(_), Err(second_expected)) => Err(second_expected),
}
}
fn select_best_fast_outcome(
outcomes: impl Iterator<Item = FastRecognizeOutcome>,
prediction_mode: PredictionMode,
) -> Option<FastRecognizeOutcome> {
outcomes.reduce(|best, outcome| {
let outcome_position = (outcome.index, outcome.consumed_eof);
let best_position = (best.index, best.consumed_eof);
let better = match prediction_mode {
PredictionMode::Ll => outcome_is_better(
outcome_position,
&outcome.diagnostics,
best_position,
&best.diagnostics,
),
PredictionMode::Sll => outcome.index > best.index,
};
if better {
return outcome;
}
best
})
}
fn select_best_outcome(
outcomes: impl Iterator<Item = RecognizeOutcome>,
prediction_mode: PredictionMode,
) -> Option<RecognizeOutcome> {
let outcomes = outcomes.collect::<Vec<_>>();
let prefer_first_tie = outcomes
.iter()
.any(|outcome| nodes_need_stable_tie(&outcome.nodes));
outcomes.into_iter().reduce(|best, outcome| {
let outcome_position = (outcome.index, outcome.consumed_eof);
let best_position = (best.index, best.consumed_eof);
let better = match prediction_mode {
PredictionMode::Ll => {
outcome_is_better(
outcome_position,
&outcome.diagnostics,
best_position,
&best.diagnostics,
) || (!prefer_first_tie
&& outcome_position == best_position
&& outcome.diagnostics.len() == best.diagnostics.len()
&& diagnostic_recovery_rank(&outcome.diagnostics)
== diagnostic_recovery_rank(&best.diagnostics)
&& (outcome.decisions < best.decisions
|| (outcome.decisions == best.decisions && outcome.actions > best.actions)))
}
PredictionMode::Sll => {
outcome_position > best_position
|| (outcome_position == best_position
&& !prefer_first_tie
&& (outcome.decisions < best.decisions
|| (outcome.decisions == best.decisions
&& outcome_is_better(
outcome_position,
&outcome.diagnostics,
best_position,
&best.diagnostics,
))))
}
};
if better {
return outcome;
}
best
})
}
fn transition_decision(
atn: &Atn,
state: &AtnState,
transition_index: usize,
predicates: &[(usize, usize, ParserPredicate)],
) -> Option<usize> {
if state.transitions.len() <= 1
|| state.precedence_rule_decision
|| decision_reaches_unsupported_predicate(atn, state, predicates)
{
return None;
}
Some(transition_index)
}
const fn starts_prediction_decision(state: &AtnState) -> bool {
state.transitions.len() > 1
&& !matches!(
state.kind,
AtnStateKind::PlusLoopBack | AtnStateKind::StarLoopBack | AtnStateKind::StarLoopEntry
)
}
fn record_no_viable_if_ambiguous(
expected: &mut ExpectedTokens,
decision_start_index: Option<usize>,
index: usize,
) {
if expected.index == Some(index) && expected.symbols.len() > 1 {
if let Some(decision_start) = no_viable_decision_start(decision_start_index, index) {
expected.record_no_viable(decision_start, index);
}
}
}
const fn record_predicate_no_viable(
expected: &mut ExpectedTokens,
decision_start_index: Option<usize>,
index: usize,
) {
if let Some(decision_start) = decision_start_index {
expected.record_no_viable(decision_start, index);
}
}
const fn no_viable_decision_start(
decision_start_index: Option<usize>,
index: usize,
) -> Option<usize> {
match decision_start_index {
Some(start) if index > start => Some(start),
_ => None,
}
}
fn restore_expected(
children: &[RecognizeOutcome],
child_start_index: usize,
expected: &mut ExpectedTokens,
snapshot: ExpectedTokens,
preserve_child_expected: bool,
) {
if preserve_child_expected {
return;
}
if children
.iter()
.any(|child| child.diagnostics.is_empty() && child.index > child_start_index)
{
*expected = snapshot;
}
}
fn decision_reaches_unsupported_predicate(
atn: &Atn,
state: &AtnState,
predicates: &[(usize, usize, ParserPredicate)],
) -> bool {
state.transitions.iter().any(|transition| {
transition_reaches_unsupported_predicate(atn, transition, predicates, &mut BTreeSet::new())
})
}
fn transition_reaches_unsupported_predicate(
atn: &Atn,
transition: &Transition,
predicates: &[(usize, usize, ParserPredicate)],
visited: &mut BTreeSet<usize>,
) -> bool {
match transition {
Transition::Predicate {
rule_index,
pred_index,
..
} => !predicates
.iter()
.any(|(rule, pred, _)| rule == rule_index && pred == pred_index),
Transition::Epsilon { target }
| Transition::Action { target, .. }
| Transition::Rule { target, .. } => {
state_reaches_unsupported_predicate(atn, *target, predicates, visited)
}
Transition::Precedence { .. }
| Transition::Atom { .. }
| Transition::Range { .. }
| Transition::Set { .. }
| Transition::NotSet { .. }
| Transition::Wildcard { .. } => false,
}
}
fn state_reaches_unsupported_predicate(
atn: &Atn,
state_number: usize,
predicates: &[(usize, usize, ParserPredicate)],
visited: &mut BTreeSet<usize>,
) -> bool {
if !visited.insert(state_number) {
return false;
}
let Some(state) = atn.state(state_number) else {
return false;
};
state.transitions.iter().any(|transition| {
transition_reaches_unsupported_predicate(atn, transition, predicates, visited)
})
}
fn prepend_decision(outcome: &mut RecognizeOutcome, decision: Option<usize>) {
if let Some(decision) = decision {
outcome.decisions.insert(0, decision);
}
}
fn outcome_is_better(
outcome_position: (usize, bool),
outcome_diagnostics: &[ParserDiagnostic],
best_position: (usize, bool),
best_diagnostics: &[ParserDiagnostic],
) -> bool {
outcome_position > best_position
|| (outcome_position == best_position
&& (outcome_diagnostics.len() < best_diagnostics.len()
|| (outcome_diagnostics.len() == best_diagnostics.len()
&& diagnostic_recovery_rank(outcome_diagnostics)
< diagnostic_recovery_rank(best_diagnostics))))
}
fn diagnostic_recovery_rank(diagnostics: &[ParserDiagnostic]) -> usize {
diagnostics
.iter()
.filter(|diagnostic| {
diagnostic.message.starts_with("mismatched input ")
&& !diagnostic.message.starts_with("mismatched input '<EOF>' ")
})
.count()
}
fn discard_recovered_fast_outcomes_if_clean_path_exists(outcomes: &mut Vec<FastRecognizeOutcome>) {
if outcomes
.iter()
.any(|outcome| outcome.diagnostics.is_empty())
{
outcomes.retain(|outcome| outcome.diagnostics.is_empty());
}
}
fn discard_recovered_outcomes_if_clean_path_exists(outcomes: &mut Vec<RecognizeOutcome>) {
if outcomes.iter().any(outcome_has_rule_failure_diagnostic) {
return;
}
if outcomes
.iter()
.any(|outcome| outcome.diagnostics.is_empty())
{
outcomes.retain(|outcome| outcome.diagnostics.is_empty());
}
}
fn outcome_has_rule_failure_diagnostic(outcome: &RecognizeOutcome) -> bool {
outcome
.diagnostics
.iter()
.any(|diagnostic| diagnostic.message.starts_with("rule "))
}
fn nodes_need_stable_tie(nodes: &[RecognizedNode]) -> bool {
nodes.iter().any(node_needs_stable_tie)
}
fn node_needs_stable_tie(node: &RecognizedNode) -> bool {
match node {
RecognizedNode::Token { .. }
| RecognizedNode::ErrorToken { .. }
| RecognizedNode::MissingToken { .. } => false,
RecognizedNode::LeftRecursiveBoundary { .. } => true,
RecognizedNode::Rule {
rule_index,
children,
..
} => children.iter().any(|child| {
matches!(
child,
RecognizedNode::Rule {
rule_index: child_rule,
..
} if child_rule == rule_index
) || node_needs_stable_tie(child)
}),
}
}
fn dedupe_fast_outcomes(outcomes: &mut Vec<FastRecognizeOutcome>) {
if outcomes.len() < 2 {
return;
}
let mut keep = Vec::with_capacity(outcomes.len());
let mut seen: BTreeMap<(usize, bool), Vec<usize>> = BTreeMap::new();
'outcomes: for (index, outcome) in outcomes.iter().enumerate() {
let bucket = seen
.entry((outcome.index, outcome.consumed_eof))
.or_default();
for &previous in bucket.iter() {
if outcomes[previous].diagnostics == outcome.diagnostics {
continue 'outcomes;
}
}
bucket.push(index);
keep.push(index);
}
if keep.len() == outcomes.len() {
return;
}
let mut iter = keep.into_iter();
let mut next_keep = iter.next();
let mut current = 0_usize;
outcomes.retain(|_| {
let result = next_keep == Some(current);
if result {
next_keep = iter.next();
}
current += 1;
result
});
}
fn dedupe_outcomes(outcomes: &mut Vec<RecognizeOutcome>) {
outcomes.sort_unstable();
outcomes.dedup();
}
impl<S> Recognizer for BaseParser<S>
where
S: TokenSource,
{
fn data(&self) -> &RecognizerData {
&self.data
}
fn data_mut(&mut self) -> &mut RecognizerData {
&mut self.data
}
}
impl<S> Parser for BaseParser<S>
where
S: TokenSource,
{
fn build_parse_trees(&self) -> bool {
self.build_parse_trees
}
fn set_build_parse_trees(&mut self, build: bool) {
self.build_parse_trees = build;
}
fn report_diagnostic_errors(&self) -> bool {
self.report_diagnostic_errors
}
fn set_report_diagnostic_errors(&mut self, report: bool) {
self.report_diagnostic_errors = report;
}
fn prediction_mode(&self) -> PredictionMode {
self.prediction_mode
}
fn set_prediction_mode(&mut self, mode: PredictionMode) {
self.prediction_mode = mode;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::atn::serialized::{AtnDeserializer, SerializedAtn};
use crate::token::{CommonToken, HIDDEN_CHANNEL, Token};
use crate::token_stream::CommonTokenStream;
use crate::vocabulary::Vocabulary;
#[test]
fn fx_hasher_write_matches_typed_methods_for_full_words() {
let value: u64 = 0x0102_0304_0506_0708;
let mut typed = FxHasher::default();
typed.write_u64(value);
let mut bytewise = FxHasher::default();
bytewise.write(&value.to_le_bytes());
assert_eq!(typed.finish(), bytewise.finish());
}
#[derive(Debug)]
struct Source {
tokens: Vec<CommonToken>,
index: usize,
}
impl TokenSource for Source {
fn next_token(&mut self) -> CommonToken {
let token = self
.tokens
.get(self.index)
.cloned()
.unwrap_or_else(|| CommonToken::eof("parser-test", self.index, 1, self.index));
self.index += 1;
token
}
fn line(&self) -> usize {
1
}
fn column(&self) -> usize {
self.index
}
fn source_name(&self) -> &'static str {
"parser-test"
}
}
fn mini_parser(tokens: Vec<CommonToken>) -> BaseParser<Source> {
let data = RecognizerData::new(
"Mini.g4",
Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
);
BaseParser::new(CommonTokenStream::new(Source { tokens, index: 0 }), data)
}
fn token_then_eof_atn() -> Atn {
AtnDeserializer::new(&SerializedAtn::from_i32(&[
4, 1, 2, 3, 2, 0, 1, 0, 7, 0, 0, 0, 1, 0, 0, 0, 2, 0, 1, 5, 1, 0, 0, 1, 2, 5, -1, 0, 0, 0, ]))
.deserialize()
.expect("artificial parser ATN should deserialize")
}
fn eof_then_action_atn() -> Atn {
AtnDeserializer::new(&SerializedAtn::from_i32(&[
4, 1, 1, 3, 2, 0, 1, 0, 7, 0, 0, 0, 1, 0, 0, 0, 2, 0, 1, 5, -1, 0, 0, 1, 2, 6, 0, 0, 0, 0, ]))
.deserialize()
.expect("artificial parser ATN should deserialize")
}
#[test]
fn parser_matches_token_and_reports_mismatch() {
let source = Source {
tokens: vec![
CommonToken::new(1).with_text("x"),
CommonToken::eof("parser-test", 1, 1, 1),
],
index: 0,
};
let data = RecognizerData::new(
"Mini.g4",
Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
);
let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
assert_eq!(
parser.match_token(1).expect("token 1 should match").text(),
"x"
);
assert!(parser.match_token(1).is_err());
}
#[test]
fn parser_interprets_simple_atn_rule() {
let atn = token_then_eof_atn();
let mut parser = mini_parser(vec![
CommonToken::new(1).with_text("x"),
CommonToken::eof("parser-test", 1, 1, 1),
]);
let tree = parser
.parse_atn_rule(&atn, 0)
.expect("artificial parser rule should parse");
assert_eq!(tree.text(), "x<EOF>");
assert_eq!(
tree.first_rule_stop(0)
.expect("rule should stop at EOF")
.token_type(),
TOKEN_EOF
);
let mut parser = mini_parser(vec![
CommonToken::new(1).with_text("x"),
CommonToken::eof("parser-test", 1, 1, 1),
]);
let (tree, actions) = parser
.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
.expect("runtime-option parser rule should parse");
assert!(actions.is_empty());
assert_eq!(
tree.first_rule_stop(0)
.expect("rule should stop at EOF")
.token_type(),
TOKEN_EOF
);
}
#[test]
fn parser_rule_start_skips_leading_hidden_tokens() {
let atn = token_then_eof_atn();
let mut parser = mini_parser(vec![
CommonToken::new(99)
.with_text(" ")
.with_channel(HIDDEN_CHANNEL),
CommonToken::new(1).with_text("x"),
CommonToken::eof("parser-test", 2, 1, 2),
]);
let tree = parser
.parse_atn_rule(&atn, 0)
.expect("artificial parser rule should parse");
let Some(ParseTree::Rule(rule)) = tree.first_rule(0) else {
panic!("rule node should be present");
};
assert_eq!(
rule.context()
.start()
.expect("rule should have a start token")
.token_type(),
1
);
}
#[test]
fn parser_action_after_eof_stops_at_eof_token() {
let atn = eof_then_action_atn();
let mut parser = mini_parser(vec![CommonToken::eof("parser-test", 0, 1, 0)]);
let (_, actions) = parser
.parse_atn_rule_with_runtime_options(&atn, 0, ParserRuntimeOptions::default())
.expect("EOF action rule should parse");
assert_eq!(actions.len(), 1);
assert_eq!(actions[0].stop_index(), Some(0));
assert_eq!(
parser.text_interval(actions[0].start_index(), actions[0].stop_index()),
""
);
}
#[test]
fn fast_outcome_selection_respects_sll_tie_order() {
let first = FastRecognizeOutcome {
index: 1,
consumed_eof: false,
diagnostics: vec![ParserDiagnostic {
line: 1,
column: 0,
message: "mismatched input 'x'".to_owned(),
}],
nodes: NodeList::new(),
};
let second = FastRecognizeOutcome {
index: first.index,
consumed_eof: first.consumed_eof,
diagnostics: Vec::new(),
nodes: NodeList::new(),
};
let selected = select_best_fast_outcome(
[first.clone(), second.clone()].into_iter(),
PredictionMode::Sll,
)
.expect("one outcome should be selected");
assert_eq!(selected.diagnostics.len(), 1);
let eof_second = FastRecognizeOutcome {
index: second.index,
consumed_eof: true,
diagnostics: Vec::new(),
nodes: NodeList::new(),
};
let selected =
select_best_fast_outcome([first.clone(), eof_second].into_iter(), PredictionMode::Sll)
.expect("one outcome should be selected");
assert!(!selected.consumed_eof);
let selected = select_best_fast_outcome([first, second].into_iter(), PredictionMode::Ll)
.expect("one outcome should be selected");
assert!(selected.diagnostics.is_empty());
}
#[test]
fn parser_error_with_empty_expected_set_omits_empty_set_display() {
let source = Source {
tokens: vec![
CommonToken::new(1).with_text("x"),
CommonToken::eof("parser-test", 1, 1, 1),
],
index: 0,
};
let data = RecognizerData::new(
"Mini.g4",
Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
);
let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
let expected = ExpectedTokens {
index: Some(0),
symbols: BTreeSet::new(),
no_viable: None,
};
let (_, message) = parser.expected_error_message(0, 0, &expected);
assert_eq!(message, "mismatched input 'x'");
}
#[test]
fn eof_rule_stop_index_points_at_eof_token() {
let source = Source {
tokens: vec![
CommonToken::new(1).with_text("x"),
CommonToken::eof("parser-test", 1, 1, 1),
],
index: 0,
};
let data = RecognizerData::new(
"Mini.g4",
Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
);
let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
assert_eq!(parser.rule_stop_token_index(1, true), Some(1));
assert_eq!(parser.rule_stop_token_index(1, false), Some(0));
}
#[test]
fn folds_left_recursive_boundary_into_rule_node() {
let nodes = fold_left_recursive_boundaries(vec![
RecognizedNode::Token { index: 0 },
RecognizedNode::LeftRecursiveBoundary { rule_index: 1 },
RecognizedNode::Token { index: 1 },
]);
assert_eq!(
nodes,
vec![
RecognizedNode::Rule {
rule_index: 1,
invoking_state: -1,
alt_number: 0,
start_index: 0,
stop_index: Some(0),
return_values: BTreeMap::new(),
children: vec![RecognizedNode::Token { index: 0 }],
},
RecognizedNode::Token { index: 1 },
]
);
}
#[test]
fn outcome_ties_keep_later_non_recursive_alternative() {
let first = RecognizeOutcome {
index: 1,
consumed_eof: false,
alt_number: 0,
member_values: BTreeMap::new(),
return_values: BTreeMap::new(),
diagnostics: Vec::new(),
decisions: Vec::new(),
actions: vec![ParserAction::new(1, 0, 0, None)],
nodes: vec![RecognizedNode::Token { index: 0 }],
};
let second = RecognizeOutcome {
actions: vec![ParserAction::new(2, 0, 0, None)],
..first.clone()
};
let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
.expect("one outcome should be selected");
assert_eq!(selected.actions[0].source_state(), 2);
}
#[test]
fn outcome_ties_prefer_more_actions_for_non_recursive_paths() {
let first = RecognizeOutcome {
index: 1,
consumed_eof: false,
alt_number: 0,
member_values: BTreeMap::new(),
return_values: BTreeMap::new(),
diagnostics: Vec::new(),
decisions: Vec::new(),
actions: vec![ParserAction::new(1, 0, 0, None)],
nodes: vec![RecognizedNode::Token { index: 0 }],
};
let second = RecognizeOutcome {
actions: vec![
ParserAction::new(2, 0, 0, None),
ParserAction::new(3, 0, 0, None),
],
..first.clone()
};
let selected = select_best_outcome([second, first].into_iter(), PredictionMode::Ll)
.expect("one outcome should be selected");
assert_eq!(selected.actions.len(), 2);
}
#[test]
fn outcome_ties_prefer_later_action_stop_for_greedy_optional_paths() {
let first = RecognizeOutcome {
index: 7,
consumed_eof: false,
alt_number: 0,
member_values: BTreeMap::new(),
return_values: BTreeMap::new(),
diagnostics: Vec::new(),
decisions: vec![1, 0],
actions: vec![
ParserAction::new(23, 2, 2, Some(4)),
ParserAction::new(23, 2, 0, Some(6)),
],
nodes: vec![RecognizedNode::Token { index: 0 }],
};
let second = RecognizeOutcome {
decisions: vec![0, 1],
actions: vec![
ParserAction::new(23, 2, 2, Some(6)),
ParserAction::new(23, 2, 0, Some(6)),
],
..first.clone()
};
let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
.expect("one outcome should be selected");
assert_eq!(selected.actions[0].stop_index(), Some(6));
}
#[test]
fn outcome_ties_keep_first_recursive_tree_shape() {
let recursive_nodes = vec![RecognizedNode::Rule {
rule_index: 1,
invoking_state: -1,
alt_number: 0,
start_index: 0,
stop_index: Some(0),
return_values: BTreeMap::new(),
children: vec![RecognizedNode::Rule {
rule_index: 1,
invoking_state: -1,
alt_number: 0,
start_index: 0,
stop_index: Some(0),
return_values: BTreeMap::new(),
children: vec![RecognizedNode::Token { index: 0 }],
}],
}];
let first = RecognizeOutcome {
index: 1,
consumed_eof: false,
alt_number: 0,
member_values: BTreeMap::new(),
return_values: BTreeMap::new(),
diagnostics: Vec::new(),
decisions: Vec::new(),
actions: vec![ParserAction::new(1, 0, 0, None)],
nodes: recursive_nodes.clone(),
};
let second = RecognizeOutcome {
index: 1,
consumed_eof: false,
alt_number: 0,
member_values: BTreeMap::new(),
return_values: BTreeMap::new(),
diagnostics: Vec::new(),
decisions: Vec::new(),
actions: vec![ParserAction::new(2, 0, 0, None)],
nodes: recursive_nodes,
};
let selected = select_best_outcome([first, second].into_iter(), PredictionMode::Ll)
.expect("one outcome should be selected");
assert_eq!(selected.actions[0].source_state(), 1);
}
#[test]
fn sll_outcome_selection_keeps_earlier_recovered_alt() {
let first_alt = RecognizeOutcome {
index: 2,
consumed_eof: true,
alt_number: 0,
member_values: BTreeMap::new(),
return_values: BTreeMap::new(),
diagnostics: vec![ParserDiagnostic {
line: 1,
column: 3,
message: "missing 'Y' at '<EOF>'".to_owned(),
}],
decisions: vec![0],
actions: vec![ParserAction::new(1, 0, 0, None)],
nodes: vec![RecognizedNode::Token { index: 0 }],
};
let second_alt = RecognizeOutcome {
diagnostics: Vec::new(),
decisions: vec![1],
actions: vec![ParserAction::new(2, 0, 0, None)],
..first_alt.clone()
};
let selected =
select_best_outcome([second_alt, first_alt].into_iter(), PredictionMode::Sll)
.expect("one outcome should be selected");
assert_eq!(selected.diagnostics.len(), 1);
assert_eq!(selected.decisions, [0]);
}
}