use std::collections::BTreeSet;
use std::rc::Rc;
pub const EMPTY_RETURN_STATE: usize = usize::MAX;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum PredictionContext {
Empty,
Singleton {
parent: Rc<Self>,
return_state: usize,
},
Array {
parents: Vec<Rc<Self>>,
return_states: Vec<usize>,
},
}
impl PredictionContext {
pub fn empty() -> Rc<Self> {
Rc::new(Self::Empty)
}
pub fn singleton(parent: Rc<Self>, return_state: usize) -> Rc<Self> {
if return_state == EMPTY_RETURN_STATE {
Self::empty()
} else {
Rc::new(Self::Singleton {
parent,
return_state,
})
}
}
pub const fn len(&self) -> usize {
match self {
Self::Empty => 1,
Self::Singleton { .. } => 1,
Self::Array { return_states, .. } => return_states.len(),
}
}
pub const fn is_empty(&self) -> bool {
matches!(self, Self::Empty)
}
pub fn return_state(&self, index: usize) -> Option<usize> {
match self {
Self::Empty if index == 0 => Some(EMPTY_RETURN_STATE),
Self::Singleton { return_state, .. } if index == 0 => Some(*return_state),
Self::Array { return_states, .. } => return_states.get(index).copied(),
Self::Empty => None,
Self::Singleton { .. } => None,
}
}
pub fn parent(&self, index: usize) -> Option<Rc<Self>> {
match self {
Self::Empty => None,
Self::Singleton { parent, .. } if index == 0 => Some(Rc::clone(parent)),
Self::Array { parents, .. } => parents.get(index).cloned(),
Self::Singleton { .. } => None,
}
}
pub fn merge(left: Rc<Self>, right: Rc<Self>) -> Rc<Self> {
if left == right {
return left;
}
let mut entries = Vec::new();
collect_entries(&left, &mut entries);
collect_entries(&right, &mut entries);
drop((left, right));
entries.sort_by(|(left_parent, left_return), (right_parent, right_return)| {
left_return
.cmp(right_return)
.then_with(|| left_parent.cmp(right_parent))
});
entries.dedup_by(|a, b| a.1 == b.1 && a.0 == b.0);
Rc::new(Self::Array {
parents: entries
.iter()
.map(|(parent, _)| Rc::clone(parent))
.collect(),
return_states: entries
.iter()
.map(|(_, return_state)| *return_state)
.collect(),
})
}
}
fn collect_entries(
context: &Rc<PredictionContext>,
entries: &mut Vec<(Rc<PredictionContext>, usize)>,
) {
match context.as_ref() {
PredictionContext::Empty => entries.push((Rc::clone(context), EMPTY_RETURN_STATE)),
PredictionContext::Singleton {
parent,
return_state,
} => entries.push((Rc::clone(parent), *return_state)),
PredictionContext::Array {
parents,
return_states,
} => {
for (parent, return_state) in parents.iter().zip(return_states) {
entries.push((Rc::clone(parent), *return_state));
}
}
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct AtnConfig {
pub state: usize,
pub alt: usize,
pub context: Rc<PredictionContext>,
pub reaches_into_outer_context: usize,
}
impl AtnConfig {
pub const fn new(state: usize, alt: usize, context: Rc<PredictionContext>) -> Self {
Self {
state,
alt,
context,
reaches_into_outer_context: 0,
}
}
}
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct AtnConfigSet {
configs: Vec<AtnConfig>,
config_index: BTreeSet<AtnConfig>,
has_semantic_context: bool,
dips_into_outer_context: bool,
readonly: bool,
}
impl AtnConfigSet {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, config: AtnConfig) -> bool {
assert!(!self.readonly, "cannot mutate readonly ATN config set");
if self.config_index.insert(config.clone()) {
if config.reaches_into_outer_context > 0 {
self.dips_into_outer_context = true;
}
self.configs.push(config);
true
} else {
false
}
}
pub fn configs(&self) -> &[AtnConfig] {
&self.configs
}
pub const fn is_empty(&self) -> bool {
self.configs.is_empty()
}
pub const fn len(&self) -> usize {
self.configs.len()
}
pub const fn set_readonly(&mut self, readonly: bool) {
self.readonly = readonly;
}
pub const fn has_semantic_context(&self) -> bool {
self.has_semantic_context
}
pub const fn set_has_semantic_context(&mut self, value: bool) {
self.has_semantic_context = value;
}
pub const fn dips_into_outer_context(&self) -> bool {
self.dips_into_outer_context
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn config_set_deduplicates_configs() {
let empty = PredictionContext::empty();
let mut set = AtnConfigSet::new();
assert!(set.add(AtnConfig::new(1, 1, Rc::clone(&empty))));
assert!(!set.add(AtnConfig::new(1, 1, Rc::clone(&empty))));
assert_eq!(set.len(), 1);
}
#[test]
fn singleton_context_reports_parent_and_return_state() {
let empty = PredictionContext::empty();
let context = PredictionContext::singleton(Rc::clone(&empty), 42);
assert_eq!(context.return_state(0), Some(42));
assert_eq!(context.parent(0), Some(empty));
}
#[test]
fn merge_with_empty_preserves_non_empty_return_state() {
let empty = PredictionContext::empty();
let singleton = PredictionContext::singleton(Rc::clone(&empty), 42);
let merged = PredictionContext::merge(Rc::clone(&singleton), Rc::clone(&empty));
assert_eq!(merged.len(), 2);
assert_eq!(merged.return_state(0), Some(42));
assert_eq!(merged.parent(0), Some(empty.clone()));
assert_eq!(merged.return_state(1), Some(EMPTY_RETURN_STATE));
assert_eq!(merged.parent(1), Some(empty));
}
#[test]
fn merge_deduplicates_entries_with_same_parent_and_return_state() {
let empty = PredictionContext::empty();
let parent_one = PredictionContext::singleton(Rc::clone(&empty), 1);
let parent_two = PredictionContext::singleton(Rc::clone(&empty), 2);
let left = Rc::new(PredictionContext::Array {
parents: vec![Rc::clone(&parent_one), parent_two],
return_states: vec![42, 42],
});
let right = PredictionContext::singleton(Rc::clone(&parent_one), 42);
let merged = PredictionContext::merge(left, right);
assert_eq!(merged.len(), 2);
}
}