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
data/lib/state.rb
CHANGED
@@ -1,67 +1,72 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@graphviz_node =
|
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
|
-
end
|
1
|
+
require 'graphviz'
|
2
|
+
require_relative 'transition.rb'
|
3
|
+
# Part of Nfa2Dfa module
|
4
|
+
module Nfa2Dfa
|
5
|
+
# State of automaton
|
6
|
+
class State
|
7
|
+
attr_reader :id, :is_final, :graphviz_node, :is_starting
|
8
|
+
|
9
|
+
def initialize(id)
|
10
|
+
@id = id
|
11
|
+
@is_final = false
|
12
|
+
@transitions = []
|
13
|
+
@graphviz_init = false
|
14
|
+
@is_starting = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_starting_node
|
18
|
+
@is_starting = true
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
@id.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def graph_id
|
26
|
+
is_starting ? (@id + '/init') : @id
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_graph_node(viz_graph)
|
30
|
+
if @is_final
|
31
|
+
@graphviz_node = viz_graph.add_nodes(
|
32
|
+
graph_id, :shape => 'doublecircle')
|
33
|
+
else
|
34
|
+
@graphviz_node = viz_graph.add_nodes(graph_id, :shape => 'circle')
|
35
|
+
end
|
36
|
+
@graphviz_init = false
|
37
|
+
end
|
38
|
+
|
39
|
+
def finalize
|
40
|
+
@is_final = true
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_transition(tr)
|
44
|
+
@transitions.insert(@transitions.size, tr)
|
45
|
+
end
|
46
|
+
|
47
|
+
def clear_transitions
|
48
|
+
@transitions.clear
|
49
|
+
end
|
50
|
+
|
51
|
+
def associate_transitions(all_transitions)
|
52
|
+
@transitions.clear
|
53
|
+
@id.split(',').each do |id_part|
|
54
|
+
all_transitions.each do |transition|
|
55
|
+
if id_part == transition.beginning_state.id
|
56
|
+
add_transition(transition)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_next(char)
|
63
|
+
ret_val = []
|
64
|
+
@transitions.each do |trans|
|
65
|
+
if trans.alphabet == char
|
66
|
+
ret_val.insert(ret_val.size, trans.ending_state)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
ret_val
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/transition.rb
CHANGED
@@ -1,24 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
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(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
1
|
+
require 'graphviz'
|
2
|
+
require_relative 'state.rb'
|
3
|
+
# Part of Nfa2Dfa module
|
4
|
+
module Nfa2Dfa
|
5
|
+
# Transition between states
|
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(
|
18
|
+
@beginning_state.graphviz_node,
|
19
|
+
@ending_state.graphviz_node, :label => @alphabet)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
ret = @beginning_state.to_s + '-' + alphabet + '-' + @ending_state.to_s
|
24
|
+
ret
|
25
|
+
end
|
26
|
+
|
27
|
+
def print
|
28
|
+
puts @beginning_state.id + '-' + @alphabet + '-' + @ending_state.id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/spec/automaton_spec.rb
CHANGED
@@ -1,134 +1,139 @@
|
|
1
|
-
require 'rspec'
|
2
|
-
require 'spec_helper'
|
3
|
-
require_relative '../lib/automaton.rb'
|
4
|
-
|
5
|
-
describe Nfa2Dfa::Automaton do
|
6
|
-
before(:each) do
|
7
|
-
@file_path = "#{File.dirname(__FILE__)}/testdata/valid_input.automat"
|
8
|
-
@file_path2 = "#{File.dirname(__FILE__)}/testdata/valid_input2.automat"
|
9
|
-
@file_path3 = "#{File.dirname(__FILE__)}/testdata/valid_input3.automat"
|
10
|
-
end
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
mat
|
15
|
-
mat.
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
(subject.accepts?
|
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
|
-
it
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
it
|
71
|
-
mat2 = Automaton.init(@file_path2)
|
72
|
-
mat2det = mat2.determine
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
word =
|
80
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
81
|
-
(mat2.accepts? word).should eq true
|
82
|
-
word =
|
83
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
84
|
-
(mat2.accepts? word).should eq true
|
85
|
-
word =
|
86
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
87
|
-
(mat2.accepts? word).should eq true
|
88
|
-
word =
|
89
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
90
|
-
(mat2.accepts? word).should eq true
|
91
|
-
word =
|
92
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
93
|
-
(mat2.accepts? word).should eq true
|
94
|
-
word =
|
95
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
96
|
-
(mat2.accepts? word).should eq true
|
97
|
-
word =
|
98
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
99
|
-
(mat2.accepts? word).should eq true
|
100
|
-
word =
|
101
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
102
|
-
(mat2.accepts? word).should eq true
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
word
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
word =
|
115
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
116
|
-
(mat2.accepts? word).should eq false
|
117
|
-
word =
|
118
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
119
|
-
(mat2.accepts? word).should eq false
|
120
|
-
word =
|
121
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
122
|
-
(mat2.accepts? word).should eq false
|
123
|
-
word =
|
124
|
-
(mat2.accepts? word).should eq mat2det.accepts? word
|
125
|
-
(mat2.accepts? word).should eq false
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
1
|
+
require 'rspec'
|
2
|
+
require 'spec_helper'
|
3
|
+
require_relative '../lib/automaton.rb'
|
4
|
+
|
5
|
+
describe Nfa2Dfa::Automaton do
|
6
|
+
before(:each) do
|
7
|
+
@file_path = "#{File.dirname(__FILE__)}/testdata/valid_input.automat"
|
8
|
+
@file_path2 = "#{File.dirname(__FILE__)}/testdata/valid_input2.automat"
|
9
|
+
@file_path3 = "#{File.dirname(__FILE__)}/testdata/valid_input3.automat"
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'initializes as' do
|
13
|
+
it 'valid automaton' do
|
14
|
+
mat = Automaton.init(@file_path)
|
15
|
+
mat.should_not eq NIL
|
16
|
+
mat.class.should eq Nfa2Dfa::Automaton
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'invalid automaton, when there is invalid input' do
|
20
|
+
mat = Automaton.init("#{File.dirname(__FILE__)}" +
|
21
|
+
'/testdata/invalid_input.automat')
|
22
|
+
mat.should eq NIL
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should be exportable in same format by method \'to_str\'' do
|
27
|
+
valid_str = "z f\na b\nz-a-f f-b-f\nz\nf"
|
28
|
+
mat = Automaton.init(@file_path)
|
29
|
+
mat.to_str.should eq valid_str
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'accepts method resolves' do
|
33
|
+
subject { Automaton.init(@file_path) }
|
34
|
+
it 'valid words' do
|
35
|
+
(subject.accepts? 'a b').should eq true
|
36
|
+
(subject.accepts? 'a').should eq true
|
37
|
+
(subject.accepts? 'a b b b').should eq true
|
38
|
+
(subject.accepts? 'a b b').should eq true
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'invalid words' do
|
42
|
+
(subject.accepts? '').should eq false
|
43
|
+
(subject.accepts? 'a a').should eq false
|
44
|
+
(subject.accepts? 'a a ').should eq false
|
45
|
+
(subject.accepts? 'a b a').should eq false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should be able to check if automat is deterministic' do
|
50
|
+
Automaton.init(@file_path).deterministic?.should eq true
|
51
|
+
Automaton.init(@file_path2).deterministic?.should eq false
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'can create deterministic automaton' do
|
55
|
+
it 'returns Automaton class' do
|
56
|
+
mat1 = Automaton.init(@file_path)
|
57
|
+
mat2 = Automaton.init(@file_path2)
|
58
|
+
mat3 = Automaton.init(@file_path3)
|
59
|
+
mat1.determine.class.should eq Nfa2Dfa::Automaton
|
60
|
+
mat2.determine.class.should eq Nfa2Dfa::Automaton
|
61
|
+
mat3.determine.class.should eq Nfa2Dfa::Automaton
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns same instance of Automaton when already deterministic' do
|
65
|
+
mat1 = Automaton.init(@file_path)
|
66
|
+
mat1det = mat1.determine
|
67
|
+
mat1.should eq mat1det
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'returns deterministic automaton' do
|
71
|
+
mat2 = Automaton.init(@file_path2)
|
72
|
+
mat2det = mat2.determine
|
73
|
+
mat2det.deterministic?.should eq true
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'which can accept same words' do
|
77
|
+
mat2 = Automaton.init(@file_path2)
|
78
|
+
mat2det = mat2.determine
|
79
|
+
word = ''
|
80
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
81
|
+
(mat2.accepts? word).should eq true
|
82
|
+
word = 'a a'
|
83
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
84
|
+
(mat2.accepts? word).should eq true
|
85
|
+
word = 'a b'
|
86
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
87
|
+
(mat2.accepts? word).should eq true
|
88
|
+
word = 'a c'
|
89
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
90
|
+
(mat2.accepts? word).should eq true
|
91
|
+
word = 'a c c'
|
92
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
93
|
+
(mat2.accepts? word).should eq true
|
94
|
+
word = 'a c c a'
|
95
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
96
|
+
(mat2.accepts? word).should eq true
|
97
|
+
word = 'a c c a c a'
|
98
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
99
|
+
(mat2.accepts? word).should eq true
|
100
|
+
word = 'a c c a c a a b'
|
101
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
102
|
+
(mat2.accepts? word).should eq true
|
103
|
+
word = 'c a b c a b c'
|
104
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
105
|
+
(mat2.accepts? word).should eq true
|
106
|
+
word = 'a c c b c'
|
107
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
108
|
+
(mat2.accepts? word).should eq true
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'which rejects same words' do
|
112
|
+
mat2 = Automaton.init(@file_path3)
|
113
|
+
mat2det = mat2.determine
|
114
|
+
word = '0'
|
115
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
116
|
+
(mat2.accepts? word).should eq false
|
117
|
+
word = '1'
|
118
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
119
|
+
(mat2.accepts? word).should eq false
|
120
|
+
word = '0 1'
|
121
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
122
|
+
(mat2.accepts? word).should eq false
|
123
|
+
word = '1 0'
|
124
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
125
|
+
(mat2.accepts? word).should eq false
|
126
|
+
word = '1 1 1 1 0'
|
127
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
128
|
+
(mat2.accepts? word).should eq false
|
129
|
+
word = '0 0 0 0 1'
|
130
|
+
(mat2.accepts? word).should eq mat2det.accepts? word
|
131
|
+
(mat2.accepts? word).should eq false
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should have method for exporting as GraphViz \'to_graph\'' do
|
135
|
+
mat = Automaton.init(@file_path2)
|
136
|
+
expect(mat.methods.include?(:to_graph)).to be true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|