automata 0.1.0 → 1.0.0
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.
- 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
|