nfa2dfa 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/nfa2dfa +3 -5
- data/lib/automaton.rb +264 -312
- data/lib/nfa2dfa.rb +6 -5
- data/lib/state.rb +72 -67
- data/lib/transition.rb +31 -24
- data/spec/automaton_spec.rb +139 -134
- data/spec/state_spec.rb +100 -101
- data/spec/transition_spec.rb +27 -28
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5c182a3da5e4d525dbcf77af28799db0d406e6e
|
4
|
+
data.tar.gz: fbbc0362690ca01b3bcd2201082eb90dde281e4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad8e97f2998a06dd239b79bc3a43342959e8e34d1ba0320f781cf2ba800927b5472501da8a53799eee7765fd23f5fd189e909186f2b8349eef7b8e9ef64979ae
|
7
|
+
data.tar.gz: 27221054cfc204c2e44bfcccba65cc71702a0b564a51c77f651c1298630ac95276b48f7d54563ab369a37f9f077185a7010d080e020ec50a7a7cb50d660aacf6
|
data/bin/nfa2dfa
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
2
|
require 'rubygems'
|
4
3
|
require 'commander/import'
|
5
4
|
require 'nfa2dfa'
|
6
5
|
program :version, '0.0.1'
|
7
6
|
program :description, 'Controller of automaton'
|
8
7
|
program :name, 'nfa2dfa'
|
9
|
-
|
8
|
+
|
10
9
|
command :create_deterministic do |c|
|
11
10
|
c.syntax = 'nfa2dfa create_finite [options]'
|
12
11
|
c.description = 'Creates determined automaton from file and writes it into "<input_file>.determined"'
|
@@ -15,7 +14,7 @@ command :create_deterministic do |c|
|
|
15
14
|
input_mat = Nfa2Dfa::Automaton.init(arg)
|
16
15
|
output_mat = input_mat.determine
|
17
16
|
data = output_mat.to_str
|
18
|
-
File.open(arg +
|
17
|
+
File.open(arg + '.determined', 'w') { |file| file.write(data) }
|
19
18
|
end
|
20
19
|
end
|
21
20
|
end
|
@@ -26,8 +25,7 @@ command :automaton_to_png do |c|
|
|
26
25
|
c.action do |args|
|
27
26
|
args.each do |arg|
|
28
27
|
input_mat = Nfa2Dfa::Automaton.init(arg)
|
29
|
-
data = input_mat.to_graph(arg +
|
28
|
+
data = input_mat.to_graph(arg + '.png')
|
30
29
|
end
|
31
30
|
end
|
32
31
|
end
|
33
|
-
|
data/lib/automaton.rb
CHANGED
@@ -1,312 +1,264 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
ret_val
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
ret_val
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
arr = arr.uniq.sort
|
266
|
-
last = arr.pop
|
267
|
-
str = ""
|
268
|
-
arr.each do |id|
|
269
|
-
str += id + ","
|
270
|
-
end
|
271
|
-
str += last
|
272
|
-
str
|
273
|
-
end
|
274
|
-
|
275
|
-
def resolve_state_on_stack(char)
|
276
|
-
popped = @stack.pop
|
277
|
-
arr = popped.get_next(char)
|
278
|
-
arr.each do |item|
|
279
|
-
@stack.push(item)
|
280
|
-
end
|
281
|
-
arr.size
|
282
|
-
end
|
283
|
-
|
284
|
-
def initialize(stat_arr, alph_arr, trans_arr, start)
|
285
|
-
@states = stat_arr
|
286
|
-
@alphabet = alph_arr
|
287
|
-
@transitions = trans_arr
|
288
|
-
@starting_state = start
|
289
|
-
end
|
290
|
-
|
291
|
-
def self.validate_states(states, start, final)
|
292
|
-
if states.include? start[0]
|
293
|
-
final.each do |fin|
|
294
|
-
if !(states.include? fin)
|
295
|
-
return false
|
296
|
-
end
|
297
|
-
return true
|
298
|
-
end
|
299
|
-
return false
|
300
|
-
end
|
301
|
-
return false
|
302
|
-
end
|
303
|
-
|
304
|
-
def self.validate_transitions(states, alphabet, transitions)
|
305
|
-
transitions.each do |tr|
|
306
|
-
trans = tr.split('-')
|
307
|
-
((states.include? trans[0]) && (states.include? trans[2]) && (alphabet.include? trans[1])) ? NIL : (return false)
|
308
|
-
end
|
309
|
-
true
|
310
|
-
end
|
311
|
-
end
|
312
|
-
end
|
1
|
+
require 'graphviz'
|
2
|
+
require_relative 'state.rb'
|
3
|
+
require_relative 'transition.rb'
|
4
|
+
# Part of Nfa2Dfa module
|
5
|
+
module Nfa2Dfa
|
6
|
+
# Representation of Automaton
|
7
|
+
class Automaton
|
8
|
+
|
9
|
+
public
|
10
|
+
|
11
|
+
def to_str
|
12
|
+
ret_val = ''
|
13
|
+
str = ''
|
14
|
+
ret_val += item_to_str(@states) + "\n" + item_to_str(@alphabet) +
|
15
|
+
"\n" + item_to_str(@transitions) + "\n"
|
16
|
+
ret_val += @starting_state.id + "\n"
|
17
|
+
@states.each do |state|
|
18
|
+
state.is_final ? str += state.id + ' ' : nil
|
19
|
+
end
|
20
|
+
ret_val += str.byteslice(0, str.length - 1)
|
21
|
+
ret_val
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_graph(path)
|
25
|
+
g = GraphViz.new(:G, :type => :digraph)
|
26
|
+
@states.each do |state|
|
27
|
+
state.to_graph_node(g)
|
28
|
+
end
|
29
|
+
@transitions.each do |trans|
|
30
|
+
trans.to_graph_transition(g)
|
31
|
+
end
|
32
|
+
g.output(:png => path)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.init(path)
|
36
|
+
File.file?(path) ? nil : (return nil)
|
37
|
+
data_arr = []
|
38
|
+
index = 0
|
39
|
+
File.open(path).each_line do |line|
|
40
|
+
data_arr[index] = line
|
41
|
+
index = index + 1
|
42
|
+
end
|
43
|
+
validate(data_arr) ? get_valid_input(data_arr) : (return nil)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.validate(data_arr)
|
47
|
+
parsed = []
|
48
|
+
data_arr.each do |item|
|
49
|
+
parsed.push item.split(' ')
|
50
|
+
end
|
51
|
+
validate_transitions(parsed[0], parsed[1], parsed[2]) &&
|
52
|
+
validate_states(parsed[0], parsed[3], parsed[4])
|
53
|
+
end
|
54
|
+
|
55
|
+
def accepts?(data)
|
56
|
+
formatted_input = data.split(' ')
|
57
|
+
@stack = []
|
58
|
+
@stack.push(@starting_state)
|
59
|
+
if formatted_input.size == 0
|
60
|
+
@starting_state.is_final
|
61
|
+
else
|
62
|
+
recurs_accepts?(formatted_input, 0)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def deterministic?
|
67
|
+
@states.each do |state|
|
68
|
+
@alphabet.each do |char|
|
69
|
+
state.get_next(char).size > 1 ? (return false) : nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def determine
|
76
|
+
deterministic? ? self : determine_prot
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
attr_accessor :states, :alphabet, :transitions, :starting_state, :stack
|
82
|
+
|
83
|
+
def recurs_accepts?(data, index)
|
84
|
+
ret_val = false
|
85
|
+
@stack.size == 0 ? (return false) : nil
|
86
|
+
cnt = resolve_state_on_stack(data[index])
|
87
|
+
index += 1
|
88
|
+
if index == data.size
|
89
|
+
cnt.times { @stack.pop.is_final ? ret_val = true : nil }
|
90
|
+
else
|
91
|
+
return recurs_accepts?(data, index)
|
92
|
+
end
|
93
|
+
ret_val
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.get_valid_input(data_arr)
|
97
|
+
states = []
|
98
|
+
data_arr[0].split(' ').each { |wrd| states.push State.new(wrd) }
|
99
|
+
fin = data_arr[4].split(' ')
|
100
|
+
states.each { |st| fin.include?(st.id) ? st.finalize : NIL }
|
101
|
+
alphabet = []
|
102
|
+
data_arr[1].split(' ').each { |wrd| alphabet.insert(alphabet.size, wrd) }
|
103
|
+
transitions = []
|
104
|
+
data_arr[2].split(' ').each do |wrd|
|
105
|
+
trans = wrd.split('-')
|
106
|
+
state1 = NIL
|
107
|
+
state2 = NIL
|
108
|
+
states.each do |item|
|
109
|
+
trans[0] == item.id ? state1 = item : NIL
|
110
|
+
trans[2] == item.id ? state2 = item : NIL
|
111
|
+
state1 != NIL && state2 != NIL ? break : NIL
|
112
|
+
end
|
113
|
+
transitions.insert(transitions.size, Transition.new(
|
114
|
+
state1, trans[1], state2))
|
115
|
+
state1.add_transition(transitions[transitions.size - 1])
|
116
|
+
end
|
117
|
+
starting = NIL
|
118
|
+
states.each do |item2|
|
119
|
+
if item2.id == data_arr[3].split(' ')[0]
|
120
|
+
starting = item2
|
121
|
+
item2.to_starting_node
|
122
|
+
break
|
123
|
+
end
|
124
|
+
end
|
125
|
+
Automaton.new(states, alphabet, transitions, starting)
|
126
|
+
end
|
127
|
+
|
128
|
+
# main determinization function
|
129
|
+
def determine_prot
|
130
|
+
undetermined_states = []
|
131
|
+
determined_states = []
|
132
|
+
transits = []
|
133
|
+
tot_transits = []
|
134
|
+
curr_states = []
|
135
|
+
undetermined_states[0] = @starting_state.clone
|
136
|
+
new_begin_state = undetermined_states[0]
|
137
|
+
while undetermined_states.size > 0
|
138
|
+
temp_state = undetermined_states[0]
|
139
|
+
curr_states.clear
|
140
|
+
transits.clear
|
141
|
+
@alphabet.each do |char|
|
142
|
+
t_states_by_char = temp_state.get_next(char)
|
143
|
+
if t_states_by_char.size > 0
|
144
|
+
state_id = merge_state_names(t_states_by_char)
|
145
|
+
state = find_state(
|
146
|
+
state_id, determined_states, undetermined_states)
|
147
|
+
if state
|
148
|
+
transits.push(Transition.new(temp_state, char, state))
|
149
|
+
tot_transits.push(Transition.new(temp_state, char, state))
|
150
|
+
else
|
151
|
+
state = State.new(state_id)
|
152
|
+
state.associate_transitions(@transitions)
|
153
|
+
undetermined_states.push(state)
|
154
|
+
transits.push(Transition.new(temp_state, char, state))
|
155
|
+
tot_transits.push(Transition.new(temp_state, char, state))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
temp_state.clear_transitions
|
160
|
+
transits.each do |transit|
|
161
|
+
temp_state.add_transition(transit)
|
162
|
+
end
|
163
|
+
undetermined_states.delete(temp_state)
|
164
|
+
determined_states.push(temp_state)
|
165
|
+
end
|
166
|
+
associate_finals(determined_states)
|
167
|
+
Automaton.new(
|
168
|
+
determined_states, @alphabet.clone, tot_transits, new_begin_state)
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
# Associates finals for determined automaton
|
174
|
+
# from states of nondetermined automaton
|
175
|
+
def associate_finals(stat)
|
176
|
+
ret_val = []
|
177
|
+
@states.each do |orig|
|
178
|
+
if orig.is_final
|
179
|
+
stat.each do |state|
|
180
|
+
state.id.split(',').each do |id|
|
181
|
+
if id == orig.id
|
182
|
+
state.finalize
|
183
|
+
ret_val.push(state)
|
184
|
+
break 2
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
ret_val
|
191
|
+
end
|
192
|
+
|
193
|
+
def find_state(state_id, container, container2)
|
194
|
+
str = ''
|
195
|
+
state_id.split(' ').sort.each { |st| str += st }
|
196
|
+
container.each do |state|
|
197
|
+
state.id == state_id ? (return state) : nil
|
198
|
+
end
|
199
|
+
container2.each do |state|
|
200
|
+
state.id == state_id ? (return state) : nil
|
201
|
+
end
|
202
|
+
NIL
|
203
|
+
end
|
204
|
+
|
205
|
+
def merge_state_names(states)
|
206
|
+
arr = []
|
207
|
+
states.each { |state| arr.push(state.id) }
|
208
|
+
arr = arr.uniq.sort
|
209
|
+
str = ''
|
210
|
+
arr.each do |id|
|
211
|
+
str += id + ','
|
212
|
+
end
|
213
|
+
str.byteslice(0, str.length - 1)
|
214
|
+
end
|
215
|
+
|
216
|
+
def resolve_state_on_stack(char)
|
217
|
+
popped = @stack.pop
|
218
|
+
arr = popped.get_next(char)
|
219
|
+
arr.each do |item|
|
220
|
+
@stack.push(item)
|
221
|
+
end
|
222
|
+
arr.size
|
223
|
+
end
|
224
|
+
|
225
|
+
def initialize(stat_arr, alph_arr, trans_arr, start)
|
226
|
+
@states = stat_arr
|
227
|
+
@alphabet = alph_arr
|
228
|
+
@transitions = trans_arr
|
229
|
+
@starting_state = start
|
230
|
+
end
|
231
|
+
|
232
|
+
# states must contain start and final states
|
233
|
+
def self.validate_states(states, start, final)
|
234
|
+
if states.include? start[0]
|
235
|
+
final.each do |fin|
|
236
|
+
states.include?(fin) ? nil : (return false)
|
237
|
+
end
|
238
|
+
return true
|
239
|
+
end
|
240
|
+
false
|
241
|
+
end
|
242
|
+
|
243
|
+
def self.validate_transitions(states, alphabet, transitions)
|
244
|
+
transitions.each do |tr|
|
245
|
+
trans = tr.split('-')
|
246
|
+
if !((states.include? trans[0]) && (states.include? trans[2]) &&
|
247
|
+
(alphabet.include? trans[1]))
|
248
|
+
return false
|
249
|
+
else
|
250
|
+
# rubocop hates it :/
|
251
|
+
end
|
252
|
+
end
|
253
|
+
true
|
254
|
+
end
|
255
|
+
|
256
|
+
def item_to_str(array)
|
257
|
+
str = ''
|
258
|
+
array.each do |item|
|
259
|
+
str += item.to_s + ' '
|
260
|
+
end
|
261
|
+
str.byteslice(0, str.length - 1)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
data/lib/nfa2dfa.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
5
|
-
|
1
|
+
# Nfa2Dfa package
|
2
|
+
module Nfa2Dfa
|
3
|
+
require_relative 'automaton'
|
4
|
+
require_relative 'state'
|
5
|
+
require_relative 'transition'
|
6
|
+
end
|