Skip to content

Commit

Permalink
correct fcfg production properties and adding
Browse files Browse the repository at this point in the history
  • Loading branch information
bygu4 committed Dec 29, 2024
1 parent 0b26bfb commit 5b5007d
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 21 deletions.
54 changes: 34 additions & 20 deletions pyformlang/fcfg/fcfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,26 @@ class FCFG(CFG):
"""

def __init__(self,
variables: AbstractSet[Variable] = None,
terminals: AbstractSet[Terminal] = None,
start_symbol: Variable = None,
productions: Iterable[FeatureProduction] = None) -> None:
variables: AbstractSet[Hashable] = None,
terminals: AbstractSet[Hashable] = None,
start_symbol: Hashable = None,
productions: Iterable[Production] = None) -> None:
super().__init__(variables, terminals, start_symbol, productions)
self._productions: Set[FeatureProduction]

def __predictor(self,
state: State,
chart: List[List[State]],
processed: StateProcessed) -> None:
# We have an incomplete state and the next token is a variable
# We must ask to process the variable with another rule
end_idx = state.positions[1]
next_var = state.production.body[state.positions[2]]
for production in self._productions:
if production.head == next_var:
new_state = State(production,
(end_idx, end_idx, 0),
production.features,
ParseTree(production.head))
if processed.add(end_idx, new_state):
chart[end_idx].append(new_state)
@property
def feature_productions(self) -> Set[FeatureProduction]:
""" Gets the feature productions of the grammar """
return self._productions

def add_production(self, production: Production) -> None:
""" Adds given production to the grammar """
if not isinstance(production, FeatureProduction):
production = FeatureProduction(production.head,
production.body,
FeatureStructure(),
[FeatureStructure()])
super().add_production(production)

def contains(self, word: Iterable[Hashable]) -> bool:
""" Gives the membership of a word to the grammar
Expand Down Expand Up @@ -212,6 +209,23 @@ def _read_line(cls,
production = FeatureProduction(head, body, head_fs, all_body_fs)
productions.add(production)

def __predictor(self,
state: State,
chart: List[List[State]],
processed: StateProcessed) -> None:
# We have an incomplete state and the next token is a variable
# We must ask to process the variable with another rule
end_idx = state.positions[1]
next_var = state.production.body[state.positions[2]]
for production in self._productions:
if production.head == next_var:
new_state = State(production,
(end_idx, end_idx, 0),
production.features,
ParseTree(production.head))
if processed.add(end_idx, new_state):
chart[end_idx].append(new_state)


def _split_text_conditions(head_text: str) -> Tuple[str, str]:
if head_text[-1] != "]":
Expand Down
52 changes: 51 additions & 1 deletion pyformlang/fcfg/tests/test_fcfg.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Test a FCFG"""

from pyformlang.cfg import Variable, Terminal
from pyformlang.cfg import Variable, Terminal, Production
from pyformlang.cfg import DerivationDoesNotExist
from pyformlang.cfg.parse_tree import ParseTree
from pyformlang.cfg.llone_parser import NotParsableException
from pyformlang.fcfg.fcfg import FCFG
Expand Down Expand Up @@ -32,6 +33,30 @@ def fcfg_text() -> str:
class TestFCFG:
"""Test a FCFG"""

def test_creation(self):
""" Tests creation of FCFG """
variable0 = Variable(0)
terminal0 = Terminal("a")
prod0 = Production(variable0, [terminal0, Terminal("A"), Variable(1)])
fcfg = FCFG({variable0}, {terminal0}, variable0, {prod0})
assert fcfg is not None
assert len(fcfg.variables) == 2
assert len(fcfg.terminals) == 2
assert len(fcfg.productions) == 1
assert len(fcfg.feature_productions) == 1
assert fcfg.productions == fcfg.feature_productions
assert fcfg.is_empty()
assert all(isinstance(prod, FeatureProduction)
for prod in fcfg.productions)

fcfg = FCFG()
assert fcfg is not None
assert len(fcfg.variables) == 0
assert len(fcfg.terminals) == 0
assert len(fcfg.productions) == 0
assert len(fcfg.feature_productions) == 0
assert fcfg.is_empty()

def test_contains(self):
"""Test containment"""
# 1st: S -> NP VP
Expand Down Expand Up @@ -231,3 +256,28 @@ def test_copy(self, fcfg_text: str):
assert fcfg.productions == fcfg_copy.productions
assert fcfg.start_symbol == fcfg_copy.start_symbol
assert fcfg is not fcfg_copy

def test_get_leftmost_derivation(self):
ter_a = Terminal("a")
ter_b = Terminal("b")
var_s = Variable("S")
var_a = Variable("A")
var_b = Variable("B")
var_c = Variable("C")
productions = [Production(var_s, [var_c, var_b]),
Production(var_c, [var_a, var_a]),
Production(var_a, [ter_a]),
Production(var_b, [ter_b])
]
fcfg = FCFG(productions=productions, start_symbol=var_s)
parse_tree = fcfg.get_cnf_parse_tree([ter_a, ter_a, ter_b])
derivation = parse_tree.get_leftmost_derivation()
assert derivation == \
[[var_s],
[var_c, var_b],
[var_a, var_a, var_b],
[ter_a, var_a, var_b],
[ter_a, ter_a, var_b],
[ter_a, ter_a, ter_b]]
with pytest.raises(DerivationDoesNotExist):
fcfg.get_cnf_parse_tree([])

0 comments on commit 5b5007d

Please sign in to comment.