nfa2dfa 1.0.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 +7 -0
- data/lib/automaton.rb +312 -0
- data/lib/nfa2dfa.rb +6 -0
- data/lib/state.rb +67 -0
- data/lib/transition.rb +24 -0
- metadata +47 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3c062458afee68eb899ec35c6fee8e82ea0366cb
|
4
|
+
data.tar.gz: 9f5191740fba0e5b0b865cd9d3e3b1af3a22856e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a4c92af38dc76982b669a26d3ba61b3038fd5f28496f37f4d1f7f6ecaf26565365fdf71e4f42da9e27062501ffe582f98348f9feb58764a0a31e4665f9aa786c
|
7
|
+
data.tar.gz: 2f1c55ad5e1a8efd8d3ff31bc7b060a063698d30d32ba370fe34d85a796b16df8ef49c940b4438cf0a86c4198aa5e645fc0911a7ad17fd655b940ec78d43c573
|
data/lib/automaton.rb
ADDED
@@ -0,0 +1,312 @@
|
|
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
|
data/lib/nfa2dfa.rb
ADDED
data/lib/state.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# To change this template, choose Tools | Templates
|
2
|
+
# and open the template in the editor.
|
3
|
+
|
4
|
+
require 'graphviz'
|
5
|
+
require_relative 'transition.rb'
|
6
|
+
|
7
|
+
module Nfa2Dfa
|
8
|
+
class State
|
9
|
+
attr_reader :id, :is_final, :graphviz_node, :is_starting
|
10
|
+
|
11
|
+
def initialize(id)
|
12
|
+
@id = id
|
13
|
+
@is_final = false
|
14
|
+
@transitions = Array.new
|
15
|
+
@graphviz_init = false
|
16
|
+
@is_starting = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_starting_node
|
20
|
+
@is_starting = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def graph_id
|
24
|
+
is_starting ? (@id + "/init") : @id
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_graph_node(graphviz_graph)
|
28
|
+
if @is_final
|
29
|
+
@graphviz_node = graphviz_graph.add_nodes(graph_id, :shape => "doublecircle")
|
30
|
+
else
|
31
|
+
@graphviz_node = graphviz_graph.add_nodes(graph_id, :shape => "circle")
|
32
|
+
end
|
33
|
+
@graphviz_init = false
|
34
|
+
end
|
35
|
+
|
36
|
+
def finalize
|
37
|
+
@is_final = true
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_transition(tr)
|
41
|
+
@transitions.insert(@transitions.size, tr)
|
42
|
+
end
|
43
|
+
|
44
|
+
def clear_transitions
|
45
|
+
@transitions.clear
|
46
|
+
end
|
47
|
+
|
48
|
+
def associate_transitions(all_transitions)
|
49
|
+
@transitions.clear
|
50
|
+
@id.split(',').each do |id_part|
|
51
|
+
all_transitions.each do |transition|
|
52
|
+
if id_part == transition.beginning_state.id
|
53
|
+
add_transition(transition)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_next(char)
|
60
|
+
ret_val = Array.new
|
61
|
+
@transitions.each do |trans|
|
62
|
+
trans.alphabet == char ? ret_val.insert(ret_val.size, trans.ending_state) : NIL
|
63
|
+
end
|
64
|
+
ret_val
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/transition.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'graphviz'
|
3
|
+
require_relative 'state.rb'
|
4
|
+
|
5
|
+
module Nfa2Dfa
|
6
|
+
class Transition
|
7
|
+
|
8
|
+
attr_reader :beginning_state, :alphabet, :ending_state
|
9
|
+
|
10
|
+
def initialize(beg_state, alphabet, end_state)
|
11
|
+
@beginning_state = beg_state
|
12
|
+
@alphabet = alphabet
|
13
|
+
@ending_state = end_state
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_graph_transition(graphviz_graph)
|
17
|
+
graphviz_graph.add_edges( @beginning_state.graphviz_node, @ending_state.graphviz_node, :label => @alphabet)
|
18
|
+
end
|
19
|
+
|
20
|
+
def print
|
21
|
+
puts @beginning_state.id + "-" + @alphabet + "-" + @ending_state.id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nfa2dfa
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Zachov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-15 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Finite automaton determinizer
|
14
|
+
email: martin.zachov@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/state.rb
|
20
|
+
- lib/automaton.rb
|
21
|
+
- lib/transition.rb
|
22
|
+
- lib/nfa2dfa.rb
|
23
|
+
homepage: http://fit.cvut.cz
|
24
|
+
licenses:
|
25
|
+
- GNU GPL
|
26
|
+
metadata: {}
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 2.0.3
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: MI-RUB semestral
|
47
|
+
test_files: []
|