Loading grammars_cfg.py +18 −8 Original line number Diff line number Diff line Loading @@ -33,6 +33,15 @@ class GeneratesResult: def __bool__(self) -> bool: return self.value # for the sake of assertions/pytest def __repr__(self) -> str: info = "" if self.cyk_table is not None: inc = "∈" if self.value else "∉" info = f" ({self.cnf_cfg.init.name} {inc} " \ f"{set(x.name for x in self.cyk_table[-1][0])})" return f"{self.value}{info}" class InfinityType: pass Loading Loading @@ -292,14 +301,15 @@ class CFG: simple_to: Dict[Nonterminal, Set[Nonterminal]] =\ {n: {n} for n in self.nonterminals} added = True while added: added = False for src, prod in self.productions(): if len(prod) == 1 and prod[0] in self.nonterminals \ and prod[0] not in simple_to[src]: added = True simple_to[src].add(typing.cast(Nonterminal, prod[0])) for tracker in ChangeTracker(): for src_orig in self.nonterminals: # make a copy to avoid error for changing during iteraton for src in list(simple_to[src_orig]): for prod in self.rules.get(src, []): if len(prod) == 1 and isinstance(prod[0], Nonterminal)\ and prod[0] not in simple_to[src_orig]: tracker.changed() simple_to[src_orig].add(prod[0]) new_rules: CFG.Rules = dict() for src in self.nonterminals: Loading test_cfl_cfg.py +34 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,23 @@ def test_generates(): go(g2) def test_remove_simple() -> None: g = cfl.CFG({S, A, B, C}, {a}, {S: {(A,)}, A: {(B, C), (B,), (C,)}, B: {(a, B), (a,)}, C: {(a, a, C), (a, a)}}, S) print(g) gs = g.remove_simple_rules() print(gs) assert (B, C) in gs.rules[gs.init] assert (a, B) in gs.rules[gs.init] assert (a,) in gs.rules[gs.init] assert (a, a, C) in gs.rules[gs.init] assert (a, a) in gs.rules[gs.init] def test_remove_eps(): g = g2.epsilon_normal_form() assert g.init == g1.init Loading @@ -59,6 +76,23 @@ def test_remove_eps(): assert g.nonterminals == g1.nonterminals assert g.rules == g1.rules g = cfl.CFG({S, A, B}, {a, b}, {S: {(A, B)}, A: {(a, A), ()}, B: {(b, B), ()}}, S) ge = g.epsilon_normal_form() print(ge.proper()) assert ge.init != S assert () in ge.rules[ge.init] assert ge.generates("") print(ge.generates("aa").cnf_cfg) assert ge.generates("aa") assert ge.generates("bb") assert ge.generates("ab") assert not ge.generates("aba") assert not ge.generates("ba") def test_equal(): R = Nonterminal("R") Loading Loading
grammars_cfg.py +18 −8 Original line number Diff line number Diff line Loading @@ -33,6 +33,15 @@ class GeneratesResult: def __bool__(self) -> bool: return self.value # for the sake of assertions/pytest def __repr__(self) -> str: info = "" if self.cyk_table is not None: inc = "∈" if self.value else "∉" info = f" ({self.cnf_cfg.init.name} {inc} " \ f"{set(x.name for x in self.cyk_table[-1][0])})" return f"{self.value}{info}" class InfinityType: pass Loading Loading @@ -292,14 +301,15 @@ class CFG: simple_to: Dict[Nonterminal, Set[Nonterminal]] =\ {n: {n} for n in self.nonterminals} added = True while added: added = False for src, prod in self.productions(): if len(prod) == 1 and prod[0] in self.nonterminals \ and prod[0] not in simple_to[src]: added = True simple_to[src].add(typing.cast(Nonterminal, prod[0])) for tracker in ChangeTracker(): for src_orig in self.nonterminals: # make a copy to avoid error for changing during iteraton for src in list(simple_to[src_orig]): for prod in self.rules.get(src, []): if len(prod) == 1 and isinstance(prod[0], Nonterminal)\ and prod[0] not in simple_to[src_orig]: tracker.changed() simple_to[src_orig].add(prod[0]) new_rules: CFG.Rules = dict() for src in self.nonterminals: Loading
test_cfl_cfg.py +34 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,23 @@ def test_generates(): go(g2) def test_remove_simple() -> None: g = cfl.CFG({S, A, B, C}, {a}, {S: {(A,)}, A: {(B, C), (B,), (C,)}, B: {(a, B), (a,)}, C: {(a, a, C), (a, a)}}, S) print(g) gs = g.remove_simple_rules() print(gs) assert (B, C) in gs.rules[gs.init] assert (a, B) in gs.rules[gs.init] assert (a,) in gs.rules[gs.init] assert (a, a, C) in gs.rules[gs.init] assert (a, a) in gs.rules[gs.init] def test_remove_eps(): g = g2.epsilon_normal_form() assert g.init == g1.init Loading @@ -59,6 +76,23 @@ def test_remove_eps(): assert g.nonterminals == g1.nonterminals assert g.rules == g1.rules g = cfl.CFG({S, A, B}, {a, b}, {S: {(A, B)}, A: {(a, A), ()}, B: {(b, B), ()}}, S) ge = g.epsilon_normal_form() print(ge.proper()) assert ge.init != S assert () in ge.rules[ge.init] assert ge.generates("") print(ge.generates("aa").cnf_cfg) assert ge.generates("aa") assert ge.generates("bb") assert ge.generates("ab") assert not ge.generates("aba") assert not ge.generates("ba") def test_equal(): R = Nonterminal("R") Loading