use std::rc::Rc;
use crate::analysis::FirstCache;
use crate::grammar::cfg::{NonTerminalIndexFn, TerminalIndexFn};
use crate::grammar::symbol_string::SymbolString;
use crate::{CompiledTerminal, GrammarConfig, KTuples, Pr, Symbol};
use parol_runtime::TerminalIndex;
use parol_runtime::lexer::FIRST_USER_TOKEN;
use parol_runtime::log::trace;
use super::k_tuples::KTuplesBuilder;
#[derive(Debug, Clone, Default)]
pub struct FirstSet {
pub productions: Vec<KTuples>,
pub non_terminals: Vec<KTuples>,
}
impl FirstSet {
pub fn is_empty(&self) -> bool {
self.productions.is_empty() && self.non_terminals.is_empty()
}
}
type DomainType = KTuples;
type DomainTypeBuilder<'a> = KTuplesBuilder<'a>;
type ResultVector = Vec<DomainType>;
#[derive(Clone)]
enum ProductionPart {
TerminalSet(DomainType),
NonTerminal(usize),
}
type EquationSystem = Vec<Vec<ProductionPart>>;
type StepFunction = Box<dyn Fn(Rc<ResultVector>) -> ResultVector>;
pub fn first_k(grammar_config: &GrammarConfig, k: usize, first_cache: &FirstCache) -> FirstSet {
let cfg = &grammar_config.cfg;
let pr_count = cfg.pr.len();
let nt_count = cfg.get_non_terminal_set().len();
let nti = Rc::new(grammar_config.cfg.get_non_terminal_index_function());
let ti = Rc::new(grammar_config.cfg.get_terminal_index_function());
let max_terminal_index = cfg.get_ordered_terminals().len() + FIRST_USER_TOKEN as usize;
let nt_for_production: Vec<usize> =
cfg.get_non_terminal_set()
.iter()
.fold(vec![0; pr_count], |mut acc, nt| {
let non_terminal_index = nti.non_terminal_index(nt);
for (pi, _) in cfg.matching_productions(nt) {
acc[pi] = non_terminal_index;
}
acc
});
let equation_system: EquationSystem =
cfg.pr
.iter()
.fold(Vec::with_capacity(pr_count), |mut es, pr| {
es.push(compile_production_equation(
pr,
Rc::clone(&ti),
Rc::clone(&nti),
k,
max_terminal_index,
));
es
});
trace!(
"Number of equations in equation system for FIRST(k) is {}",
equation_system.len()
);
let step_function: StepFunction = {
let empty_set = DomainTypeBuilder::new()
.k(k)
.max_terminal_index(max_terminal_index)
.build()
.unwrap();
let epsilon_set = DomainTypeBuilder::new()
.k(k)
.max_terminal_index(max_terminal_index)
.eps()
.unwrap();
Box::new(move |result_vector: Rc<ResultVector>| {
let mut new_result_vector: ResultVector = vec![empty_set.clone(); result_vector.len()];
let result_nt = &result_vector[pr_count..];
let (new_productions, new_non_terminals) = new_result_vector.split_at_mut(pr_count);
for ((equation, nt_index), production_slot) in equation_system
.iter()
.zip(nt_for_production.iter())
.zip(new_productions.iter_mut())
{
let mut r = epsilon_set.clone();
for part in equation {
r = match part {
ProductionPart::TerminalSet(terminal_set) => r.k_concat(terminal_set, k),
ProductionPart::NonTerminal(nt_index) => {
debug_assert!(*nt_index < result_nt.len());
let nt_tuple = &result_nt[*nt_index];
r.k_concat(nt_tuple, k)
}
};
}
debug_assert!(*nt_index < new_non_terminals.len());
new_non_terminals[*nt_index].append(r.clone());
*production_slot = r;
}
new_result_vector
})
};
let mut result_vector = Rc::new(if k == 0 {
(0..pr_count + nt_count).fold(Vec::with_capacity(pr_count + nt_count), |mut acc, i| {
if i < pr_count {
acc.push(
DomainTypeBuilder::new()
.k(k)
.max_terminal_index(max_terminal_index)
.build()
.unwrap(),
);
} else {
acc.push(
DomainTypeBuilder::new()
.k(k)
.max_terminal_index(max_terminal_index)
.eps()
.unwrap(),
);
}
acc
})
} else {
let last_first_set = first_cache.get(k - 1, grammar_config).borrow().clone();
let mut result_vector = Vec::with_capacity(pr_count + nt_count);
for t in last_first_set.productions.iter() {
result_vector.push(t.clone().set_k(k));
}
debug_assert_eq!(last_first_set.non_terminals.len(), nt_count);
for t in last_first_set.non_terminals.iter() {
result_vector.push(t.clone().set_k(k));
}
result_vector
});
let mut iterations = 0usize;
loop {
let new_result_vector = Rc::new(step_function(result_vector.clone()));
if new_result_vector == result_vector {
break;
}
result_vector = new_result_vector;
iterations += 1;
trace!("Iteration number {iterations} completed");
}
let (r, k_tuples_of_nt) = result_vector.split_at(pr_count);
FirstSet {
productions: r.to_vec(),
non_terminals: k_tuples_of_nt.to_vec(),
}
}
fn compile_production_equation<N, T>(
pr: &Pr,
ti_fn: Rc<T>,
nti_fn: Rc<N>,
k: usize,
max_terminal_index: usize,
) -> Vec<ProductionPart>
where
T: TerminalIndexFn,
N: NonTerminalIndexFn,
{
let parts = pr
.get_r()
.iter()
.fold(Vec::<SymbolString>::new(), |mut acc, s| {
match s {
Symbol::N(..) => acc.push(SymbolString(vec![s.clone()])),
Symbol::T(_) => {
if acc.is_empty() {
acc.push(SymbolString(vec![s.clone()]));
} else if let Some(last_part) = acc.last_mut() {
if matches!(last_part.0.last(), Some(Symbol::T(_))) {
last_part.0.push(s.clone());
} else {
acc.push(SymbolString(vec![s.clone()]));
}
}
}
_ => {
unreachable!(
"Scanner switching directives have been removed from the grammar syntax."
);
}
}
acc
});
let mut equation = Vec::with_capacity(parts.len());
for symbol_string in parts {
match &symbol_string.0[0] {
Symbol::T(_) => {
let terminal_indices: Vec<TerminalIndex> = symbol_string
.0
.iter()
.map(|s| CompiledTerminal::create(s, Rc::clone(&ti_fn)).0)
.collect();
let terminal_set = DomainTypeBuilder::new()
.k(k)
.max_terminal_index(max_terminal_index)
.terminal_indices(&[&terminal_indices])
.build()
.unwrap();
equation.push(ProductionPart::TerminalSet(terminal_set));
}
Symbol::N(nt, _, _, _) => {
equation.push(ProductionPart::NonTerminal(nti_fn.non_terminal_index(nt)));
}
_ => {
unreachable!(
"Scanner switching directives have been removed from the grammar syntax."
);
}
}
}
equation
}