2
2
Representation of a recursive automaton
3
3
"""
4
4
5
- from typing import AbstractSet
5
+ from typing import AbstractSet , Union
6
6
7
7
from pyformlang .finite_automaton .finite_automaton import to_symbol
8
8
from pyformlang .finite_automaton .symbol import Symbol
@@ -19,143 +19,121 @@ class RecursiveAutomaton:
19
19
20
20
Parameters
21
21
----------
22
- labels : set of :class:`~pyformlang.finite_automaton.Symbol`, optional
23
- A finite set of labels for boxes
24
- initial_label : :class:`~pyformlang.finite_automaton.Symbol`, optional
25
- A start label for automaton
26
- boxes : set of :class:`~pyformlang.rsa.Box`, optional
22
+ start_box : :class:`~pyformlang.rsa.Box`
23
+ Start box
24
+ boxes : set of :class:`~pyformlang.rsa.Box`
27
25
A finite set of boxes
28
26
29
27
"""
30
28
31
29
def __init__ (self ,
32
- labels : AbstractSet [Symbol ] = None ,
33
- initial_label : Symbol = None ,
34
- boxes : AbstractSet [Box ] = None ):
35
-
36
- if labels is not None :
37
- labels = {to_symbol (x ) for x in labels }
38
- self ._labels = labels or set ()
39
-
40
- if initial_label is not None :
41
- initial_label = to_symbol (initial_label )
42
- if initial_label not in self ._labels :
43
- self ._labels .add (initial_label )
44
- self ._initial_label = initial_label or Symbol ("" )
45
-
46
- self ._boxes = {}
47
- if boxes is not None :
48
- for box in boxes :
49
- self ._boxes .update ({to_symbol (box .label ): box })
50
- self ._labels .add (box .label )
51
-
52
- for label in self ._labels :
53
- box = self .get_box (label )
54
- if box is None :
55
- raise ValueError (
56
- "RSA must have the same number of labels and DFAs" )
57
-
58
- def get_box (self , label : Symbol ):
59
- """ Box by label """
60
-
61
- label = to_symbol (label )
62
- if label in self ._boxes :
63
- return self ._boxes [label ]
64
-
65
- return None
66
-
67
- def add_box (self , new_box : Box ):
68
- """ Set a box
30
+ start_box : Box ,
31
+ boxes : AbstractSet [Box ]):
32
+ self ._nonterminal_to_box = {}
33
+ if start_box not in boxes :
34
+ self ._nonterminal_to_box [to_symbol (start_box .nonterminal )] = start_box
35
+ self ._start_nonterminal = to_symbol (start_box .nonterminal )
36
+ for box in boxes :
37
+ self ._nonterminal_to_box [to_symbol (box .nonterminal )] = box
38
+
39
+ def get_box_by_nonterminal (self , nonterminal : Union [Symbol , str ]):
40
+ """
41
+ Box by nonterminal
69
42
70
43
Parameters
71
- -----------
72
- new_box : :class:`~pyformlang.rsa.Box`
73
- The new box
44
+ ----------
45
+ nonterminal : :class:`~pyformlang.finite_automaton.Symbol` | str
46
+ the nonterminal of which represents a box
74
47
48
+ Returns
49
+ -----------
50
+ box : :class:`~pyformlang.rsa.Box` | None
51
+ box represented by given nonterminal
75
52
"""
76
53
77
- self ._boxes .update ({new_box .label : new_box })
78
- self ._labels .add (to_symbol (new_box .label ))
54
+ nonterminal = to_symbol (nonterminal )
55
+ if nonterminal in self ._nonterminal_to_box :
56
+ return self ._nonterminal_to_box [nonterminal ]
79
57
80
- def get_number_of_boxes (self ):
81
- """ Size of set of boxes """
58
+ return None
82
59
83
- return len (self ._boxes )
60
+ def get_number_boxes (self ):
61
+ """ Size of set of boxes """
84
62
85
- def change_initial_label (self , new_initial_label : Symbol ):
86
- """ Set an initial label
63
+ return len (self ._nonterminal_to_box )
87
64
88
- Parameters
89
- -----------
90
- new_initial_label : :class:`~pyformlang.finite_automaton.Symbol`
91
- The new initial label
92
-
93
- """
94
-
95
- new_initial_label = to_symbol (new_initial_label )
96
- if new_initial_label not in self ._labels :
97
- raise ValueError (
98
- "New initial label not in set of labels for boxes" )
65
+ def to_dot (self ):
66
+ """ Create dot representation of recursive automaton """
67
+ dot_string = 'digraph "" {'
68
+ for box in self ._nonterminal_to_box .values ():
69
+ dot_string += f'\n { box .to_subgraph_dot ()} '
70
+ dot_string += "\n }"
71
+ return dot_string
99
72
100
73
@property
101
- def labels (self ) -> set :
102
- """ The set of labels """
74
+ def nonterminals (self ) -> set :
75
+ """ The set of nonterminals """
103
76
104
- return self ._labels
77
+ return set ( self ._nonterminal_to_box . keys ())
105
78
106
79
@property
107
80
def boxes (self ) -> dict :
108
81
""" The set of boxes """
109
82
110
- return self ._boxes
83
+ return self ._nonterminal_to_box
84
+
85
+ @property
86
+ def start_nonterminal (self ) -> Symbol :
87
+ """ The start nonterminal """
88
+
89
+ return self ._start_nonterminal
111
90
112
91
@property
113
- def initial_label (self ) -> Symbol :
114
- """ The initial label """
92
+ def start_box (self ):
93
+ """ The start box """
115
94
116
- return self ._initial_label
95
+ return self .boxes [ self . start_nonterminal ]
117
96
118
97
@classmethod
119
- def from_regex (cls , regex : Regex , initial_label : Symbol ):
98
+ def from_regex (cls , regex : Regex , start_nonterminal : Union [ Symbol , str ] ):
120
99
""" Create a recursive automaton from regular expression
121
100
122
101
Parameters
123
102
-----------
124
103
regex : :class:`~pyformlang.regular_expression.Regex`
125
104
The regular expression
126
- initial_label : :class:`~pyformlang.finite_automaton.Symbol`
127
- The initial label for the recursive automaton
105
+ start_nonterminal : :class:`~pyformlang.finite_automaton.Symbol` | str
106
+ The start nonterminal for the recursive automaton
128
107
129
108
Returns
130
109
-----------
131
110
rsa : :class:`~pyformlang.rsa.RecursiveAutomaton`
132
111
The new recursive automaton built from regular expression
133
112
"""
134
-
135
- initial_label = to_symbol (initial_label )
136
- box = Box (regex .to_epsilon_nfa ().minimize (), initial_label )
137
- return RecursiveAutomaton ({initial_label }, initial_label , {box })
113
+ start_nonterminal = to_symbol (start_nonterminal )
114
+ box = Box (regex .to_epsilon_nfa ().minimize (), start_nonterminal )
115
+ return RecursiveAutomaton (box , {box })
138
116
139
117
@classmethod
140
- def from_text (cls , text , start_symbol : Symbol = Symbol ("S" )):
141
- """ Create a recursive automaton from text
118
+ def from_ebnf (cls , text , start_nonterminal : Union [ Symbol , str ] = Symbol ("S" )):
119
+ """ Create a recursive automaton from ebnf (ebnf = Extended Backus-Naur Form)
142
120
143
121
Parameters
144
122
-----------
145
123
text : str
146
124
The text of transform
147
- start_symbol : str, optional
148
- The start symbol , S by default
125
+ start_nonterminal : :class:`~pyformlang.finite_automaton.Symbol` | str, optional
126
+ The start nonterminal , S by default
149
127
150
128
Returns
151
129
-----------
152
130
rsa : :class:`~pyformlang.rsa.RecursiveAutomaton`
153
131
The new recursive automaton built from context-free grammar
154
132
"""
155
-
133
+ start_nonterminal = to_symbol ( start_nonterminal )
156
134
productions = {}
157
135
boxes = set ()
158
- labels = set ()
136
+ nonterminals = set ()
159
137
for production in text .splitlines ():
160
138
production = production .strip ()
161
139
if "->" not in production :
@@ -164,7 +142,7 @@ def from_text(cls, text, start_symbol: Symbol = Symbol("S")):
164
142
head , body = production .split ("->" )
165
143
head = head .strip ()
166
144
body = body .strip ()
167
- labels .add (to_symbol (head ))
145
+ nonterminals .add (to_symbol (head ))
168
146
169
147
if body == "" :
170
148
body = Epsilon ().to_text ()
@@ -177,11 +155,13 @@ def from_text(cls, text, start_symbol: Symbol = Symbol("S")):
177
155
for head , body in productions .items ():
178
156
boxes .add (Box (Regex (body ).to_epsilon_nfa ().minimize (),
179
157
to_symbol (head )))
158
+ start_box = Box (Regex (productions [start_nonterminal .value ]).to_epsilon_nfa ().minimize (), start_nonterminal )
159
+ return RecursiveAutomaton (start_box , boxes )
180
160
181
- return RecursiveAutomaton ( labels , start_symbol , boxes )
182
-
183
- def is_equivalent_to ( self , other ):
184
- """ Check whether two recursive automata are equivalent
161
+ def is_equals_to ( self , other ):
162
+ """
163
+ Check whether two recursive automata are equals by boxes.
164
+ Not equivalency in terms of formal languages theory, just mapping boxes
185
165
186
166
Parameters
187
167
----------
@@ -191,23 +171,11 @@ def is_equivalent_to(self, other):
191
171
Returns
192
172
----------
193
173
are_equivalent : bool
194
- Whether the two recursive automata are equivalent or not
174
+ Whether the two recursive automata are equals or not
195
175
"""
196
-
197
176
if not isinstance (other , RecursiveAutomaton ):
198
177
return False
199
-
200
- if self ._labels != other ._labels :
201
- return False
202
-
203
- for label in self ._labels :
204
- box_1 = self .get_box (label )
205
- box_2 = other .get_box (label )
206
-
207
- if box_1 != box_2 :
208
- return False
209
-
210
- return True
178
+ return self .boxes == other .boxes
211
179
212
180
def __eq__ (self , other ):
213
- return self .is_equivalent_to (other )
181
+ return self .is_equals_to (other )
0 commit comments