Verified Commit 4ba5984f authored by Vladimír Štill's avatar Vladimír Štill
Browse files

CFL: Fix removal of simple rules & more tests

parent dffa3596
Loading
Loading
Loading
Loading
+18 −8
Original line number Diff line number Diff line
@@ -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
@@ -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:
+34 −0
Original line number Diff line number Diff line
@@ -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
@@ -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")