yatm 0.2.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: be3ead1b208b860b2ef2021496657196eff8a1bc42fc25c569cf77d3be6f37ff
4
+ data.tar.gz: a502d0f27f32c4a8686f5edca9aaf6e53f842a524b3e6cd26f34140528966bc5
5
+ SHA512:
6
+ metadata.gz: 8f63ffe798d7500425b9d2de058fbc4704584b5a9b3fbc20ac440677df16b5b1a1d1fb512cc5c3efcf5e97fb5c89d974c5cf15560cf8fad22bf54e511bdd9602
7
+ data.tar.gz: fa8a1cfef51c8cc0673a17c461e53319ab3d953e7cc8b85803e6136a3f010665c27992d7ae26f83f0bcfa162369691e9778414d88e9391ef31f88b1212bb8e5d
data/lib/yatm/error.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class YATM::Error < StandardError
4
+ def text
5
+ raise NotImplementedError
6
+ end
7
+
8
+ def to_s
9
+ super == self.class.to_s ? text : super
10
+ end
11
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ class YATM::Machine
4
+ attr_reader :tape, :state_machine, :history
5
+
6
+ def initialize(position: 0, content: [nil])
7
+ @tape = YATM::Tape.new(content, position: position)
8
+ @state_machine = YATM::StateMachine.new
9
+ @history = []
10
+ end
11
+
12
+ def initial_state(...); @state_machine.initial_state(...); end
13
+
14
+ def final_state(...); @state_machine.final_state(...); end
15
+
16
+ def states(...); @state_machine.states(...); end
17
+
18
+ def events(...); @state_machine.events(...); end
19
+
20
+ def event(...); @state_machine.event(...); end
21
+
22
+ def reset(...)
23
+ @history = []
24
+ @tape.reset(...)
25
+ @state_machine.reset
26
+ end
27
+
28
+ def to_h
29
+ {
30
+ state: @state_machine.current_state,
31
+ final: @state_machine.final_states.include?(@state_machine.current_state),
32
+ position: @tape.pos
33
+ }
34
+ end
35
+
36
+ def to_s; to_h.to_s; end
37
+
38
+ def to_txt
39
+ <<~TO_S
40
+ ,_______________
41
+ | State Machine
42
+ `---------------
43
+ #{@state_machine}
44
+ ,______
45
+ | Tape
46
+ `------
47
+ #{@tape}
48
+ TO_S
49
+ end
50
+
51
+ def step!(n = 1)
52
+ return if n < 1
53
+
54
+ (1..n).each do
55
+ result = @state_machine.process!(@tape.read)
56
+ @history << result
57
+ break if result[:final]
58
+
59
+ @tape.write result[:write]
60
+ @tape.move result[:move]
61
+ end
62
+
63
+ @history.last
64
+ end
65
+
66
+ def step(...)
67
+ step!(...) rescue YATM::Error
68
+ end
69
+
70
+ def run!(max = Float::INFINITY)
71
+ (1..max).each do
72
+ latest = step!
73
+ break to_h if latest[:final]
74
+ end
75
+ end
76
+
77
+ def run(...)
78
+ run!(...) rescue YATM::Error
79
+ end
80
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "state_machine_errors"
4
+ require_relative "event"
5
+
6
+ class YATM::StateMachine
7
+ attr_reader :current_state, :events, :final_states, :initial_state
8
+
9
+ def initialize
10
+ @events = {}
11
+ @final_states = []
12
+ end
13
+
14
+ def reset
15
+ @current_state = @initial_state
16
+ end
17
+
18
+ def to_s
19
+ <<~TO_S.chomp
20
+ | states: #{states}
21
+ | current: #{@current_state}
22
+ | events: #{@events.map(&:name)}
23
+ TO_S
24
+ end
25
+
26
+ def states
27
+ @events.map do |_name, event|
28
+ event.keys + event.map { |_, val| val[:to] }
29
+ end.flatten.uniq
30
+ end
31
+
32
+ def initial_state(state)
33
+ @initial_state = self.class.statify(state)
34
+ @current_state = @initial_state
35
+ end
36
+
37
+ def final_state(*states)
38
+ states.each do |state|
39
+ (@final_states << self.class.statify(state)).uniq!
40
+ end
41
+ end
42
+
43
+ def event(name, **transitions)
44
+ @events[name] = YATM::Event.new(name, **transitions)
45
+ end
46
+
47
+ def process(value)
48
+ process!(value)
49
+ rescue StateMachineError
50
+ nil
51
+ end
52
+
53
+ def process!(value)
54
+ return { final: @current_state } if @final_states.include?(@current_state)
55
+ raise InitialStateNotSet unless @current_state
56
+ raise InvalidEvent, value unless (event = @events[value])
57
+ raise InvalidTransition.new(@current_state, event) unless (
58
+ transition = event[@current_state] || event[YATM::ANY]
59
+ )
60
+
61
+ @current_state = transition[:to] unless transition[:to] == YATM::SAME
62
+ transition
63
+ end
64
+
65
+ def self.statify(state)
66
+ raise InvalidState, state unless state.respond_to?(:to_s)
67
+
68
+ state = state.to_s
69
+ raise InvalidState, state unless state.respond_to?(:to_sym)
70
+
71
+ state.to_sym
72
+ end
73
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "tape_errors"
4
+
5
+ class YATM::Tape
6
+ attr_accessor :pos
7
+
8
+ def initialize(...)
9
+ reset(...)
10
+ end
11
+
12
+ def reset(content = [nil], position: 0)
13
+ @pos = position
14
+ @p_tape = content
15
+ @n_tape = [nil]
16
+ end
17
+
18
+ def read
19
+ tape[table_pos]
20
+ end
21
+
22
+ def write(value)
23
+ tape[table_pos] = value
24
+ end
25
+
26
+ def move(direction = YATM::NONE)
27
+ case direction
28
+ when YATM::RIGHT
29
+ @pos += 1
30
+ expand if table_pos == tape.size
31
+ when YATM::LEFT
32
+ @pos -= 1
33
+ expand if table_pos > tape.size
34
+ when YATM::NONE
35
+ else
36
+ raise InvalidMove
37
+ end
38
+ end
39
+
40
+ def to_s
41
+ full = @n_tape.reverse + @p_tape
42
+ full.map.with_index do |val, idx|
43
+ cell_txt(val, idx)
44
+ end.join(" ")
45
+ .gsub(/^\[_\] +/, "")
46
+ .gsub(/(\[_\] )*\[_\]\z/, "")
47
+ .gsub(/ \z/, "")
48
+ end
49
+
50
+ def to_txt
51
+ full = @n_tape.reverse + @p_tape
52
+ str = ""
53
+ full.each_with_index do |val, idx|
54
+ str += (idx - @n_tape.size).to_s.rjust(4) + " | #{val.inspect}".ljust(8)
55
+ str += " <=" if idx == abs_pos
56
+ str += "\n"
57
+ end
58
+
59
+ str
60
+ end
61
+
62
+ private
63
+
64
+ def cell_txt(val, idx)
65
+ str = idx == @n_tape.size ? "|:| " : ""
66
+ str += idx == abs_pos ? ">|" : "["
67
+ str += val.nil? ? "_" : val.to_s
68
+ str += idx == abs_pos ? "|<" : "]"
69
+ str
70
+ end
71
+
72
+ def tape
73
+ @pos >= 0 ?
74
+ @p_tape :
75
+ @n_tape
76
+ end
77
+
78
+ def abs_pos
79
+ @pos + @n_tape.size
80
+ end
81
+
82
+ def table_pos
83
+ @pos >= 0 ?
84
+ @pos :
85
+ -@pos - 1
86
+ end
87
+
88
+ def expand
89
+ current_size = tape.size
90
+ (1..current_size).each { tape << nil }
91
+ end
92
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YATM
4
+ VERSION = "0.2.0"
5
+ end
data/lib/yatm.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "yatm/version"
4
+ require_relative "yatm/tape/tape"
5
+ require_relative "yatm/state_machine/state_machine"
6
+ require_relative "yatm/machine/machine"
7
+
8
+ module YATM
9
+ ANY = :*
10
+ SAME = :_
11
+ LEFT = :l
12
+ RIGHT = :r
13
+ NONE = nil
14
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yatm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - tiago-macedo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-08-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |-
14
+ This gem provides you with the module `YATM`.
15
+ The class YATM::Machine, specifically, allows you to program a functioning
16
+ turing machine and load it with a tape containing arbitrary symbols.
17
+
18
+ Check out files `lib/example_*.rb` to see it in action.
19
+ email:
20
+ - tiagomacedo@ufrj.br
21
+ executables: []
22
+ extensions: []
23
+ extra_rdoc_files: []
24
+ files:
25
+ - lib/yatm.rb
26
+ - lib/yatm/error.rb
27
+ - lib/yatm/machine/machine.rb
28
+ - lib/yatm/state_machine/state_machine.rb
29
+ - lib/yatm/tape/tape.rb
30
+ - lib/yatm/version.rb
31
+ homepage: https://github.com/tiago-macedo/yatm
32
+ licenses:
33
+ - MIT
34
+ metadata:
35
+ homepage_uri: https://github.com/tiago-macedo/yatm
36
+ source_code_uri: https://github.com/tiago-macedo/yatm
37
+ changelog_uri: https://github.com/tiago-macedo/yatm
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 3.0.0
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubygems_version: 3.2.3
54
+ signing_key:
55
+ specification_version: 4
56
+ summary: Yet Another Turing Machine implementation.
57
+ test_files: []