use std::{
collections::{hash_map::HashMap, HashSet},
hash::Hash,
};
use cfgrammar::{yacc::YaccGrammar, SIdx, Symbol};
use num_traits::{AsPrimitive, PrimInt, Unsigned};
use vob::Vob;
use crate::{itemset::Itemset, stategraph::StateGraph, StIdx};
impl<StorageT: Hash + PrimInt + Unsigned> Itemset<StorageT> {
fn weakly_compatible(&self, other: &Self) -> bool {
let len = self.items.len();
if len != other.items.len() {
return false;
}
for &(pidx, dot) in self.items.keys() {
if other.items.get(&(pidx, dot)).is_none() {
return false;
}
}
if len == 1 {
return true;
}
let keys: Vec<_> = self.items.keys().collect();
for (i, i_key) in keys.iter().enumerate().take(len - 1) {
for j_key in keys.iter().take(len).skip(i + 1) {
if !(vob_intersect(&self.items[*i_key], &other.items[*j_key])
|| vob_intersect(&self.items[*j_key], &other.items[*i_key]))
{
continue;
}
if vob_intersect(&self.items[*i_key], &self.items[*j_key])
|| vob_intersect(&other.items[*i_key], &other.items[*j_key])
{
continue;
}
return false;
}
}
true
}
fn weakly_merge(&mut self, other: &Self) -> bool {
let mut changed = false;
for (&(pidx, dot), ctx) in &mut self.items {
if ctx.or(&other.items[&(pidx, dot)]) {
changed = true;
}
}
changed
}
}
fn vob_intersect(v1: &Vob, v2: &Vob) -> bool {
for (b1, b2) in v1.iter_storage().zip(v2.iter_storage()) {
if b1 & b2 != 0 {
return true;
}
}
false
}
pub fn pager_stategraph<StorageT: 'static + Hash + PrimInt + Unsigned>(
grm: &YaccGrammar<StorageT>,
) -> StateGraph<StorageT>
where
usize: AsPrimitive<StorageT>,
{
let firsts = grm.firsts();
let mut closed_states = Vec::new();
let mut core_states = Vec::new();
let mut edges: Vec<HashMap<Symbol<StorageT>, StIdx<usize>>> = Vec::new();
let start_state = StIdx(0usize);
let mut state0 = Itemset::new(grm);
let mut ctx = Vob::from_elem(false, usize::from(grm.tokens_len()));
ctx.set(usize::from(grm.eof_token_idx()), true);
state0.add(grm.start_prod(), SIdx(StorageT::zero()), &ctx);
closed_states.push(None);
core_states.push(state0);
edges.push(HashMap::new());
let mut seen_rules = Vob::from_elem(false, usize::from(grm.rules_len()));
let mut seen_tokens = Vob::from_elem(false, usize::from(grm.tokens_len()));
let mut new_states = Vec::new();
let mut cnd_rule_weaklies: Vec<Vec<StIdx<usize>>> =
vec![Vec::new(); usize::from(grm.rules_len())];
let mut cnd_token_weaklies: Vec<Vec<StIdx<usize>>> =
vec![Vec::new(); usize::from(grm.tokens_len()).checked_add(1).unwrap()];
let mut todo = 1; let mut todo_off = 0; while todo > 0 {
debug_assert_eq!(core_states.len(), closed_states.len());
let state_i = match closed_states
.iter()
.skip(todo_off)
.position(Option::is_none)
{
Some(i) => todo_off + i,
None => closed_states.iter().position(Option::is_none).unwrap(),
};
todo_off = state_i + 1;
todo -= 1;
{
closed_states[state_i] = Some(core_states[state_i].close(grm, &firsts));
let cl_state = &closed_states[state_i].as_ref().unwrap();
seen_rules.set_all(false);
seen_tokens.set_all(false);
for &(pidx, dot) in cl_state.items.keys() {
let prod = grm.prod(pidx);
if dot == grm.prod_len(pidx) {
continue;
}
let sym = prod[usize::from(dot)];
match sym {
Symbol::Rule(s_ridx) => {
if seen_rules[usize::from(s_ridx)] {
continue;
}
seen_rules.set(usize::from(s_ridx), true);
}
Symbol::Token(s_tidx) => {
if seen_tokens[usize::from(s_tidx)] {
continue;
}
seen_tokens.set(usize::from(s_tidx), true);
}
}
let nstate = cl_state.goto(grm, &sym);
new_states.push((sym, nstate));
}
}
'a: for (sym, nstate) in new_states.drain(..) {
let mut m = None;
{
let cnd_states = match sym {
Symbol::Rule(s_ridx) => &cnd_rule_weaklies[usize::from(s_ridx)],
Symbol::Token(s_tidx) => &cnd_token_weaklies[usize::from(s_tidx)],
};
for cnd in cnd_states.iter().cloned() {
if core_states[usize::from(cnd)] == nstate {
edges[state_i].insert(sym, cnd);
continue 'a;
}
}
for cnd in cnd_states.iter().cloned() {
if core_states[usize::from(cnd)].weakly_compatible(&nstate) {
m = Some(cnd);
break;
}
}
}
match m {
Some(k) => {
edges[state_i].insert(sym, k);
if core_states[usize::from(k)].weakly_merge(&nstate) {
if closed_states[usize::from(k)].is_some() {
closed_states[usize::from(k)] = None;
todo += 1;
}
}
}
None => {
if core_states.len() >= num_traits::cast(StorageT::max_value()).unwrap() {
panic!("StorageT is not big enough to store this stategraph.");
}
let stidx = StIdx(core_states.len());
match sym {
Symbol::Rule(s_ridx) => {
cnd_rule_weaklies[usize::from(s_ridx)].push(stidx);
}
Symbol::Token(s_tidx) => {
cnd_token_weaklies[usize::from(s_tidx)].push(stidx);
}
}
edges[state_i].insert(sym, stidx);
edges.push(HashMap::new());
closed_states.push(None);
core_states.push(nstate);
todo += 1;
}
}
}
}
debug_assert_eq!(core_states.len(), closed_states.len());
let (gc_states, gc_edges) = gc(
core_states
.drain(..)
.zip(closed_states.drain(..).map(Option::unwrap))
.collect(),
start_state,
edges,
);
if gc_states.len() > num_traits::cast(StorageT::max_value()).unwrap() {
panic!("StorageT is not big enough to store this stategraph.");
}
let start_state = StIdx::<StorageT>(start_state.as_storaget().as_());
let mut gc_edges_storaget = Vec::with_capacity(gc_edges.len());
for x in gc_edges {
let mut m = HashMap::with_capacity(x.len());
for (k, v) in x {
m.insert(k, StIdx::<StorageT>(v.as_storaget().as_()));
}
gc_edges_storaget.push(m);
}
StateGraph::new(gc_states, start_state, gc_edges_storaget)
}
fn gc<StorageT: 'static + Eq + Hash + PrimInt + Unsigned>(
mut states: Vec<(Itemset<StorageT>, Itemset<StorageT>)>,
start_state: StIdx<usize>,
mut edges: Vec<HashMap<Symbol<StorageT>, StIdx<usize>>>,
) -> (
Vec<(Itemset<StorageT>, Itemset<StorageT>)>,
Vec<HashMap<Symbol<StorageT>, StIdx<usize>>>,
)
where
usize: AsPrimitive<StorageT>,
{
let mut todo = HashSet::new();
todo.insert(start_state);
let mut seen = HashSet::new();
while !todo.is_empty() {
let state_i = *todo.iter().next().unwrap();
todo.remove(&state_i);
seen.insert(state_i);
todo.extend(
edges[usize::from(state_i)]
.values()
.filter(|x| !seen.contains(x)),
);
}
if states.len() == seen.len() {
return (states, edges);
}
let mut gc_states = Vec::with_capacity(seen.len());
let mut offsets = Vec::with_capacity(states.len());
let mut offset = 0;
for (state_i, zstate) in states.drain(..).enumerate() {
offsets.push(StIdx(state_i - offset));
if !seen.contains(&StIdx(state_i)) {
offset += 1;
continue;
}
gc_states.push(zstate);
}
let mut gc_edges = Vec::with_capacity(seen.len());
for (st_edge_i, st_edges) in edges.drain(..).enumerate() {
if !seen.contains(&StIdx(st_edge_i)) {
continue;
}
gc_edges.push(
st_edges
.iter()
.map(|(&k, &v)| (k, offsets[usize::from(v)]))
.collect(),
);
}
(gc_states, gc_edges)
}
#[cfg(test)]
mod test {
use vob::Vob;
use crate::{pager::pager_stategraph, stategraph::state_exists, StIdx};
use cfgrammar::{
yacc::{YaccGrammar, YaccKind, YaccOriginalActionKind},
SIdx, Symbol,
};
use super::vob_intersect;
#[test]
fn test_vob_intersect() {
let mut b1 = Vob::from_elem(false, 8);
let mut b2 = Vob::from_elem(false, 8);
assert!(!vob_intersect(&b1, &b2));
b1.push(false);
b2.push(false);
assert!(!vob_intersect(&b1, &b2));
b1.push(true);
b2.push(true);
assert!(vob_intersect(&b1, &b2));
b1 = Vob::from_elem(false, 64);
b2 = Vob::from_elem(false, 64);
b1.push(true);
b2.push(true);
for _ in 0..63 {
b1.push(false);
b2.push(false);
}
assert!(vob_intersect(&b1, &b2));
}
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_stategraph() {
let grm = grammar3();
let sg = pager_stategraph(&grm);
assert_eq!(sg.all_states_len(), StIdx(10));
assert_eq!(sg.all_edges_len(), 10);
assert_eq!(sg.closed_state(sg.start_state()).items.len(), 3);
state_exists(&grm, sg.closed_state(sg.start_state()), "^", 0, SIdx(0), vec!["$"]);
state_exists(&grm, sg.closed_state(sg.start_state()), "S", 0, SIdx(0), vec!["$", "b"]);
state_exists(&grm, sg.closed_state(sg.start_state()), "S", 1, SIdx(0), vec!["$", "b"]);
let s1 = sg.edge(sg.start_state(), Symbol::Rule(grm.rule_idx("S").unwrap())).unwrap();
assert_eq!(sg.closed_state(s1).items.len(), 2);
state_exists(&grm, sg.closed_state(s1), "^", 0, SIdx(1), vec!["$"]);
state_exists(&grm, sg.closed_state(s1), "S", 0, SIdx(1), vec!["$", "b"]);
let s2 = sg.edge(s1, Symbol::Token(grm.token_idx("b").unwrap())).unwrap();
assert_eq!(sg.closed_state(s2).items.len(), 1);
state_exists(&grm, sg.closed_state(s2), "S", 0, SIdx(2), vec!["$", "b"]);
let s3 = sg.edge(sg.start_state(), Symbol::Token(grm.token_idx("b").unwrap())).unwrap();
assert_eq!(sg.closed_state(s3).items.len(), 4);
state_exists(&grm, sg.closed_state(s3), "S", 1, SIdx(1), vec!["$", "b", "c"]);
state_exists(&grm, sg.closed_state(s3), "A", 0, SIdx(0), vec!["a"]);
state_exists(&grm, sg.closed_state(s3), "A", 1, SIdx(0), vec!["a"]);
state_exists(&grm, sg.closed_state(s3), "A", 2, SIdx(0), vec!["a"]);
let s4 = sg.edge(s3, Symbol::Rule(grm.rule_idx("A").unwrap())).unwrap();
assert_eq!(sg.closed_state(s4).items.len(), 1);
state_exists(&grm, sg.closed_state(s4), "S", 1, SIdx(2), vec!["$", "b", "c"]);
let s5 = sg.edge(s4, Symbol::Token(grm.token_idx("a").unwrap())).unwrap();
assert_eq!(sg.closed_state(s5).items.len(), 1);
state_exists(&grm, sg.closed_state(s5), "S", 1, SIdx(3), vec!["$", "b", "c"]);
let s6 = sg.edge(s3, Symbol::Token(grm.token_idx("a").unwrap())).unwrap();
assert_eq!(s3, sg.edge(s6, Symbol::Token(grm.token_idx("b").unwrap())).unwrap());
assert_eq!(sg.closed_state(s6).items.len(), 5);
state_exists(&grm, sg.closed_state(s6), "A", 0, SIdx(1), vec!["a"]);
state_exists(&grm, sg.closed_state(s6), "A", 1, SIdx(1), vec!["a"]);
state_exists(&grm, sg.closed_state(s6), "A", 2, SIdx(1), vec!["a"]);
state_exists(&grm, sg.closed_state(s6), "S", 0, SIdx(0), vec!["b", "c"]);
state_exists(&grm, sg.closed_state(s6), "S", 1, SIdx(0), vec!["b", "c"]);
let s7 = sg.edge(s6, Symbol::Rule(grm.rule_idx("S").unwrap())).unwrap();
assert_eq!(sg.closed_state(s7).items.len(), 3);
state_exists(&grm, sg.closed_state(s7), "A", 0, SIdx(2), vec!["a"]);
state_exists(&grm, sg.closed_state(s7), "A", 2, SIdx(2), vec!["a"]);
state_exists(&grm, sg.closed_state(s7), "S", 0, SIdx(1), vec!["b", "c"]);
let s8 = sg.edge(s7, Symbol::Token(grm.token_idx("c").unwrap())).unwrap();
assert_eq!(sg.closed_state(s8).items.len(), 1);
state_exists(&grm, sg.closed_state(s8), "A", 0, SIdx(3), vec!["a"]);
let s9 = sg.edge(s7, Symbol::Token(grm.token_idx("b").unwrap())).unwrap();
assert_eq!(sg.closed_state(s9).items.len(), 2);
state_exists(&grm, sg.closed_state(s9), "A", 2, SIdx(3), vec!["a"]);
state_exists(&grm, sg.closed_state(s9), "S", 0, SIdx(2), vec!["b", "c"]);
}
fn grammar_pager() -> YaccGrammar {
YaccGrammar::new(
YaccKind::Original(YaccOriginalActionKind::GenericParseTree),
"
%start X
%%
X : 'a' Y 'd' | 'a' Z 'c' | 'a' T | 'b' Y 'e' | 'b' Z 'd' | 'b' T;
Y : 't' W | 'u' X;
Z : 't' 'u';
T : 'u' X 'a';
W : 'u' V;
V : ;
",
)
.unwrap()
}
#[rustfmt::skip]
fn test_pager_graph(grm: &YaccGrammar) {
let sg = pager_stategraph(grm);
assert_eq!(sg.all_states_len(), StIdx(23));
assert_eq!(sg.all_edges_len(), 27);
assert_eq!(sg.closed_state(sg.start_state()).items.len(), 7);
state_exists(grm, sg.closed_state(sg.start_state()), "^", 0, SIdx(0), vec!["$"]);
state_exists(grm, sg.closed_state(sg.start_state()), "X", 0, SIdx(0), vec!["$"]);
state_exists(grm, sg.closed_state(sg.start_state()), "X", 1, SIdx(0), vec!["$"]);
state_exists(grm, sg.closed_state(sg.start_state()), "X", 2, SIdx(0), vec!["$"]);
state_exists(grm, sg.closed_state(sg.start_state()), "X", 3, SIdx(0), vec!["$"]);
state_exists(grm, sg.closed_state(sg.start_state()), "X", 4, SIdx(0), vec!["$"]);
state_exists(grm, sg.closed_state(sg.start_state()), "X", 5, SIdx(0), vec!["$"]);
let s1 = sg.edge(sg.start_state(), Symbol::Token(grm.token_idx("a").unwrap())).unwrap();
assert_eq!(sg.closed_state(s1).items.len(), 7);
state_exists(grm, sg.closed_state(s1), "X", 0, SIdx(1), vec!["a", "d", "e", "$"]);
state_exists(grm, sg.closed_state(s1), "X", 1, SIdx(1), vec!["a", "d", "e", "$"]);
state_exists(grm, sg.closed_state(s1), "X", 2, SIdx(1), vec!["a", "d", "e", "$"]);
state_exists(grm, sg.closed_state(s1), "Y", 0, SIdx(0), vec!["d"]);
state_exists(grm, sg.closed_state(s1), "Y", 1, SIdx(0), vec!["d"]);
state_exists(grm, sg.closed_state(s1), "Z", 0, SIdx(0), vec!["c"]);
state_exists(grm, sg.closed_state(s1), "T", 0, SIdx(0), vec!["a", "d", "e", "$"]);
let s7 = sg.edge(sg.start_state(), Symbol::Token(grm.token_idx("b").unwrap())).unwrap();
assert_eq!(sg.closed_state(s7).items.len(), 7);
state_exists(grm, sg.closed_state(s7), "X", 3, SIdx(1), vec!["a", "d", "e", "$"]);
state_exists(grm, sg.closed_state(s7), "X", 4, SIdx(1), vec!["a", "d", "e", "$"]);
state_exists(grm, sg.closed_state(s7), "X", 5, SIdx(1), vec!["a", "d", "e", "$"]);
state_exists(grm, sg.closed_state(s1), "Y", 0, SIdx(0), vec!["d"]);
state_exists(grm, sg.closed_state(s1), "Y", 1, SIdx(0), vec!["d"]);
state_exists(grm, sg.closed_state(s1), "Z", 0, SIdx(0), vec!["c"]);
state_exists(grm, sg.closed_state(s1), "T", 0, SIdx(0), vec!["a", "d", "e", "$"]);
let s4 = sg.edge(s1, Symbol::Token(grm.token_idx("u").unwrap())).unwrap();
assert_eq!(sg.closed_state(s4).items.len(), 8);
assert_eq!(s4, sg.edge(s7, Symbol::Token(grm.token_idx("u").unwrap())).unwrap());
state_exists(grm, sg.closed_state(s4), "Y", 1, SIdx(1), vec!["d", "e"]);
state_exists(grm, sg.closed_state(s4), "T", 0, SIdx(1), vec!["a", "d", "e", "$"]);
state_exists(grm, sg.closed_state(s4), "X", 0, SIdx(0), vec!["a", "d", "e"]);
state_exists(grm, sg.closed_state(s4), "X", 1, SIdx(0), vec!["a", "d", "e"]);
state_exists(grm, sg.closed_state(s4), "X", 2, SIdx(0), vec!["a", "d", "e"]);
state_exists(grm, sg.closed_state(s4), "X", 3, SIdx(0), vec!["a", "d", "e"]);
state_exists(grm, sg.closed_state(s4), "X", 4, SIdx(0), vec!["a", "d", "e"]);
state_exists(grm, sg.closed_state(s4), "X", 5, SIdx(0), vec!["a", "d", "e"]);
assert_eq!(s1, sg.edge(s4, Symbol::Token(grm.token_idx("a").unwrap())).unwrap());
assert_eq!(s7, sg.edge(s4, Symbol::Token(grm.token_idx("b").unwrap())).unwrap());
let s2 = sg.edge(s1, Symbol::Token(grm.token_idx("t").unwrap())).unwrap();
assert_eq!(sg.closed_state(s2).items.len(), 3);
state_exists(grm, sg.closed_state(s2), "Y", 0, SIdx(1), vec!["d"]);
state_exists(grm, sg.closed_state(s2), "Z", 0, SIdx(1), vec!["c"]);
state_exists(grm, sg.closed_state(s2), "W", 0, SIdx(0), vec!["d"]);
let s3 = sg.edge(s2, Symbol::Token(grm.token_idx("u").unwrap())).unwrap();
assert_eq!(sg.closed_state(s3).items.len(), 3);
state_exists(grm, sg.closed_state(s3), "Z", 0, SIdx(2), vec!["c"]);
state_exists(grm, sg.closed_state(s3), "W", 0, SIdx(1), vec!["d"]);
state_exists(grm, sg.closed_state(s3), "V", 0, SIdx(0), vec!["d"]);
let s5 = sg.edge(s4, Symbol::Rule(grm.rule_idx("X").unwrap())).unwrap();
assert_eq!(sg.closed_state(s5).items.len(), 2);
state_exists(grm, sg.closed_state(s5), "Y", 1, SIdx(2), vec!["d", "e"]);
state_exists(grm, sg.closed_state(s5), "T", 0, SIdx(2), vec!["a", "d", "e", "$"]);
let s6 = sg.edge(s5, Symbol::Token(grm.token_idx("a").unwrap())).unwrap();
assert_eq!(sg.closed_state(s6).items.len(), 1);
state_exists(grm, sg.closed_state(s6), "T", 0, SIdx(3), vec!["a", "d", "e", "$"]);
let s8 = sg.edge(s7, Symbol::Token(grm.token_idx("t").unwrap())).unwrap();
assert_eq!(sg.closed_state(s8).items.len(), 3);
state_exists(grm, sg.closed_state(s8), "Y", 0, SIdx(1), vec!["e"]);
state_exists(grm, sg.closed_state(s8), "Z", 0, SIdx(1), vec!["d"]);
state_exists(grm, sg.closed_state(s8), "W", 0, SIdx(0), vec!["e"]);
let s9 = sg.edge(s8, Symbol::Token(grm.token_idx("u").unwrap())).unwrap();
assert_eq!(sg.closed_state(s9).items.len(), 3);
state_exists(grm, sg.closed_state(s9), "Z", 0, SIdx(2), vec!["d"]);
state_exists(grm, sg.closed_state(s9), "W", 0, SIdx(1), vec!["e"]);
state_exists(grm, sg.closed_state(s3), "V", 0, SIdx(0), vec!["d"]);
let s0x = sg.edge(sg.start_state(), Symbol::Rule(grm.rule_idx("X").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s0x), "^", 0, SIdx(1), vec!["$"]);
let s1y = sg.edge(s1, Symbol::Rule(grm.rule_idx("Y").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s1y), "X", 0, SIdx(2), vec!["a", "d", "e", "$"]);
let s1yd = sg.edge(s1y, Symbol::Token(grm.token_idx("d").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s1yd), "X", 0, SIdx(3), vec!["a", "d", "e", "$"]);
let s1z = sg.edge(s1, Symbol::Rule(grm.rule_idx("Z").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s1z), "X", 1, SIdx(2), vec!["a", "d", "e", "$"]);
let s1zc = sg.edge(s1z, Symbol::Token(grm.token_idx("c").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s1zc), "X", 1, SIdx(3), vec!["a", "d", "e", "$"]);
let s1t = sg.edge(s1, Symbol::Rule(grm.rule_idx("T").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s1t), "X", 2, SIdx(2), vec!["a", "d", "e", "$"]);
let s7y = sg.edge(s7, Symbol::Rule(grm.rule_idx("Y").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s7y), "X", 3, SIdx(2), vec!["a", "d", "e", "$"]);
let s7ye = sg.edge(s7y, Symbol::Token(grm.token_idx("e").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s7ye), "X", 3, SIdx(3), vec!["a", "d", "e", "$"]);
let s7z = sg.edge(s7, Symbol::Rule(grm.rule_idx("Z").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s7z), "X", 4, SIdx(2), vec!["a", "d", "e", "$"]);
let s7zd = sg.edge(s7z, Symbol::Token(grm.token_idx("d").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s7zd), "X", 4, SIdx(3), vec!["a", "d", "e", "$"]);
let s7t = sg.edge(s7, Symbol::Rule(grm.rule_idx("T").unwrap())).unwrap();
state_exists(grm, sg.closed_state(s7t), "X", 5, SIdx(2), vec!["a", "d", "e", "$"]);
let s8w = sg.edge(s8, Symbol::Rule(grm.rule_idx("W").unwrap())).unwrap();
assert_eq!(s8w, sg.edge(s2, Symbol::Rule(grm.rule_idx("W").unwrap())).unwrap());
state_exists(grm, sg.closed_state(s8w), "Y", 0, SIdx(2), vec!["d", "e"]);
let s9v = sg.edge(s9, Symbol::Rule(grm.rule_idx("V").unwrap())).unwrap();
assert_eq!(s9v, sg.edge(s3, Symbol::Rule(grm.rule_idx("V").unwrap())).unwrap());
state_exists(grm, sg.closed_state(s9v), "W", 0, SIdx(2), vec!["d", "e"]);
}
#[test]
fn test_pager_graph_and_gc() {
let grm = grammar_pager();
for _ in 0..750 {
test_pager_graph(&grm);
}
}
#[test]
#[rustfmt::skip]
fn test_pager_graph_core_states() {
let grm = grammar_pager();
let sg = pager_stategraph(&grm);
assert_eq!(sg.core_state(sg.start_state()).items.len(), 1);
state_exists(&grm, sg.core_state(sg.start_state()), "^", 0, SIdx(0), vec!["$"]);
}
}