use std::collections::{HashMap, HashSet};
use ungrammar::{Grammar, Node, Rule, Token};
pub(super) struct FirstSet(HashMap<Node, HashSet<Token>>);
impl FirstSet {
pub(super) fn new() -> Self {
FirstSet(HashMap::new())
}
pub(super) fn contains(&self, node: &Node) -> bool {
self.0.contains_key(node)
}
pub(super) fn insert(&mut self, node: Node, set: HashSet<Token>) -> Option<HashSet<Token>> {
self.0.insert(node, set)
}
pub(super) fn get(&self, node: &Node) -> Option<&HashSet<Token>> {
self.0.get(node)
}
pub(super) fn get_first_of_sorted(&self, rule: &Rule, grammar: &Grammar) -> Vec<Token> {
let mut first_set = self
.get_first_of(rule, grammar)
.into_iter()
.collect::<Vec<Token>>();
first_set.sort();
return first_set;
}
pub(super) fn get_first_of(&self, rule: &Rule, grammar: &Grammar) -> HashSet<Token> {
match rule {
Rule::Rep(other)
| Rule::Opt(other)
| Rule::Labeled {
label: _,
rule: other,
} => self.get_first_of(other, grammar),
Rule::Node(node) => self
.get(node)
.expect("Every node should have a first-set")
.clone(),
Rule::Seq(rules) => {
let mut set: HashSet<Token> = HashSet::new();
for rule in rules.iter() {
set.extend(&self.get_first_of(rule, grammar).clone());
if !is_nullable(rule, grammar) {
break;
}
}
set
}
Rule::Alt(rules) => {
let set = rules.iter().fold(HashSet::new(), |mut accu, rule| {
accu.extend(self.get_first_of(rule, grammar));
accu
});
set
}
Rule::Token(token) => HashSet::from([*token]),
}
}
}
pub(super) fn compute_first(grammar: &Grammar) -> FirstSet {
let mut first = FirstSet::new();
for node in grammar.iter() {
compute_first_helper(node, &grammar, &mut first);
}
return first;
}
fn compute_first_helper(node: Node, grammar: &Grammar, first_set: &mut FirstSet) {
if !first_set.contains(&node) {
let set = get_first(&grammar[node].rule, grammar, first_set);
first_set.insert(node, set);
}
}
pub(super) fn is_nullable(rule: &Rule, grammar: &Grammar) -> bool {
match rule {
Rule::Token(_) => false,
Rule::Opt(_) => true,
Rule::Rep(_) => true,
Rule::Labeled {
label: _,
rule: other,
} => is_nullable(other, grammar),
Rule::Node(node) => is_nullable(&grammar[*node].rule, grammar),
Rule::Seq(rules) => rules.iter().all(|rule| is_nullable(rule, grammar)),
Rule::Alt(rules) => rules.iter().any(|rule| is_nullable(rule, grammar)),
}
}
pub(super) fn get_first(
rule: &Rule,
grammar: &Grammar,
first_set: &mut FirstSet,
) -> HashSet<Token> {
match rule {
Rule::Rep(other)
| Rule::Opt(other)
| Rule::Labeled {
label: _,
rule: other,
} => get_first(other, grammar, first_set),
Rule::Node(node) => {
compute_first_helper(*node, grammar, first_set);
first_set
.get(node)
.expect("FIRST name should have been connected")
.clone()
}
Rule::Seq(rules) => {
let mut set: HashSet<Token> = HashSet::new();
for rule in rules.iter() {
set.extend(get_first(rule, grammar, first_set));
if !is_nullable(rule, grammar) {
break;
}
}
set
}
Rule::Alt(rules) => rules.iter().fold(HashSet::new(), |mut accu, rule| {
accu.extend(get_first(rule, grammar, first_set));
accu
}),
Rule::Token(token) => HashSet::from([*token]),
}
}