automata 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/README.md +12 -14
- data/automata.gemspec +1 -1
- data/examples/pda_sample.yml +38 -0
- data/examples/turing_1.yml +49 -0
- data/lib/automata.rb +3 -1
- data/lib/automata/dfa.rb +22 -3
- data/lib/automata/nfa.rb +25 -6
- data/lib/automata/pda.rb +159 -0
- data/lib/automata/turing.rb +157 -0
- data/lib/automata/version.rb +1 -1
- data/spec/pda_spec.rb +59 -0
- data/spec/turing_spec.rb +141 -0
- metadata +17 -8
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -39,26 +39,24 @@ We can easily define a machine's _n_-tuples in a YAML file. For example, let's c
|
|
39
39
|
- C
|
40
40
|
- D
|
41
41
|
alphabet:
|
42
|
-
-
|
43
|
-
-
|
42
|
+
- 0
|
43
|
+
- 1
|
44
44
|
start: A
|
45
45
|
accept:
|
46
46
|
- C
|
47
47
|
transitions:
|
48
48
|
A:
|
49
|
-
|
50
|
-
|
49
|
+
0: B
|
50
|
+
1: D
|
51
51
|
B:
|
52
|
-
|
53
|
-
|
52
|
+
0: C
|
53
|
+
1: D
|
54
54
|
C:
|
55
|
-
|
56
|
-
|
55
|
+
0: C
|
56
|
+
1: C
|
57
57
|
D:
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
__Note:__ Integer hash keys are currently unsupported, hence they must be wrapped in quotes.
|
58
|
+
0: D
|
59
|
+
1: D
|
62
60
|
|
63
61
|
With our machine defined, we can create a new DFA object from the file.
|
64
62
|
|
@@ -87,11 +85,11 @@ Now that we've built a machine, we can pass it input and let it work its magic.
|
|
87
85
|
>> dfa.accepts? '0101'
|
88
86
|
=> false
|
89
87
|
|
90
|
-
Awesomesauce.
|
88
|
+
Awesomesauce. Please refer to the [wiki](https://github.com/jico/automata/wiki "automata wiki") for more details.
|
91
89
|
|
92
90
|
## Special Characters
|
93
91
|
|
94
|
-
* _&_ - Represents an ε-transition (epsilon transition)
|
92
|
+
* _&_ - Represents an ε-transition (epsilon transition). Note that you must wrap the symbol in quotes.
|
95
93
|
|
96
94
|
## Contributing
|
97
95
|
|
data/automata.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
require File.expand_path('../lib/automata/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
gem.authors = ["Jico Baligod"]
|
5
|
+
gem.authors = ["Jico Baligod", "Lukas Sabota"]
|
6
6
|
gem.email = ["jico@baligod.com"]
|
7
7
|
gem.description = %q{Create and simulate automaton.}
|
8
8
|
gem.summary = %q{This gem provides a number of classes to create and simulate Deterministic/Nondeterministic Finite Automata, Push-down Automata, and Turing Machines.}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
## # Sample PDA file.
|
2
|
+
#
|
3
|
+
# Accepts balanced parentheses only.
|
4
|
+
#
|
5
|
+
# '@' to represent delta
|
6
|
+
# 'ACCEPT' to represent unique, single start state
|
7
|
+
states:
|
8
|
+
- S
|
9
|
+
- A
|
10
|
+
- B
|
11
|
+
- ha
|
12
|
+
alphabet:
|
13
|
+
- '('
|
14
|
+
- ')'
|
15
|
+
start: S
|
16
|
+
accept: ha
|
17
|
+
transitions:
|
18
|
+
S:
|
19
|
+
'&':
|
20
|
+
to: 'A'
|
21
|
+
push: '@'
|
22
|
+
A:
|
23
|
+
'(':
|
24
|
+
to: 'A'
|
25
|
+
push: 'x'
|
26
|
+
')':
|
27
|
+
to: 'B'
|
28
|
+
pop: 'x'
|
29
|
+
B:
|
30
|
+
'(':
|
31
|
+
to: 'A'
|
32
|
+
push: 'x'
|
33
|
+
')':
|
34
|
+
to: 'B'
|
35
|
+
pop: 'x'
|
36
|
+
'&':
|
37
|
+
to: 'ha'
|
38
|
+
pop: '@'
|
@@ -0,0 +1,49 @@
|
|
1
|
+
##
|
2
|
+
# Sample TM build file
|
3
|
+
#
|
4
|
+
# Accepts all binary strings containing
|
5
|
+
# the substring 101.
|
6
|
+
#
|
7
|
+
# Notes:
|
8
|
+
# '@' denotes the blank for the tape alphabet
|
9
|
+
# 'ACCEPT' denotes unique accept state (include in transition)
|
10
|
+
# 'REJECT' denotes unique reject state (include in transition)
|
11
|
+
states:
|
12
|
+
- A
|
13
|
+
- B
|
14
|
+
- C
|
15
|
+
inputAlphabet:
|
16
|
+
- 0
|
17
|
+
- 1
|
18
|
+
tapeAlphabet:
|
19
|
+
- 0
|
20
|
+
- 1
|
21
|
+
start: A
|
22
|
+
transitions:
|
23
|
+
A:
|
24
|
+
0:
|
25
|
+
to: A
|
26
|
+
write: 0
|
27
|
+
move: R
|
28
|
+
1:
|
29
|
+
to: B
|
30
|
+
write: 1
|
31
|
+
move: R
|
32
|
+
B:
|
33
|
+
1:
|
34
|
+
to: B
|
35
|
+
write: 1
|
36
|
+
move: R
|
37
|
+
0:
|
38
|
+
to: C
|
39
|
+
write: 0
|
40
|
+
move: R
|
41
|
+
C:
|
42
|
+
1:
|
43
|
+
to: ACCEPT
|
44
|
+
write: 1
|
45
|
+
move: R
|
46
|
+
0:
|
47
|
+
to: A
|
48
|
+
write: 0
|
49
|
+
move: R
|
data/lib/automata.rb
CHANGED
@@ -3,6 +3,8 @@ require 'yaml'
|
|
3
3
|
require "automata/state_diagram"
|
4
4
|
require "automata/dfa"
|
5
5
|
require "automata/nfa"
|
6
|
+
require "automata/pda"
|
7
|
+
require "automata/turing"
|
6
8
|
|
7
9
|
module Automata
|
8
10
|
|
@@ -18,4 +20,4 @@ class Hash
|
|
18
20
|
obj = obj.inject({}){|h,(k,v)| h[k.to_s] = Hash.keys_to_strings(v); h}
|
19
21
|
return obj
|
20
22
|
end
|
21
|
-
end
|
23
|
+
end
|
data/lib/automata/dfa.rb
CHANGED
@@ -18,14 +18,33 @@ module Automata
|
|
18
18
|
return true
|
19
19
|
end
|
20
20
|
|
21
|
+
# Runs the input on the machine and returns a Hash describing
|
22
|
+
# the machine's final state after running.
|
23
|
+
#
|
24
|
+
# @param [String] input the string to use as input for the DFA
|
25
|
+
# @return [Hash] a hash describing the DFA state after running
|
26
|
+
# * :input [String] the original input string
|
27
|
+
# * :accept [Boolean] whether or not the DFA accepted the string
|
28
|
+
# * :head [String] the state which the head is currently on
|
29
|
+
def feed(input)
|
30
|
+
head = @start.to_s
|
31
|
+
input.each_char { |symbol| head = @transitions[head][symbol] }
|
32
|
+
accept = is_accept_state? head
|
33
|
+
resp = {
|
34
|
+
input: input,
|
35
|
+
accept: accept,
|
36
|
+
head: head
|
37
|
+
}
|
38
|
+
resp
|
39
|
+
end
|
40
|
+
|
21
41
|
# Determines whether the DFA accepts a given string.
|
22
42
|
#
|
23
43
|
# @param [String] input the string to use as input for the DFA.
|
24
44
|
# @return [Boolean] whether or not the DFA accepts the string.
|
25
45
|
def accepts?(input)
|
26
|
-
|
27
|
-
|
28
|
-
is_accept_state? head
|
46
|
+
resp = feed(input)
|
47
|
+
resp[:accept]
|
29
48
|
end
|
30
49
|
|
31
50
|
# Determines if a given state is an accept state.
|
data/lib/automata/nfa.rb
CHANGED
@@ -5,11 +5,15 @@ module Automata
|
|
5
5
|
# Iterate through each states to verify the graph
|
6
6
|
# is not disjoint.
|
7
7
|
|
8
|
-
#
|
8
|
+
# Runs the input on the machine and returns a Hash describing
|
9
|
+
# the machine's final state after running.
|
9
10
|
#
|
10
|
-
# @param [String] input the string to use as input for the NFA
|
11
|
-
# @return [
|
12
|
-
|
11
|
+
# @param [String] input the string to use as input for the NFA
|
12
|
+
# @return [Hash] a hash describing the NFA state after running
|
13
|
+
# * :input [String] the original input string
|
14
|
+
# * :accept [Boolean] whether or not the NFA accepted the string
|
15
|
+
# * :heads [Array] the state which the head is currently on
|
16
|
+
def feed(input)
|
13
17
|
heads = [@start]
|
14
18
|
|
15
19
|
# Move any initial e-transitions
|
@@ -41,8 +45,23 @@ module Automata
|
|
41
45
|
break if heads.empty?
|
42
46
|
end
|
43
47
|
|
44
|
-
|
45
|
-
|
48
|
+
accept = false
|
49
|
+
heads.each { |head| accept = true if accept_state? head }
|
50
|
+
|
51
|
+
resp = {
|
52
|
+
input: input,
|
53
|
+
accept: accept,
|
54
|
+
heads: heads
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
# Determines whether the NFA accepts a given string.
|
59
|
+
#
|
60
|
+
# @param [String] input the string to use as input for the NFA.
|
61
|
+
# @return [Boolean] Whether or not the NFA accepts the input string.
|
62
|
+
def accepts?(input)
|
63
|
+
resp = feed(input)
|
64
|
+
resp[:accept]
|
46
65
|
end
|
47
66
|
|
48
67
|
# Determines the transition states, if any, from a given
|
data/lib/automata/pda.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
module Automata
|
2
|
+
# Push Down Automata
|
3
|
+
class PDA < NFA
|
4
|
+
attr_accessor :stack
|
5
|
+
|
6
|
+
# Initialize a PDA object.
|
7
|
+
def initialize(params={})
|
8
|
+
super(params)
|
9
|
+
@alphabet << '&' unless !@alphabet || @alphabet.include?('&')
|
10
|
+
@stack = []
|
11
|
+
end
|
12
|
+
|
13
|
+
# Runs the input on the machine and returns a Hash describing
|
14
|
+
# the machine's final state after running.
|
15
|
+
#
|
16
|
+
# @param [String] input the string to use as input for the PDA
|
17
|
+
# @return [Hash] a hash describing the PDA state after running
|
18
|
+
# * :input [String] the original input string
|
19
|
+
# * :accept [Boolean] whether or not the PDA accepted the string
|
20
|
+
# * :heads [Array] the state which the head is currently on
|
21
|
+
def feed(input)
|
22
|
+
heads, @stack, accept = [@start], [], false
|
23
|
+
|
24
|
+
# Move any initial e-transitions
|
25
|
+
eTrans = transition(@start, '&') if has_transition?(@start, '&')
|
26
|
+
heads += eTrans
|
27
|
+
|
28
|
+
puts "initial heads: #{heads}"
|
29
|
+
puts "initial stack: #{@stack}"
|
30
|
+
|
31
|
+
# Iterate through each symbol of input string
|
32
|
+
input.each_char do |symbol|
|
33
|
+
newHeads = []
|
34
|
+
|
35
|
+
puts "Reading symbol: #{symbol}"
|
36
|
+
|
37
|
+
heads.each do |head|
|
38
|
+
puts "At head #{head}"
|
39
|
+
|
40
|
+
# Check if head can transition read symbol
|
41
|
+
# Head dies if no transition for symbol
|
42
|
+
if has_transition?(head, symbol)
|
43
|
+
puts "Head #{head} transitions #{symbol}"
|
44
|
+
puts "stack: #{@stack}"
|
45
|
+
transition(head, symbol).each { |t| newHeads << t }
|
46
|
+
puts "heads: #{newHeads}"
|
47
|
+
puts "stack: #{@stack}"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
heads = newHeads
|
53
|
+
break if heads.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
puts "Loop finished"
|
57
|
+
|
58
|
+
accept = includes_accept_state? heads
|
59
|
+
|
60
|
+
puts "heads: #{heads}"
|
61
|
+
puts "stack: #{stack}"
|
62
|
+
puts "accept: #{accept}"
|
63
|
+
|
64
|
+
resp = {
|
65
|
+
input: input,
|
66
|
+
accept: accept,
|
67
|
+
heads: heads,
|
68
|
+
stack: stack
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# Determines whether the PDA accepts a given string.
|
74
|
+
#
|
75
|
+
# This method is intended to override the parent accepts method.
|
76
|
+
#
|
77
|
+
# @param [String] input the string to use as input for PDA.
|
78
|
+
# @return [Boolean] whether or not the PDA accepts the string.
|
79
|
+
def accepts?(input)
|
80
|
+
resp = feed(input)
|
81
|
+
resp[:accept]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Determines the transition states, if any, from a given
|
85
|
+
# beginning state and input symbol pair.
|
86
|
+
#
|
87
|
+
# @param [String] state state label for beginning state.
|
88
|
+
# @param [String] symbol input symbol.
|
89
|
+
# @return [Array] Array of destination transition states.
|
90
|
+
def transition(state, symbol, stackTop=nil)
|
91
|
+
dests = []
|
92
|
+
|
93
|
+
if has_transition?(state, symbol)
|
94
|
+
actions = @transitions[state][symbol]
|
95
|
+
stackTop ||= @stack.last
|
96
|
+
able = true
|
97
|
+
@stack.push actions['push'] if actions['push']
|
98
|
+
if actions['pop']
|
99
|
+
able = false unless stackTop == actions['pop']
|
100
|
+
@stack.pop if able
|
101
|
+
end
|
102
|
+
if able
|
103
|
+
dests << actions['to']
|
104
|
+
|
105
|
+
if has_transition?(actions['to'], '&')
|
106
|
+
actions = @transitions[actions['to']]['&']
|
107
|
+
able = true
|
108
|
+
@stack.push actions['push'] if actions['push']
|
109
|
+
if actions['pop']
|
110
|
+
able = false unless @stack.last == actions['pop']
|
111
|
+
@stack.pop if able
|
112
|
+
end
|
113
|
+
if able
|
114
|
+
dests << actions['to']
|
115
|
+
end
|
116
|
+
end
|
117
|
+
dests
|
118
|
+
else
|
119
|
+
return dests
|
120
|
+
end
|
121
|
+
else
|
122
|
+
return []
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def pop?(symbol)
|
127
|
+
@stack.last == symbol
|
128
|
+
end
|
129
|
+
|
130
|
+
# Determines whether or not any transition states exist
|
131
|
+
# given a beginning state and input symbol pair.
|
132
|
+
#
|
133
|
+
# @param (see #transition)
|
134
|
+
# @return [Boolean] Whether or not a transition exists.
|
135
|
+
def has_transition?(state, symbol)
|
136
|
+
return false unless @transitions.has_key? state
|
137
|
+
if @transitions[state].has_key? symbol
|
138
|
+
actions = @transitions[state][symbol]
|
139
|
+
return false if actions['pop'] && @stack.last != actions['pop']
|
140
|
+
return true
|
141
|
+
else
|
142
|
+
return false
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Determines if a given state is an accept state.
|
147
|
+
#
|
148
|
+
# @param [String] state the state label to check.
|
149
|
+
# @return [Boolean] whether or not the state is an accept state.
|
150
|
+
def accept_state?(state)
|
151
|
+
@accept.include? state
|
152
|
+
end
|
153
|
+
|
154
|
+
def includes_accept_state?(states)
|
155
|
+
states.each { |s| return true if accept_state? s }
|
156
|
+
return false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module Automata
|
2
|
+
# Turing Machine
|
3
|
+
class Turing < StateDiagram
|
4
|
+
attr_accessor :inputAlphabet, :tapeAlphabet, :tape, :reject
|
5
|
+
|
6
|
+
# @todo Check that each state is connected.
|
7
|
+
# Iterate through each states to verify the graph
|
8
|
+
# is not disjoint.
|
9
|
+
|
10
|
+
def initialize(params={})
|
11
|
+
yaml = {}
|
12
|
+
yaml = YAML::load_file(params[:file]) if params.has_key? :file
|
13
|
+
super(params)
|
14
|
+
@inputAlphabet = params[:inputAlphabet] || yaml['inputAlphabet']
|
15
|
+
@tapeAlphabet = params[:tapeAlphabet] || yaml['tapeAlphabet']
|
16
|
+
@tape = Tape.new
|
17
|
+
@accept = false
|
18
|
+
@reject = false
|
19
|
+
end
|
20
|
+
|
21
|
+
# Runs the input on the turing machine and returns a Hash describing
|
22
|
+
# the machine's final state after running.
|
23
|
+
#
|
24
|
+
# @param [String] input the string to use as input/tape for the TM
|
25
|
+
# @return [Hash] a hash describing the TM state after running
|
26
|
+
# * :input [String] the original input string
|
27
|
+
# * :accept [Boolean] whether or not the TM halted in an accept state
|
28
|
+
# * :reject [Boolean] whether or not the TM halted in a reject state
|
29
|
+
# * :head [String] the state which the head halted
|
30
|
+
# * :tape [String] the resulting tape string
|
31
|
+
def feed(input)
|
32
|
+
@tape = Tape.new(input)
|
33
|
+
@accept = false
|
34
|
+
@reject = false
|
35
|
+
|
36
|
+
stateHead = @start.to_s
|
37
|
+
input.each_char do |symbol|
|
38
|
+
toState = transition(stateHead, symbol)
|
39
|
+
if @accept || @reject
|
40
|
+
break
|
41
|
+
else
|
42
|
+
stateHead = toState
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
resp = {
|
47
|
+
input: input,
|
48
|
+
accept: @accept,
|
49
|
+
reject: @reject,
|
50
|
+
head: stateHead,
|
51
|
+
tape: @tape.memory,
|
52
|
+
output: @tape.output
|
53
|
+
}
|
54
|
+
resp
|
55
|
+
end
|
56
|
+
|
57
|
+
# Determines whether the TM halts in an accept state.
|
58
|
+
#
|
59
|
+
# @param [String] input the string to use as input for the TM.
|
60
|
+
# @return [Boolean] Whether or not the TM accepts the input string.
|
61
|
+
def accepts?(input)
|
62
|
+
resp = feed(input)
|
63
|
+
resp[:accept]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Determines whether the TM halts in an reject state.
|
67
|
+
#
|
68
|
+
# @param [String] input the string to use as input for the TM.
|
69
|
+
# @return [Boolean] Whether or not the TM rejects the input string.
|
70
|
+
def rejects?(input)
|
71
|
+
resp = feed(input)
|
72
|
+
resp[:reject]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Determines the transition states, if any, from a given
|
76
|
+
# beginning state and input symbol pair.
|
77
|
+
#
|
78
|
+
# @note Turing machines have two unique states:
|
79
|
+
# * +ACCEPT+ Unique accept state
|
80
|
+
# * +REJECT+ Unique reject state
|
81
|
+
# @param [String] state state label for beginning state.
|
82
|
+
# @param [String] symbol input symbol.
|
83
|
+
# @return [Array] Array of destination transition states.
|
84
|
+
def transition(state, symbol)
|
85
|
+
actions = @transitions[state][symbol]
|
86
|
+
@tape.transition(symbol, actions['write'], actions['move'])
|
87
|
+
|
88
|
+
# Flip the bits for halting states
|
89
|
+
@accept = true if actions['to'] == 'ACCEPT'
|
90
|
+
@reject = true if actions['to'] == 'REJECT'
|
91
|
+
@head = actions['to']
|
92
|
+
end
|
93
|
+
|
94
|
+
# Determines whether or not any transition states exist
|
95
|
+
# given a beginning state and input symbol pair.
|
96
|
+
#
|
97
|
+
# @param (see #transition)
|
98
|
+
# @return [Boolean] Whether or not a transition exists.
|
99
|
+
def has_transition?(state, read)
|
100
|
+
return false unless @transitions.include? state
|
101
|
+
@transitions[state].has_key? read
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
# Turing Machine tape
|
107
|
+
class Tape
|
108
|
+
attr_accessor :memory
|
109
|
+
|
110
|
+
# Creates a new tape.
|
111
|
+
#
|
112
|
+
# @param [String] string the input string
|
113
|
+
def initialize(string=nil)
|
114
|
+
if string
|
115
|
+
@memory = []
|
116
|
+
@memory << '@' unless string[0] == '@'
|
117
|
+
@memory += string.split('')
|
118
|
+
@memory << '@' unless string[-1] == '@'
|
119
|
+
@head = 1
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Transitions the tape.
|
124
|
+
#
|
125
|
+
# @param [String] read the input symbol read
|
126
|
+
# @param [String] write the symbol to write
|
127
|
+
# @param [String] move Either 'L', 'R', or 'S' (default) to
|
128
|
+
# move left, right, or stay, respectively.
|
129
|
+
# @return [Boolean] whether the transition succeeded
|
130
|
+
def transition(read, write, move)
|
131
|
+
if read == @memory[@head]
|
132
|
+
@memory[@head] = write
|
133
|
+
case move
|
134
|
+
when 'R'
|
135
|
+
# Move right
|
136
|
+
@memory << '@' if @memory[@head + 1]
|
137
|
+
@head += 1
|
138
|
+
when 'L'
|
139
|
+
# Move left
|
140
|
+
@memory.unshift('@') if @head == 0
|
141
|
+
@head -= 1
|
142
|
+
end
|
143
|
+
return true
|
144
|
+
else
|
145
|
+
return false
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Trims the memory empty cells and outputs the tape string.
|
150
|
+
#
|
151
|
+
# @return [String] the tape memory string
|
152
|
+
def output
|
153
|
+
@memory.join.sub(/^@*/, '').sub(/@*$/, '')
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
data/lib/automata/version.rb
CHANGED
data/spec/pda_spec.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Automata::PDA do
|
4
|
+
|
5
|
+
##
|
6
|
+
# balanced bracket pda
|
7
|
+
#
|
8
|
+
context "Initializing from a valid file" do
|
9
|
+
before do
|
10
|
+
@pda = Automata::PDA.new(file: 'examples/pda_sample.yml')
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have transition S, &" do
|
14
|
+
@pda.has_transition?('S','&').should == true
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have transition A, (" do
|
18
|
+
@pda.has_transition?('S','&').should == true
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should accept '()'" do
|
22
|
+
@pda.accepts?('()').should == true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should accept '(())'" do
|
26
|
+
@pda.accepts?('(())').should == true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not accept the empty string" do
|
30
|
+
@pda.accepts?('').should == false
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not accept '(('" do
|
34
|
+
@pda.accepts?('((').should == false
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should not accept '())'" do
|
38
|
+
@pda.accepts?('())').should == false
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should accept '(()())'" do
|
42
|
+
@pda.accepts?('(()())').should == true
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should not accept '(()'" do
|
46
|
+
@pda.accepts?('(()').should == false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "Initializing an empty pda" do
|
51
|
+
before do
|
52
|
+
@pda = Automata::PDA.new
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should be created successfully" do
|
56
|
+
@pda.should be_an_instance_of Automata::PDA
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/spec/turing_spec.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Automata::Turing do
|
4
|
+
|
5
|
+
##
|
6
|
+
# Turing from file
|
7
|
+
#
|
8
|
+
context "Initializing from a valid file" do
|
9
|
+
before do
|
10
|
+
@turing = Automata::Turing.new(file: 'examples/turing_1.yml')
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should not accept '0'" do
|
14
|
+
@turing.accepts?('0').should == false
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should not accept '1'" do
|
18
|
+
@turing.accepts?('1').should == false
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should not accept the empty string" do
|
22
|
+
@turing.accepts?('').should == false
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should not accept '01'" do
|
26
|
+
@turing.accepts?('01').should == false
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should accept '101'" do
|
30
|
+
@turing.accepts?('101').should == true
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should accept '1010'" do
|
34
|
+
@turing.accepts?('1010').should == true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should accept '01011'" do
|
38
|
+
@turing.accepts?('01011').should == true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "Initializing an empty Turing" do
|
43
|
+
before do
|
44
|
+
@turing = Automata::Turing.new
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should be created successfully" do
|
48
|
+
@turing.should be_an_instance_of Automata::Turing
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Turing params
|
54
|
+
#
|
55
|
+
context "Initializing a Turing by params" do
|
56
|
+
before do
|
57
|
+
states = ['A','B','C']
|
58
|
+
inputAlphabet = [0,1]
|
59
|
+
tapeAlphabet = [0,1]
|
60
|
+
start = 'A'
|
61
|
+
transitions = {
|
62
|
+
'A' => {
|
63
|
+
0 => {
|
64
|
+
to: 'A',
|
65
|
+
write: 0,
|
66
|
+
move: 'R'
|
67
|
+
},
|
68
|
+
1 => {
|
69
|
+
to: 'B',
|
70
|
+
write: 1,
|
71
|
+
move: 'R'
|
72
|
+
}
|
73
|
+
},
|
74
|
+
'B' => {
|
75
|
+
0 => {
|
76
|
+
to: 'C',
|
77
|
+
write: 0,
|
78
|
+
move: 'R'
|
79
|
+
},
|
80
|
+
1 => {
|
81
|
+
to: 'B',
|
82
|
+
write: 1,
|
83
|
+
move: 'R'
|
84
|
+
}
|
85
|
+
},
|
86
|
+
'C' => {
|
87
|
+
0 => {
|
88
|
+
to: 'A',
|
89
|
+
write: 0,
|
90
|
+
move: 'R'
|
91
|
+
},
|
92
|
+
1 => {
|
93
|
+
to: 'ACCEPT',
|
94
|
+
write: 1,
|
95
|
+
move: 'R'
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
params = {
|
100
|
+
states: states,
|
101
|
+
inputAlphabet: inputAlphabet,
|
102
|
+
tapeAlphabet: tapeAlphabet,
|
103
|
+
start: start,
|
104
|
+
transitions: transitions
|
105
|
+
}
|
106
|
+
@turing = Automata::Turing.new(params)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should be created successfully" do
|
110
|
+
@turing.should be_an_instance_of Automata::Turing
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should not accept '0'" do
|
114
|
+
@turing.accepts?('0').should == false
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should not accept '1'" do
|
118
|
+
@turing.accepts?('1').should == false
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should not accept the empty string" do
|
122
|
+
@turing.accepts?('').should == false
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should not accept '01'" do
|
126
|
+
@turing.accepts?('01').should == false
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should accept '101'" do
|
130
|
+
@turing.accepts?('101').should == true
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should accept '1010'" do
|
134
|
+
@turing.accepts?('1010').should == true
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should accept '01011'" do
|
138
|
+
@turing.accepts?('01011').should == true
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
metadata
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: automata
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jico Baligod
|
9
|
+
- Lukas Sabota
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
13
|
+
date: 2012-04-30 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: rake
|
16
|
-
requirement: &
|
17
|
+
requirement: &70159780666060 !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
20
|
- - ! '>='
|
@@ -21,10 +22,10 @@ dependencies:
|
|
21
22
|
version: '0'
|
22
23
|
type: :development
|
23
24
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
+
version_requirements: *70159780666060
|
25
26
|
- !ruby/object:Gem::Dependency
|
26
27
|
name: rspec
|
27
|
-
requirement: &
|
28
|
+
requirement: &70159780280380 !ruby/object:Gem::Requirement
|
28
29
|
none: false
|
29
30
|
requirements:
|
30
31
|
- - ~>
|
@@ -32,7 +33,7 @@ dependencies:
|
|
32
33
|
version: 2.9.0
|
33
34
|
type: :development
|
34
35
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
+
version_requirements: *70159780280380
|
36
37
|
description: Create and simulate automaton.
|
37
38
|
email:
|
38
39
|
- jico@baligod.com
|
@@ -50,14 +51,20 @@ files:
|
|
50
51
|
- examples/dfa_sample.yml
|
51
52
|
- examples/nfa_1.yml
|
52
53
|
- examples/nfa_2.yml
|
54
|
+
- examples/pda_sample.yml
|
55
|
+
- examples/turing_1.yml
|
53
56
|
- lib/automata.rb
|
54
57
|
- lib/automata/dfa.rb
|
55
58
|
- lib/automata/nfa.rb
|
59
|
+
- lib/automata/pda.rb
|
56
60
|
- lib/automata/state_diagram.rb
|
61
|
+
- lib/automata/turing.rb
|
57
62
|
- lib/automata/version.rb
|
58
63
|
- spec/dfa_spec.rb
|
59
64
|
- spec/nfa_spec.rb
|
65
|
+
- spec/pda_spec.rb
|
60
66
|
- spec/spec_helper.rb
|
67
|
+
- spec/turing_spec.rb
|
61
68
|
homepage: http://github.com/jico/automata
|
62
69
|
licenses: []
|
63
70
|
post_install_message:
|
@@ -72,7 +79,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
72
79
|
version: '0'
|
73
80
|
segments:
|
74
81
|
- 0
|
75
|
-
hash:
|
82
|
+
hash: -3684933468062137733
|
76
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
84
|
none: false
|
78
85
|
requirements:
|
@@ -81,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
88
|
version: '0'
|
82
89
|
segments:
|
83
90
|
- 0
|
84
|
-
hash:
|
91
|
+
hash: -3684933468062137733
|
85
92
|
requirements: []
|
86
93
|
rubyforge_project:
|
87
94
|
rubygems_version: 1.8.17
|
@@ -92,4 +99,6 @@ summary: This gem provides a number of classes to create and simulate Determinis
|
|
92
99
|
test_files:
|
93
100
|
- spec/dfa_spec.rb
|
94
101
|
- spec/nfa_spec.rb
|
102
|
+
- spec/pda_spec.rb
|
95
103
|
- spec/spec_helper.rb
|
104
|
+
- spec/turing_spec.rb
|