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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dbab533490518b4c647279ce350c19a02fb0689c
4
- data.tar.gz: d179faa2486eb52b07c1004c15e13315c6fbb7a2
3
+ metadata.gz: b5c182a3da5e4d525dbcf77af28799db0d406e6e
4
+ data.tar.gz: fbbc0362690ca01b3bcd2201082eb90dde281e4b
5
5
  SHA512:
6
- metadata.gz: 07695859de50e73183fa7fcee02061f125cdc0ab9aac9f41e79fdd806c6c636fd9ca337a3284e333ee64c9d04d80065ed611b77dc906848ad8bf0531208dfcec
7
- data.tar.gz: 1272fd6c80caa9eb789758e718017101031a5ec87da7d0a7f0c4b5475ed71581b9fc071e094687521c5cc2950c97c2cc4a3ba90653e8b3da24f275f91a22fc0e
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 + ".determined", 'w') { |file| file.write(data) }
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 + ".png")
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
- #z f - mnozina stavu
2
- #a b - vstupni abeceda
3
- #z-a-f f-b-f - prechod Q-T-Q ze stavu Q pres pismeno T do stavu Q
4
- #z - Pocatecni stav
5
- #f - mnozina koncovych stavu
6
-
7
- require 'graphviz'
8
- require_relative 'state.rb'
9
- require_relative 'transition.rb'
10
- module Nfa2Dfa
11
- class Automaton
12
-
13
- public
14
-
15
- def to_str
16
- ret_val = ""
17
- str = ""
18
- @states.each do |state|
19
- str += state.id + " "
20
- end
21
- ret_val += str.byteslice(0, str.length-1) + "\n"
22
- str = ""
23
- @alphabet.each do |a|
24
- str+= a + " "
25
- end
26
- ret_val += str.byteslice(0, str.length-1) + "\n"
27
- str = ""
28
- @transitions.each do |trans|
29
- str += trans.beginning_state.id + "-" + trans.alphabet + "-" + trans.ending_state.id + " "
30
- end
31
- ret_val += str.byteslice(0, str.length-1) + "\n"
32
- str = ""
33
- ret_val += @starting_state.id + "\n"
34
- @states.each do |state|
35
- if state.is_final == true
36
- str += state.id + " "
37
- end
38
- end
39
- ret_val += str.byteslice(0, str.length-1)
40
- ret_val
41
- end
42
-
43
- def to_graph(path)
44
- g = GraphViz.new( :G, :type => :digraph)
45
- @states.each do |state|
46
- #puts state.id
47
- state.to_graph_node(g)
48
- end
49
- g.each_node() do |name, node|
50
- # puts name
51
- end
52
- @transitions.each do |trans|
53
- trans.to_graph_transition(g)
54
- end
55
- g.each_edge do |ed|
56
- #puts ed.node_one + " " + ed.node_two
57
- end
58
- g.output( :png => path )
59
- end
60
-
61
- def self.init(path)
62
- #nacteni ze souboru
63
- if File.file?(path)
64
- data_arr = Array.new
65
- index = 0
66
- File.open(path).each_line do |line|
67
- data_arr[index] = line
68
- index = index + 1
69
- end
70
- if validate(data_arr)
71
- get_valid_input(data_arr)
72
- else
73
- puts "Invalid input"
74
- NIL
75
- end
76
- else
77
- puts "Invalid input"
78
- NIL
79
- end
80
- end
81
-
82
- def self.validate(data_arr)
83
- parsed = []
84
- data_arr.each do |item|
85
- parsed.push item.split(' ')
86
- end
87
- validate_transitions(parsed[0], parsed[1], parsed[2]) && validate_states(parsed[0], parsed[3], parsed[4])
88
- end
89
-
90
- #format: pismeno<mezera>pismeno...
91
- def accepts?(data)
92
- formatted_input = data.split(' ')
93
- @stack = Array.new
94
- @stack.push(@starting_state)
95
- formatted_input.size == 0 ? @starting_state.is_final : recurs_accepts?(formatted_input, 0)
96
- end
97
-
98
- def deterministic?
99
- @states.each do |state|
100
- @alphabet.each do |char|
101
- if state.get_next(char).size > 1
102
- return false
103
- end
104
- end
105
- end
106
- true
107
- end
108
-
109
- def determine
110
- deterministic? ? self : determine_prot
111
- end
112
-
113
- protected
114
-
115
- attr_accessor :states, :alphabet, :transitions, :starting_state, :stack
116
-
117
- def recurs_accepts?(data, index)
118
- ret_val = false
119
- if @stack.size != 0
120
- cnt = resolve_state_on_stack(data[index])
121
- index = index + 1
122
- if index == data.size
123
- cnt.times do
124
- if @stack.pop.is_final
125
- ret_val = true
126
- end
127
- end
128
- else
129
- return recurs_accepts?(data,index)
130
- end
131
- end
132
- ret_val
133
- end
134
-
135
- def self.get_valid_input(data_arr)
136
- states = Array.new
137
- fin = data_arr[4].split(' ')
138
-
139
- data_arr[0].split(' ').each do |wrd|
140
- states.push State.new(wrd)
141
- end
142
- fin.include?(states[0].id)
143
- states.each do |st|
144
- (fin.include? st.id) ? st.finalize : NIL
145
- end
146
- alphabet = Array.new
147
- data_arr[1].split(' ').each do |wrd|
148
- alphabet.insert(alphabet.size, wrd)
149
- end
150
- transitions = Array.new
151
- data_arr[2].split(' ').each do |wrd|
152
- trans = wrd.split('-')
153
- state1 = NIL
154
- state2 = NIL
155
- states.each do |item|
156
- trans[0] == item.id ? state1 = item : NIL
157
- trans[2] == item.id ? state2 = item : NIL
158
- state1 != NIL && state2 != NIL ? break : NIL
159
- end
160
- transitions.insert(transitions.size, Transition.new(state1, trans[1], state2))
161
- state1.add_transition(transitions[transitions.size-1])
162
- end
163
- starting = NIL
164
- states.each do |item2|
165
- if item2.id == data_arr[3].split(' ')[0]
166
- starting = item2
167
- item2.to_starting_node
168
- break
169
- end
170
- end
171
- Automaton.new(states, alphabet, transitions, starting)
172
- end
173
-
174
- def determine_prot
175
- #https://edux.fit.cvut.cz/courses/BI-AAG/_media/lectures/03/bi-aag-03-operace_s_automaty-4.pdf
176
- undetermined_states = Array.new
177
- determined_states = Array.new
178
- transits = Array.new
179
- tot_transits = Array.new
180
- curr_states = Array.new
181
- undetermined_states[0] = @starting_state.clone
182
- new_begin_state = undetermined_states[0]
183
- while(undetermined_states.size > 0)
184
- temp_state = undetermined_states[0]
185
- curr_states.clear
186
- transits.clear
187
- @alphabet.each do |char|
188
- t_states_by_char = temp_state.get_next(char)
189
- if t_states_by_char.size > 0
190
- state_id = merge_state_names(t_states_by_char)
191
- state = find_state(state_id, determined_states, undetermined_states)
192
- if state
193
- transits.push(Transition.new(temp_state, char, state))
194
- tot_transits.push(Transition.new(temp_state, char, state))
195
- else
196
- #Tvorba noveho statu
197
- state = State.new(state_id)
198
- state.associate_transitions(@transitions)
199
- undetermined_states.push(state)
200
- transits.push(Transition.new(temp_state, char, state))
201
- tot_transits.push(Transition.new(temp_state, char, state))
202
- end
203
- end
204
- end
205
- temp_state.clear_transitions
206
- transits.each do |transit|
207
- temp_state.add_transition(transit)
208
- end
209
- undetermined_states.delete(temp_state)
210
- determined_states.push(temp_state)
211
- end
212
-
213
- determined_states
214
- @alphabet
215
- tot_transits
216
- new_begin_state
217
- finals = associate_finals(determined_states)
218
- Automaton.new(determined_states, @alphabet.clone, tot_transits, new_begin_state)
219
- end
220
-
221
- private
222
-
223
- def associate_finals(stat)
224
- ret_val = Array.new
225
- @states.each do |orig|
226
- if orig.is_final
227
- stat.each do |state|
228
- state.id.split(',').each do |id|
229
- if id == orig.id
230
- state.finalize
231
- ret_val.push(state)
232
- break 2
233
- end
234
- end
235
- end
236
- end
237
- end
238
- ret_val
239
- end
240
-
241
- def find_state(state_id, container, container2)
242
- str = ""
243
- state_id.split(' ').sort.each do |st|
244
- str += st
245
- end
246
- # state_id = str.slice(0, str.size - 2)
247
- container.each do |state|
248
- if state.id == state_id
249
- return state
250
- end
251
- end
252
- container2.each do |state|
253
- if state.id == state_id
254
- return state
255
- end
256
- end
257
- NIL
258
- end
259
-
260
- def merge_state_names(states)
261
- arr = Array.new
262
- states.each do |state|
263
- arr.push(state.id)
264
- end
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
- module Nfa2Dfa
2
- require_relative 'automaton'
3
- require_relative 'state'
4
- require_relative 'transition'
5
- end
1
+ # Nfa2Dfa package
2
+ module Nfa2Dfa
3
+ require_relative 'automaton'
4
+ require_relative 'state'
5
+ require_relative 'transition'
6
+ end