nfa2dfa 1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
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