use std::any::TypeId;
use std::collections::{BTreeMap, BTreeSet};
use crate::syntax::First;
use crate::Lexicon;
use super::{Follow, FollowSet};
#[derive(Debug)]
pub struct FollowBuilder<'a, L: Lexicon> {
first: &'a First<L>,
rels: Vec<FollowRel>,
}
impl<'a, L: Lexicon> FollowBuilder<'a, L> {
pub fn new(first: &'a First<L>) -> Self {
Self {
first,
rels: Default::default(),
}
}
#[inline]
pub fn add(&mut self, expr: FollowRel) {
self.rels.push(expr);
}
#[inline]
pub fn build_union(&mut self, x: TypeId, variants: &[TypeId]) {
for y in variants {
self.add(FollowRel::union_follow(*y, x));
}
}
pub fn build_sequence(&mut self, x: TypeId, sequence: &[TypeId]) {
let mut set = BTreeSet::new();
for yi in sequence.iter().rev() {
self.add(FollowRel::if_epsilon_in_all_first_iter(
set.iter(),
FollowRel::union_follow(*yi, x),
));
set.insert(*yi);
}
for yi in sequence.windows(2).rev() {
self.add(FollowRel::union_first_minus_epsilon(yi[0], yi[1]));
}
}
pub fn build(mut self, root: TypeId) -> Follow<L> {
let mut map = {
let mut map = BTreeMap::new();
let mut root_set = FollowSet::new();
root_set.insert_eof();
map.insert(root, root_set);
map
};
let mut changed = true;
while changed {
changed = false;
for rel in &mut self.rels {
changed = rel.process_rel(self.first, &mut map) || changed;
}
}
Follow {
map,
empty: FollowSet::new(),
}
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum FollowRel {
UnionFirstMinusEpsilon(TypeId, TypeId),
UnionFollow(TypeId, TypeId),
IfEpsilonInAllFirst(BTreeSet<TypeId>, Option<Box<FollowRel>>),
}
impl FollowRel {
#[inline]
pub fn union_first_minus_epsilon(a: TypeId, b: TypeId) -> Self {
Self::UnionFirstMinusEpsilon(a, b)
}
#[inline]
pub fn union_follow(a: TypeId, b: TypeId) -> Self {
Self::UnionFollow(a, b)
}
#[inline]
pub fn if_epsilon_in_all_first_iter<'s, Iter: IntoIterator<Item = &'s TypeId>>(
set: Iter,
expr: FollowRel,
) -> Self {
let set = set.into_iter().copied().collect::<BTreeSet<_>>();
Self::if_epsilon_in_all_first(set, expr)
}
#[inline]
pub fn if_epsilon_in_all_first(set: BTreeSet<TypeId>, expr: FollowRel) -> Self {
if set.is_empty() {
return expr;
}
Self::IfEpsilonInAllFirst(set, Some(Box::new(expr)))
}
#[must_use]
fn process_rel<L: Lexicon>(
&mut self,
first: &First<L>,
follow: &mut BTreeMap<TypeId, FollowSet<L>>,
) -> bool {
match self {
Self::UnionFirstMinusEpsilon(a, b) => {
let follow_a = follow.entry(*a).or_default();
let first_b = first.get(b);
follow_a.union_first(first_b)
}
Self::UnionFollow(a, b) => {
let mut follow_a = follow.remove(a).unwrap_or_default();
let changed = match follow.get(b) {
Some(follow_b) => follow_a.union_follow(follow_b),
None => false,
};
follow.insert(*a, follow_a);
changed
}
Self::IfEpsilonInAllFirst(set, inner) => {
set.retain(|t| !first.get(t).contains_epsilon());
if !set.is_empty() {
return false;
}
if let Some(mut inner) = std::mem::take(inner) {
let changed = inner.process_rel(first, follow);
*self = *inner;
return changed;
}
false
}
}
}
}