use std::collections::{HashMap, HashSet};
use super::super::precedence::nullable;
use super::super::program::{Associativity, Expr, Name, Rule};
use super::Lead;
use super::lead_sets::{first_of_items, first_sets, follow_sets, left_corner_follow};
use crate::grammar::RuleKind;
#[derive(Debug, Default)]
pub(in crate::grammar::feature_runtime) struct Reductions {
first: Vec<HashSet<Lead>>,
pub(in crate::grammar::feature_runtime) splits: HashMap<(usize, usize), Split>,
pub(in crate::grammar::feature_runtime) keys: HashSet<Lead>,
}
#[derive(Debug)]
pub(in crate::grammar::feature_runtime) struct Split {
pub(in crate::grammar::feature_runtime) optional_from: usize,
pub(in crate::grammar::feature_runtime) always: HashSet<Lead>,
pub(in crate::grammar::feature_runtime) marked: HashSet<Lead>,
}
impl Reductions {
pub(in crate::grammar::feature_runtime) fn key(items: &[Expr]) -> (usize, usize) {
(items.as_ptr().addr(), items.len())
}
pub(in crate::grammar::feature_runtime) fn marked(
&self,
items: &[Expr],
rules: &[Rule],
) -> Option<HashSet<Lead>> {
let mut keys = HashSet::new();
first_of_items(items, rules, &self.first, &mut keys);
keys.retain(|key| self.keys.contains(key));
(!keys.is_empty()).then_some(keys)
}
}
fn left_sequences<'e>(expr: &'e Expr, found: &mut Vec<&'e [Expr]>) {
match expr {
Expr::Choice { items, .. } => {
for item in items {
left_sequences(item, found);
}
}
Expr::Capture { item, .. } | Expr::Alias { item, .. } => left_sequences(item, found),
Expr::Precedence {
associativity: Associativity::Left,
item,
..
} => {
if let Expr::Seq(items) = &**item {
found.push(items);
}
}
_ => {}
}
}
pub(super) fn reduction_facts(conflicts: &[HashSet<Name>], rules: &[Rule]) -> Reductions {
let first = first_sets(rules);
let (mut follow, mut corner, mut shared) = (None, None, None);
let mut splits = HashMap::new();
let mut keys = HashSet::new();
let begins = |name: &Name, key: &Lead| match rules.iter().position(|rule| rule.name == *name) {
Some(index) if !matches!(rules[index].kind, RuleKind::Token | RuleKind::Atomic) => {
first[index].contains(key)
}
_ => *key == Lead::Ref(name.clone()),
};
let forked = |name: &Name, key: &Lead| {
conflicts.iter().any(|group| {
group.contains(name)
&& group
.iter()
.any(|other| other != name && begins(other, key))
})
};
for (index, rule) in rules.iter().enumerate() {
if !matches!(rule.kind, RuleKind::Normal) {
continue;
}
let mut sequences = Vec::new();
left_sequences(&rule.expression, &mut sequences);
for items in sequences {
let mut split = items.len();
while split > 0 && nullable(&items[split - 1]) {
split -= 1;
}
if split == 0 || split == items.len() {
continue;
}
let follow = follow.get_or_insert_with(|| follow_sets(rules, &first));
let corner = corner.get_or_insert_with(|| left_corner_follow(rules, &first));
let shared = shared.get_or_insert_with(|| shared_aliases(rules));
let mut parts = HashSet::new();
first_of_items(&items[split..], rules, &first, &mut parts);
parts.retain(|key| {
follow[index].contains(key) && !shared.contains(key) && !forked(&rule.name, key)
});
if parts.is_empty() {
continue;
}
let (always, marked): (HashSet<Lead>, HashSet<Lead>) = parts
.into_iter()
.partition(|key| corner[index].contains(key));
keys.extend(marked.iter().cloned());
splits.insert(
Reductions::key(items),
Split {
optional_from: split,
always,
marked,
},
);
}
}
Reductions {
first,
splits,
keys,
}
}
fn shared_aliases(rules: &[Rule]) -> HashSet<Lead> {
let mut contents: HashMap<&Name, HashSet<String>> = HashMap::new();
let mut pending: Vec<&Expr> = rules.iter().map(|rule| &rule.expression).collect();
while let Some(expr) = pending.pop() {
match expr {
Expr::Alias { name, item } => {
contents
.entry(name)
.or_default()
.insert(format!("{item:?}"));
pending.push(item);
}
Expr::Seq(items) | Expr::Choice { items, .. } | Expr::Longest(items) => {
pending.extend(items);
}
Expr::Repeat { item, .. }
| Expr::And(item)
| Expr::Not(item)
| Expr::Capture { item, .. }
| Expr::Precedence { item, .. }
| Expr::DynamicPrecedence { item, .. }
| Expr::LexicalPrecedence { item, .. }
| Expr::Token(item)
| Expr::ImmediateToken(item)
| Expr::Predicate { item, .. }
| Expr::Recover { item, .. }
| Expr::Missing { item, .. }
| Expr::Embed { item, .. } => pending.push(item),
_ => {}
}
}
contents
.into_iter()
.filter(|(_, seen)| seen.len() > 1)
.map(|(name, _)| Lead::Ref(name.clone()))
.collect()
}