#![allow(clippy::derive_partial_eq_without_eq)]
use std::{
collections::hash_map::{Entry, HashMap},
hash::{BuildHasherDefault, Hash},
};
use cfgrammar::{yacc::YaccGrammar, PIdx, SIdx, Symbol};
use fnv::FnvHasher;
use num_traits::{AsPrimitive, PrimInt, Unsigned};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use vob::Vob;
use cfgrammar::yacc::firsts::YaccFirsts;
pub type Ctx = Vob;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Itemset<StorageT: Eq + Hash> {
pub items: HashMap<(PIdx<StorageT>, SIdx<StorageT>), Ctx, BuildHasherDefault<FnvHasher>>,
}
impl<StorageT: 'static + Hash + PrimInt + Unsigned> Itemset<StorageT>
where
usize: AsPrimitive<StorageT>,
{
pub fn new(_: &YaccGrammar<StorageT>) -> Self {
Itemset {
items: HashMap::with_hasher(BuildHasherDefault::<FnvHasher>::default()),
}
}
pub fn add(&mut self, pidx: PIdx<StorageT>, dot: SIdx<StorageT>, ctx: &Ctx) -> bool {
let entry = self.items.entry((pidx, dot));
match entry {
Entry::Occupied(mut e) => e.get_mut().or(ctx),
Entry::Vacant(e) => {
e.insert(ctx.clone());
true
}
}
}
pub fn close(&self, grm: &YaccGrammar<StorageT>, firsts: &YaccFirsts<StorageT>) -> Self {
let mut new_is = self.clone();
let mut keys_iter = self.items.keys(); let mut zero_todos = Vob::from_elem(false, usize::from(grm.prods_len())); let mut new_ctx = Vob::from_elem(false, usize::from(grm.tokens_len()));
loop {
let pidx;
let dot;
match keys_iter.next() {
Some(&(x, y)) => {
pidx = x;
dot = y;
}
None => {
match zero_todos.iter_set_bits(..).next() {
Some(i) => {
pidx = PIdx(i.as_())
}
None => break,
}
dot = SIdx(StorageT::zero());
zero_todos.set(pidx.into(), false);
}
}
let prod = grm.prod(pidx);
if dot == grm.prod_len(pidx) {
continue;
}
if let Symbol::Rule(s_ridx) = prod[usize::from(dot)] {
new_ctx.set_all(false);
let mut nullable = true;
for sym in prod.iter().skip(usize::from(dot) + 1) {
match *sym {
Symbol::Token(s_tidx) => {
new_ctx.set(usize::from(s_tidx), true);
nullable = false;
break;
}
Symbol::Rule(s_ridx) => {
new_ctx.or(firsts.firsts(s_ridx));
if !firsts.is_epsilon_set(s_ridx) {
nullable = false;
break;
}
}
}
}
if nullable {
new_ctx.or(&new_is.items[&(pidx, dot)]);
}
for ref_pidx in grm.rule_to_prods(s_ridx).iter() {
if new_is.add(*ref_pidx, SIdx(StorageT::zero()), &new_ctx) {
zero_todos.set(usize::from(*ref_pidx), true);
}
}
}
}
new_is
}
pub fn goto(&self, grm: &YaccGrammar<StorageT>, sym: &Symbol<StorageT>) -> Self {
let mut newis = Itemset::new(grm);
for (&(pidx, dot), ctx) in &self.items {
let prod = grm.prod(pidx);
if dot == grm.prod_len(pidx) {
continue;
}
if sym == &prod[usize::from(dot)] {
newis.add(pidx, SIdx(dot.as_storaget() + StorageT::one()), ctx);
}
}
newis
}
}
#[cfg(test)]
mod test {
use cfgrammar::{
yacc::{YaccGrammar, YaccKind, YaccOriginalActionKind},
SIdx, Symbol,
};
use vob::Vob;
use super::Itemset;
use crate::stategraph::state_exists;
#[test]
#[rustfmt::skip]
fn test_dragon_grammar() {
let grm = YaccGrammar::new(
YaccKind::Original(YaccOriginalActionKind::GenericParseTree),
"
%start S
%%
S: L '=' R | R;
L: '*' R | 'id';
R: L;
"
).unwrap();
let firsts = grm.firsts();
let mut is = Itemset::new(&grm);
let mut la = Vob::from_elem(false, usize::from(grm.tokens_len()));
la.set(usize::from(grm.eof_token_idx()), true);
is.add(grm.rule_to_prods(grm.rule_idx("^").unwrap())[0], SIdx(0), &la);
let cls_is = is.close(&grm, &firsts);
println!("{:?}", cls_is);
assert_eq!(cls_is.items.len(), 6);
state_exists(&grm, &cls_is, "^", 0, SIdx(0), vec!["$"]);
state_exists(&grm, &cls_is, "S", 0, SIdx(0), vec!["$"]);
state_exists(&grm, &cls_is, "S", 1, SIdx(0), vec!["$"]);
state_exists(&grm, &cls_is, "L", 0, SIdx(0), vec!["$", "="]);
state_exists(&grm, &cls_is, "L", 1, SIdx(0), vec!["$", "="]);
state_exists(&grm, &cls_is, "R", 0, SIdx(0), vec!["$"]);
}
fn eco_grammar() -> YaccGrammar {
YaccGrammar::new(
YaccKind::Original(YaccOriginalActionKind::GenericParseTree),
"
%start S
%token a b c d f
%%
S: S 'b' | 'b' A 'a' | 'a';
A: 'a' S 'c' | 'a' | 'a' S 'b';
B: A S;
C: D A;
D: 'd' | ;
F: C D 'f';
",
)
.unwrap()
}
#[test]
#[rustfmt::skip]
fn test_closure1_ecogrm() {
let grm = eco_grammar();
let firsts = grm.firsts();
let mut is = Itemset::new(&grm);
let mut la = Vob::from_elem(false, usize::from(grm.tokens_len()));
la.set(usize::from(grm.eof_token_idx()), true);
is.add(grm.rule_to_prods(grm.rule_idx("^").unwrap())[0], SIdx(0), &la);
let mut cls_is = is.close(&grm, &firsts);
state_exists(&grm, &cls_is, "^", 0, SIdx(0), vec!["$"]);
state_exists(&grm, &cls_is, "S", 0, SIdx(0), vec!["b", "$"]);
state_exists(&grm, &cls_is, "S", 1, SIdx(0), vec!["b", "$"]);
state_exists(&grm, &cls_is, "S", 2, SIdx(0), vec!["b", "$"]);
is = Itemset::new(&grm);
is.add(grm.rule_to_prods(grm.rule_idx("F").unwrap())[0], SIdx(0), &la);
cls_is = is.close(&grm, &firsts);
state_exists(&grm, &cls_is, "F", 0, SIdx(0), vec!["$"]);
state_exists(&grm, &cls_is, "C", 0, SIdx(0), vec!["d", "f"]);
state_exists(&grm, &cls_is, "D", 0, SIdx(0), vec!["a"]);
state_exists(&grm, &cls_is, "D", 1, SIdx(0), vec!["a"]);
}
fn grammar3() -> YaccGrammar {
YaccGrammar::new(
YaccKind::Original(YaccOriginalActionKind::GenericParseTree),
"
%start S
%token a b c d
%%
S: S 'b' | 'b' A 'a';
A: 'a' S 'c' | 'a' | 'a' S 'b';
",
)
.unwrap()
}
#[test]
#[rustfmt::skip]
fn test_closure1_grm3() {
let grm = grammar3();
let firsts = grm.firsts();
let mut is = Itemset::new(&grm);
let mut la = Vob::from_elem(false, usize::from(grm.tokens_len()));
la.set(usize::from(grm.eof_token_idx()), true);
is.add(grm.rule_to_prods(grm.rule_idx("^").unwrap())[0], SIdx(0), &la);
let mut cls_is = is.close(&grm, &firsts);
state_exists(&grm, &cls_is, "^", 0, SIdx(0), vec!["$"]);
state_exists(&grm, &cls_is, "S", 0, SIdx(0), vec!["b", "$"]);
state_exists(&grm, &cls_is, "S", 1, SIdx(0), vec!["b", "$"]);
is = Itemset::new(&grm);
la = Vob::from_elem(false, usize::from(grm.tokens_len()));
la.set(usize::from(grm.token_idx("b").unwrap()), true);
la.set(usize::from(grm.eof_token_idx()), true);
is.add(grm.rule_to_prods(grm.rule_idx("S").unwrap())[1], SIdx(1), &la);
cls_is = is.close(&grm, &firsts);
state_exists(&grm, &cls_is, "A", 0, SIdx(0), vec!["a"]);
state_exists(&grm, &cls_is, "A", 1, SIdx(0), vec!["a"]);
state_exists(&grm, &cls_is, "A", 2, SIdx(0), vec!["a"]);
is = Itemset::new(&grm);
la = Vob::from_elem(false, usize::from(grm.tokens_len()));
la.set(usize::from(grm.token_idx("a").unwrap()), true);
is.add(grm.rule_to_prods(grm.rule_idx("A").unwrap())[0], SIdx(1), &la);
cls_is = is.close(&grm, &firsts);
state_exists(&grm, &cls_is, "S", 0, SIdx(0), vec!["b", "c"]);
state_exists(&grm, &cls_is, "S", 1, SIdx(0), vec!["b", "c"]);
}
#[test]
#[rustfmt::skip]
fn test_goto1() {
let grm = grammar3();
let firsts = grm.firsts();
let mut is = Itemset::new(&grm);
let mut la = Vob::from_elem(false, usize::from(grm.tokens_len()));
la.set(usize::from(grm.eof_token_idx()), true);
is.add(grm.rule_to_prods(grm.rule_idx("^").unwrap())[0], SIdx(0), &la);
let cls_is = is.close(&grm, &firsts);
let goto1 = cls_is.goto(&grm, &Symbol::Rule(grm.rule_idx("S").unwrap()));
state_exists(&grm, &goto1, "^", 0, SIdx(1), vec!["$"]);
state_exists(&grm, &goto1, "S", 0, SIdx(1), vec!["$", "b"]);
let goto2 = cls_is.goto(&grm, &Symbol::Token(grm.token_idx("b").unwrap()));
state_exists(&grm, &goto2, "S", 1, SIdx(1), vec!["$", "b"]);
let goto3 = goto2.close(&grm, &firsts).goto(&grm, &Symbol::Token(grm.token_idx("a").unwrap()));
state_exists(&grm, &goto3, "A", 1, SIdx(1), vec!["a"]);
state_exists(&grm, &goto3, "A", 2, SIdx(1), vec!["a"]);
}
}