use std::any::TypeId;
use std::collections::{BTreeMap, BTreeSet};
use derivative::Derivative;
use crate::Lexicon;
use super::{First, FirstSet};
#[derive(Derivative, Debug)]
#[derivative(Default(new = "true", bound = ""))]
pub struct FirstBuilder<L: Lexicon> {
rels: Vec<FirstRel<L>>,
}
impl<L: Lexicon> FirstBuilder<L> {
#[inline]
pub fn add(&mut self, expr: FirstRel<L>) {
self.rels.push(expr);
}
#[inline]
pub fn build_union(&mut self, x: TypeId, variants: &[TypeId]) {
for y in variants {
self.add(FirstRel::union_minus_epsilon(x, *y));
self.add(FirstRel::if_epsilon_in_all_iter(
[y],
FirstRel::insert_epsilon(x),
));
}
}
#[inline]
pub fn build_sequence(&mut self, x: TypeId, sequence: &[TypeId]) {
let mut set = BTreeSet::new();
for yi in sequence {
self.add(FirstRel::if_epsilon_in_all_iter(
set.iter(),
FirstRel::union_minus_epsilon(x, *yi),
));
set.insert(*yi);
}
self.add(FirstRel::if_epsilon_in_all(
set,
FirstRel::insert_epsilon(x),
));
}
pub fn build(self) -> First<L> {
let mut first = BTreeMap::<TypeId, FirstSet<L>>::new();
let mut rels = self.rels;
let mut changed = true;
while changed {
changed = Self::process_rels(&mut first, &mut rels);
}
First::new(first)
}
#[must_use]
fn process_rels(
first: &mut BTreeMap<TypeId, FirstSet<L>>,
rels: &mut Vec<FirstRel<L>>,
) -> bool {
let mut changed = false;
rels.retain_mut(|rel| {
let (chg, retain) = rel.process_rel(first);
changed |= chg;
retain
});
changed
}
}
#[derive(Debug, PartialEq)]
pub enum FirstRel<L: Lexicon> {
Insert(TypeId, FirstInsert<L>),
UnionMinusEpsilon(TypeId, TypeId),
IfEpsilonInAll(BTreeSet<TypeId>, Option<Box<FirstRel<L>>>),
}
impl<L: Lexicon> FirstRel<L> {
#[inline]
pub fn insert_epsilon(t: TypeId) -> Self {
Self::Insert(t, FirstInsert::Epsilon)
}
#[inline]
pub fn insert_token(t: TypeId, token: L, lit: Option<&'static str>) -> Self {
Self::Insert(t, FirstInsert::Token(token, lit))
}
#[inline]
pub fn union_minus_epsilon(a: TypeId, b: TypeId) -> Self {
Self::UnionMinusEpsilon(a, b)
}
#[inline]
pub fn if_epsilon_in_all_iter<'s, Iter: IntoIterator<Item = &'s TypeId>>(
set: Iter,
expr: FirstRel<L>,
) -> Self {
let set = set.into_iter().copied().collect::<BTreeSet<_>>();
Self::if_epsilon_in_all(set, expr)
}
#[inline]
pub fn if_epsilon_in_all(set: BTreeSet<TypeId>, expr: FirstRel<L>) -> Self {
if set.is_empty() {
return expr;
}
Self::IfEpsilonInAll(set, Some(Box::new(expr)))
}
#[must_use]
fn process_rel(&mut self, first: &mut BTreeMap<TypeId, FirstSet<L>>) -> (bool, bool) {
match self {
Self::Insert(t, insert) => {
let set = first.entry(*t).or_default();
let changed = match insert {
FirstInsert::Epsilon => set.insert_epsilon(),
FirstInsert::Token(token, lit) => set.insert(*token, *lit),
};
(changed, false)
}
Self::UnionMinusEpsilon(a, b) => {
let mut first_a = first.remove(a).unwrap_or_default();
let changed = match first.get(b) {
Some(first_b) => first_a.union_minus_epsilon(first_b),
None => false,
};
first.insert(*a, first_a);
(changed, true)
}
Self::IfEpsilonInAll(set, inner) => {
set.retain(|t| match first.get(t) {
Some(set) => !set.contains_epsilon(),
None => true,
});
if !set.is_empty() {
return (false, true);
}
if let Some(mut inner) = std::mem::take(inner) {
let (changed, retain) = inner.process_rel(first);
*self = *inner;
return (changed, retain);
}
(false, false)
}
}
}
}
#[derive(Debug, PartialEq)]
pub enum FirstInsert<L: Lexicon> {
Token(L, Option<&'static str>),
Epsilon,
}