Skip to content

Commit 05835f4

Browse files
authored
Merge branch 'rework_cfg' into rework_indexed_grammars
2 parents c90888b + de224bc commit 05835f4

File tree

2 files changed

+82
-18
lines changed

2 files changed

+82
-18
lines changed

pyformlang/fcfg/fcfg.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,26 +62,23 @@ def __init__(self,
6262
variables: AbstractSet[Hashable] = None,
6363
terminals: AbstractSet[Hashable] = None,
6464
start_symbol: Hashable = None,
65-
productions: Iterable[FeatureProduction] = None) -> None:
65+
productions: Iterable[Production] = None) -> None:
6666
super().__init__(variables, terminals, start_symbol, productions)
6767
self._productions: Set[FeatureProduction]
6868

69-
def __predictor(self,
70-
state: State,
71-
chart: List[List[State]],
72-
processed: StateProcessed) -> None:
73-
# We have an incomplete state and the next token is a variable
74-
# We must ask to process the variable with another rule
75-
end_idx = state.positions[1]
76-
next_var = state.production.body[state.positions[2]]
77-
for production in self._productions:
78-
if production.head == next_var:
79-
new_state = State(production,
80-
(end_idx, end_idx, 0),
81-
production.features,
82-
ParseTree(production.head))
83-
if processed.add(end_idx, new_state):
84-
chart[end_idx].append(new_state)
69+
@property
70+
def feature_productions(self) -> Set[FeatureProduction]:
71+
""" Gets the feature productions of the grammar """
72+
return self._productions
73+
74+
def add_production(self, production: Production) -> None:
75+
""" Adds given production to the grammar """
76+
if not isinstance(production, FeatureProduction):
77+
production = FeatureProduction(production.head,
78+
production.body,
79+
FeatureStructure(),
80+
[FeatureStructure()])
81+
super().add_production(production)
8582

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

212+
def __predictor(self,
213+
state: State,
214+
chart: List[List[State]],
215+
processed: StateProcessed) -> None:
216+
# We have an incomplete state and the next token is a variable
217+
# We must ask to process the variable with another rule
218+
end_idx = state.positions[1]
219+
next_var = state.production.body[state.positions[2]]
220+
for production in self._productions:
221+
if production.head == next_var:
222+
new_state = State(production,
223+
(end_idx, end_idx, 0),
224+
production.features,
225+
ParseTree(production.head))
226+
if processed.add(end_idx, new_state):
227+
chart[end_idx].append(new_state)
228+
215229

216230
def _split_text_conditions(head_text: str) -> Tuple[str, str]:
217231
if head_text[-1] != "]":

pyformlang/fcfg/tests/test_fcfg.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Test a FCFG"""
22

3-
from pyformlang.cfg import Variable, Terminal
3+
from pyformlang.cfg import Variable, Terminal, Production
4+
from pyformlang.cfg import DerivationDoesNotExist
45
from pyformlang.cfg.parse_tree import ParseTree
56
from pyformlang.cfg.llone_parser import NotParsableException
67
from pyformlang.fcfg.fcfg import FCFG
@@ -32,6 +33,30 @@ def fcfg_text() -> str:
3233
class TestFCFG:
3334
"""Test a FCFG"""
3435

36+
def test_creation(self):
37+
""" Tests creation of FCFG """
38+
variable0 = Variable(0)
39+
terminal0 = Terminal("a")
40+
prod0 = Production(variable0, [terminal0, Terminal("A"), Variable(1)])
41+
fcfg = FCFG({variable0}, {terminal0}, variable0, {prod0})
42+
assert fcfg is not None
43+
assert len(fcfg.variables) == 2
44+
assert len(fcfg.terminals) == 2
45+
assert len(fcfg.productions) == 1
46+
assert len(fcfg.feature_productions) == 1
47+
assert fcfg.productions == fcfg.feature_productions
48+
assert fcfg.is_empty()
49+
assert all(isinstance(prod, FeatureProduction)
50+
for prod in fcfg.productions)
51+
52+
fcfg = FCFG()
53+
assert fcfg is not None
54+
assert len(fcfg.variables) == 0
55+
assert len(fcfg.terminals) == 0
56+
assert len(fcfg.productions) == 0
57+
assert len(fcfg.feature_productions) == 0
58+
assert fcfg.is_empty()
59+
3560
def test_contains(self):
3661
"""Test containment"""
3762
# 1st: S -> NP VP
@@ -231,3 +256,28 @@ def test_copy(self, fcfg_text: str):
231256
assert fcfg.productions == fcfg_copy.productions
232257
assert fcfg.start_symbol == fcfg_copy.start_symbol
233258
assert fcfg is not fcfg_copy
259+
260+
def test_get_leftmost_derivation(self):
261+
ter_a = Terminal("a")
262+
ter_b = Terminal("b")
263+
var_s = Variable("S")
264+
var_a = Variable("A")
265+
var_b = Variable("B")
266+
var_c = Variable("C")
267+
productions = [Production(var_s, [var_c, var_b]),
268+
Production(var_c, [var_a, var_a]),
269+
Production(var_a, [ter_a]),
270+
Production(var_b, [ter_b])
271+
]
272+
fcfg = FCFG(productions=productions, start_symbol=var_s)
273+
parse_tree = fcfg.get_cnf_parse_tree([ter_a, ter_a, ter_b])
274+
derivation = parse_tree.get_leftmost_derivation()
275+
assert derivation == \
276+
[[var_s],
277+
[var_c, var_b],
278+
[var_a, var_a, var_b],
279+
[ter_a, var_a, var_b],
280+
[ter_a, ter_a, var_b],
281+
[ter_a, ter_a, ter_b]]
282+
with pytest.raises(DerivationDoesNotExist):
283+
fcfg.get_cnf_parse_tree([])

0 commit comments

Comments
 (0)