yarbf 0.0.2 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c50a097c05b4592a3af56541e41e1ce016f8aaef
4
- data.tar.gz: de0acce92f5167ce6fd1c3597bd925010205407b
3
+ metadata.gz: 3c0d5787578afc52f343e2c4450c85fffd9a591a
4
+ data.tar.gz: a740710edcdd967b813b7a56c43d73824027ab4b
5
5
  SHA512:
6
- metadata.gz: bf7026eaece5ff3bdc25cfcd1d7293208ed6ede199807103b931da68bfbbeb57429e34d46b41fb62fdf9f164377f79d4f191eab144b8258efd466eb415d941ef
7
- data.tar.gz: 8289e0617d9b293a03d1e9971ebeaea728a250cc5fd6f39de7ffb65e220b5cdc3a69daf39cfe6fe3c7f03916261c6e4e63c81150383b8d1b0a499d35922e1119
6
+ metadata.gz: 9b6fcad593c96a3af8a71c5c2ef9497a0fbcc9be9f3d9298767a0e5558e8ab4b9231003a20413d362c9f8631376425581edf8b7f49ff77a9bac2ef4726a070f9
7
+ data.tar.gz: 1dc418e4a83997e9ebe8171f0d1c6f17e5ef7839f85ef3188005db6b6c6ca750b666016f67f786c81886acd31de32eef85fbab866f9f05af82c6a0d5f282ca25
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ # Sublime Text project files
2
+ *.sublime-project
3
+ *.sublime-workspace
4
+
5
+ # RubyMine files
6
+ .idea/
7
+
8
+ # Ruby Gems
9
+ *.gem
10
+
11
+ # bundle generated
12
+ /.bundle/
13
+ /.yardoc
14
+ /Gemfile.lock
15
+ /_yardoc/
16
+ /coverage/
17
+ /doc/
18
+ /pkg/
19
+ /spec/reports/
20
+ /tmp/
21
+
22
+ # Coverage
23
+ test/coverage/
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
7
+ - ruby-head
8
+ before_install: gem install bundler
9
+ before_script: bundle update
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at chaosdefinition@hotmail.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in yarbf.gemspec
4
+ gemspec
5
+
6
+ # development dependencies
7
+ group :development do
8
+ gem 'bundler', '~> 1.11'
9
+ gem 'rake', '~> 10.0'
10
+ end
11
+
12
+ # test dependencies
13
+ group :test do
14
+ gem 'minitest', '~> 5.8'
15
+ gem 'coveralls', require: false
16
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Chaos Shen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Yarbf
2
+
3
+ [![Gem Version](https://img.shields.io/gem/v/yarbf.svg)](https://rubygems.org/gems/yarbf)
4
+ [![Build Status](https://travis-ci.org/chaosdefinition/yarbf.svg)](https://travis-ci.org/chaosdefinition/yarbf)
5
+ [![Code Climate](https://codeclimate.com/github/chaosdefinition/yarbf/badges/gpa.svg)](https://codeclimate.com/github/chaosdefinition/yarbf)
6
+ [![Dependency Status](https://gemnasium.com/chaosdefinition/yarbf.svg)](https://gemnasium.com/chaosdefinition/yarbf)
7
+ [![Coverage Status](https://coveralls.io/repos/chaosdefinition/yarbf/badge.svg?service=github)](https://coveralls.io/github/chaosdefinition/yarbf)
8
+ [![Security](https://hakiri.io/github/chaosdefinition/yarbf/master.svg)](https://hakiri.io/github/chaosdefinition/yarbf/master)
9
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt)
10
+
11
+ Yet another Brainfuck interpreter in Ruby.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'yarbf'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ ```shell
24
+ $ bundle
25
+ ```
26
+
27
+ Or install it yourself as:
28
+
29
+ ```shell
30
+ $ gem install yarbf
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ After installation, execute:
36
+
37
+ ```shell
38
+ $ yarbf --help
39
+ ```
40
+
41
+ It will show its usage information.
42
+
43
+ ## Contributing
44
+
45
+ Bug reports and pull requests are welcome on GitHub at https://github.com/chaosdefinition/yarbf. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
46
+
47
+
48
+ ## License
49
+
50
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ # test task
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.libs << 'test'
7
+ t.libs << 'lib'
8
+ t.test_files = FileList['test/test_*.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task :default => :spec
13
+ task :spec => :test
data/bin/yarbf CHANGED
@@ -1,15 +1,26 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # for parsing command line arguments
4
- require 'optparse'
3
+ lib = File.expand_path('../../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
+ require 'optparse' # for OptionParser
6
7
  require 'yarbf'
7
8
 
9
+ # overrides the default #Kernel.fail.
10
+ def fail(reason)
11
+ STDERR.puts File.basename($0) + ': ' + reason.to_s
12
+ exit false
13
+ end
14
+
15
+ # deal with SIGINT
16
+ Signal.trap('INT') { fail 'Interrupted!' }
17
+
8
18
  # set default values
9
19
  options = Hash.new
10
20
  options[:debug] = false
11
21
  options[:wrap_around] = false
12
22
  options[:cell_size] = 8
23
+ options[:input_mode] = :buffered
13
24
 
14
25
  parser = OptionParser.new do |opts|
15
26
  opts.banner = 'yarbf - Yet another Brainfuck interpreter in Ruby'
@@ -26,11 +37,15 @@ parser = OptionParser.new do |opts|
26
37
 
27
38
  # cell size
28
39
  opts.on('-s', '--size SIZE', OptionParser::DecimalInteger,
29
- 'Set cell size') { |size| options[:cell_size] = size }
40
+ 'Set cell size') { |s| options[:cell_size] = s }
41
+
42
+ # input mode
43
+ opts.on('-i', '--input MODE', Yarbf::INPUT_MODE_OPTIONS,
44
+ 'Set input mode (buffered, raw)') { |m| options[:input_mode] = m }
30
45
 
31
46
  # version
32
47
  opts.on('-V', '--version', 'show version and exit') do
33
- puts 'yarbf 0.0.1'
48
+ puts 'yarbf ' + Yarbf::VERSION
34
49
  exit
35
50
  end
36
51
 
@@ -47,14 +62,12 @@ end
47
62
  begin
48
63
  parser.parse!
49
64
  rescue OptionParser::ParseError => e
50
- STDERR.puts $0 + ': ' + e.to_s
51
- exit false
65
+ fail e.to_s
52
66
  end
53
67
 
54
- if ARGV[0] == nil
55
- STDERR.puts $0 + ': no source file specified'
56
- exit false
68
+ if ARGV[0].nil?
69
+ fail 'No source file specified!'
57
70
  end
58
71
 
59
- interpreter = BfInterpreter.new(options)
72
+ interpreter = Yarbf::BfInterpreter.new(options)
60
73
  interpreter.run(ARGV[0])
@@ -0,0 +1,4 @@
1
+ module Yarbf
2
+ # gem version
3
+ VERSION = '0.1.0'
4
+ end
data/lib/yarbf.rb CHANGED
@@ -1,244 +1,312 @@
1
- require 'io/console'
2
-
3
- ##
4
- # == BfInterpreter
5
- #
6
- # BfInterpreter is the main class of yarbf.
7
- #
8
- # === Options
9
- #
10
- # Options to interpreter can be specified via passing a #Hash object to
11
- # the constructor or just calling attribute writers. Supported options are:
12
- #
13
- # - debug:: Debug mode switch. Setting this option to true will print out
14
- # each Brainfuck instruction when interpreting. Default is false.
15
- # - wrap_around:: Wrap around switch. Setting this option to true will
16
- # ignore cell value overflow or underflow. Default is false.
17
- # - cell_size:: Size of each cell in bit. Default is 8.
18
- #
19
- # === Examples
20
- #
21
- # Following is a brief example.
22
- #
23
- # require 'yarbf'
24
- #
25
- # interpreter = BfInterpreter.new({:debug => true, :cell_size => 16})
26
- # interpreter.run('/path/to/Brainfuck/source')
27
- #
28
- class BfInterpreter
29
- ##
30
- # Initialize the instance.
31
- #
32
- # +hash+:: A Hash containing options to the interpreter.
33
- #
34
- def initialize(hash)
35
- @option = hash
36
- end
1
+ require 'io/console' # for IO.getch
37
2
 
38
- ##
39
- # Returns whether the interpreter is in debug mode.
40
- #
41
- def debug?
42
- @option[:debug]
43
- end
3
+ require 'yarbf/version' # for Yarbf::VERSION
44
4
 
45
- ##
46
- # Sets the interpreter to debug mode
47
- #
48
- # +debug+:: A boolean value.
49
- #
50
- def debug=(debug)
51
- @option[:debug] = debug
52
- end
5
+ module Yarbf
6
+ # available options for the program
7
+ OPTIONS = [:debug, :wrap_around, :cell_size, :input_mode] # :nodoc:
53
8
 
54
- ##
55
- # Returns whether the interpreter accepts wrap around.
56
- #
57
- def wrap_around?
58
- @option[:wrap_around]
59
- end
9
+ # available options for input mode
10
+ INPUT_MODE_OPTIONS = [:buffered, :raw] # :nodoc:
60
11
 
61
12
  ##
62
- # Sets whether the interpreter should accept wrap around.
13
+ # == BfInterpreter
63
14
  #
64
- # +wrap_around+:: A boolean value.
15
+ # BfInterpreter is the main class of module #YARBF.
65
16
  #
66
- def wrap_around=(wrap_around)
67
- @option[:wrap_around] = wrap_around
68
- end
69
-
70
- ##
71
- # Returns the size of each tape cell.
17
+ # === Options
72
18
  #
73
- def cell_size?
74
- @option[:cell_size]
75
- end
76
-
77
- ##
78
- # Sets the size of each tape cell.
19
+ # Options to interpreter can be specified via passing a #Hash object to
20
+ # the constructor or just calling attribute writers. Supported options are:
79
21
  #
80
- # +cell_size+:: An integer.
22
+ # - debug:: Debug mode switch. Setting this options to true will print out
23
+ # each Brainfuck instruction when interpreting. Default is false.
24
+ # - wrap_around:: Wrap around switch. Setting this options to true will
25
+ # ignore cell value overflow or underflow. Default is false.
26
+ # - cell_size:: Size of each cell in bit. Default is 8.
27
+ # - input_mode:: Input mode. Available options are +:buffered+ and +:raw+.
28
+ # In buffered mode, the characters you type will be echoed on
29
+ # screen and will be buffered until you type an enter. In raw
30
+ # mode, there's no echoing nor buffering. Default is
31
+ # +:buffered+.
81
32
  #
82
- def cell_size=(cell_size)
83
- @option[:cell_size] = cell_size
84
- end
85
-
86
- ##
87
- # Interpret a Brainfuck source file.
33
+ # === Examples
34
+ #
35
+ # Following is a brief example.
36
+ #
37
+ # require 'yarbf'
88
38
  #
89
- # +src+:: Path of the source.
39
+ # options = {
40
+ # :debug => true,
41
+ # :wrap_around => true,
42
+ # :cell_size => 16,
43
+ # :input_mode => :buffered
44
+ # }
45
+ # interpreter = YARBF::BfInterpreter.new(options)
46
+ # interpreter.run('/path/to/Brainfuck/source')
90
47
  #
91
- def run(src)
92
- units = []
48
+ class BfInterpreter
49
+ ##
50
+ # Initialize the instance.
51
+ #
52
+ # +options+:: A Hash containing options to the interpreter.
53
+ #
54
+ def initialize(options)
55
+ unless options.is_a? Hash and OPTIONS.all? { |s| options.has_key? s }
56
+ fail 'Invalid options given!'
57
+ end
58
+ @option = options.dup
59
+ end
93
60
 
94
- # construct units
95
- File.open(src) do |file|
96
- units = construct_program_units(file)
61
+ ##
62
+ # Returns whether the interpreter is in debug mode.
63
+ #
64
+ def debug?
65
+ @option[:debug]
97
66
  end
98
67
 
99
- # match brackets
100
- match_brackets(units)
101
-
102
- # do interpret
103
- tape = Array.new
104
- position = 0
105
- unit = units[0]
106
- while unit != nil
107
- tape[position] = BfCell.new(cell_size?) if tape[position] == nil
108
-
109
- STDERR.printf('%s', unit.instruction) if debug?
110
-
111
- case unit.instruction
112
- when '+' then tape[position].increase(1, wrap_around?)
113
- when '-' then tape[position].decrease(1, wrap_around?)
114
- when '<' then
115
- position -= 1
116
- fail 'Cell position out of bound!' if position < 0
117
- when '>' then position += 1
118
- when ',' then
119
- tape[position].value = STDIN.getch.ord
120
- when '.' then STDOUT.putc tape[position].value
121
- when '[' then
122
- if tape[position].value == 0
123
- unit = unit.match
124
- next
125
- end
126
- when ']' then
127
- if tape[position].value != 0
128
- unit = unit.match
129
- next
130
- end
131
- else fail "Illegal instruction '#{unit.instruction}'!"
68
+ ##
69
+ # Sets the interpreter to debug mode
70
+ #
71
+ # +debug+:: A boolean value.
72
+ #
73
+ def debug=(debug)
74
+ unless debug.is_a?(TrueClass) or debug.is_a?(FalseClass)
75
+ fail "'debug' switch should be a boolean but is a #{debug.class}!"
132
76
  end
77
+ @option[:debug] = debug
78
+ end
133
79
 
134
- unit = unit.next
80
+ ##
81
+ # Returns whether the interpreter accepts wrap around.
82
+ #
83
+ def wrap_around?
84
+ @option[:wrap_around]
135
85
  end
136
- end
137
86
 
138
- ##
139
- # Constructs and returns the program units of class #BfProgramUnit.
140
- #
141
- # +file+:: The #File object of source file.
142
- #
143
- def construct_program_units(file)
144
- units = Array.new
145
- position = 0
146
-
147
- file.each_byte do |c|
148
- case c.chr
149
- when '+', '-', '<', '>', '[', ']', '.', ',' then
150
- unit = BfProgramUnit.new(c.chr)
151
- units[position - 1].next = unit if position > 0
152
- units[position] = unit
153
- position += 1
154
- else
155
- # other characters are considered as comments, do nothing
87
+ ##
88
+ # Sets whether the interpreter should accept wrap around.
89
+ #
90
+ # +wrap_around+:: A boolean value.
91
+ #
92
+ def wrap_around=(wrap_around)
93
+ unless wrap_around.is_a?(TrueClass) or wrap_around.is_a?(FalseClass)
94
+ fail "'wrap_around' should be a boolean but is a #{wrap_around.class}!"
156
95
  end
96
+ @option[:wrap_around] = wrap_around
157
97
  end
158
98
 
159
- units
160
- end
99
+ ##
100
+ # Returns the size of each tape cell.
101
+ #
102
+ def cell_size?
103
+ @option[:cell_size]
104
+ end
161
105
 
162
- ##
163
- # Matches each bracket '[' and ']' in the source.
164
- #
165
- # +units+:: An #Array of program units.
166
- #
167
- def match_brackets(units)
168
- units.each_index do |i|
169
- if units[i].instruction == '['
170
- level = 0
171
- units[i + 1 .. units.length - 1].each_index do |j|
172
- j += i + 1
173
- if units[j].instruction == '['
174
- level += 1
175
- elsif units[j].instruction == ']'
176
- if level > 0
177
- level -= 1
178
- else
179
- units[i].match = units[j]
180
- units[j].match = units[i]
181
- break
106
+ ##
107
+ # Sets the size of each tape cell.
108
+ #
109
+ # +cell_size+:: An integer.
110
+ #
111
+ def cell_size=(cell_size)
112
+ unless cell_size.is_a? Integer
113
+ fail "'cell_size' should be an integer but is a #{cell_size.class}!"
114
+ end
115
+ @option[:cell_size] = cell_size
116
+ end
117
+
118
+ ##
119
+ # Returns the current input mode.
120
+ #
121
+ def input_mode?
122
+ @option[:input_mode]
123
+ end
124
+
125
+ ##
126
+ # Sets the input mode.
127
+ #
128
+ # +input_mode+:: A symbol of +:buffered+ or +:raw+.
129
+ #
130
+ def input_mode=(input_mode)
131
+ unless INPUT_MODE_OPTIONS.include? input_mode
132
+ fail 'Invalid value of input mode!'
133
+ end
134
+ @option[:input_mode] = input_mode
135
+ end
136
+
137
+ ##
138
+ # Interpret a Brainfuck source file.
139
+ #
140
+ # +src+:: Path of the source.
141
+ #
142
+ def run(src)
143
+ units = []
144
+
145
+ # construct units
146
+ begin
147
+ File.open(src) { |file| units = construct_program_units(file) }
148
+ rescue SystemCallError => e
149
+ STDERR.puts $0 + ': ' + e.to_s
150
+ return
151
+ end
152
+
153
+ # match brackets
154
+ match_brackets(units)
155
+
156
+ # do interpret
157
+ tape = Array.new
158
+ position = 0
159
+ unit = units[0]
160
+ until unit.nil?
161
+ tape[position] = BfCell.new(cell_size?) if tape[position].nil?
162
+
163
+ STDERR.printf('%s', unit.instruction) if debug?
164
+
165
+ case unit.instruction
166
+ when '+' then
167
+ tape[position].increase(1, wrap_around?)
168
+ when '-' then
169
+ tape[position].decrease(1, wrap_around?)
170
+ when '<' then
171
+ position -= 1
172
+ fail 'Cell position out of bound!' if position < 0
173
+ when '>' then
174
+ position += 1
175
+ when ',' then
176
+ ch = nil
177
+ begin
178
+ ch = STDIN.getc if input_mode? == :buffered
179
+ ch = STDIN.getch if input_mode? == :raw
180
+ rescue SystemCallError => e
181
+ fail e.to_s
182
182
  end
183
- end
183
+ return if ch.nil?
184
+ tape[position].value = ch.ord
185
+ when '.' then
186
+ STDOUT.putc tape[position].value
187
+ when '[' then
188
+ if tape[position].value == 0
189
+ unit = unit.match
190
+ next
191
+ end
192
+ when ']' then
193
+ if tape[position].value != 0
194
+ unit = unit.match
195
+ next
196
+ end
197
+ else
198
+ fail "Invalid instruction '#{unit.instruction}'!"
184
199
  end
185
- fail 'Unmatched brackets!' if level > 0
200
+
201
+ unit = unit.next
186
202
  end
187
203
  end
188
- end
189
204
 
190
- private :construct_program_units, :match_brackets
205
+ ##
206
+ # Constructs and returns the program units of class #BfProgramUnit.
207
+ #
208
+ # +file+:: The #File object of source file.
209
+ #
210
+ def construct_program_units(file)
211
+ units = Array.new
212
+ position = 0
191
213
 
192
- ##
193
- # Cell of the Brainfuck tape.
194
- #
195
- class BfCell
196
- attr_accessor :cell_size, :value
214
+ file.each_byte do |c|
215
+ case c.chr
216
+ when '+', '-', '<', '>', '[', ']', '.', ',' then
217
+ unit = BfProgramUnit.new(c.chr)
218
+ units[position - 1].next = unit if position > 0
219
+ units[position] = unit
220
+ position += 1
221
+ else
222
+ # other characters are considered as comments, do nothing
223
+ end
224
+ end
197
225
 
198
- def initialize(cell_size = 8, value = 0)
199
- @cell_size = cell_size
200
- @value = value
226
+ units
201
227
  end
202
228
 
203
229
  ##
204
- # Increase the value of a cell.
230
+ # Matches each bracket '[' and ']' in the source.
205
231
  #
206
- # +increment+:: Value to increase by. Default is 1.
207
- # +wrap_around+:: Whether to wrap around. Default is false.
232
+ # +units+:: An #Array of program units.
208
233
  #
209
- def increase(increment = 1, wrap_around = false)
210
- if !wrap_around &&
211
- (@value + increment < 0 || @value + increment >= (1 << @cell_size))
212
- fail 'Overflow or underflow happened while forbidden!'
213
- else
214
- @value = (@value + increment) % (1 << @cell_size)
234
+ def match_brackets(units)
235
+ units.each_index do |i|
236
+ if units[i].instruction == '['
237
+ level = 0
238
+ units[i + 1 .. units.length - 1].each_index do |j|
239
+ j += i + 1
240
+ if units[j].instruction == '['
241
+ level += 1
242
+ elsif units[j].instruction == ']'
243
+ if level > 0
244
+ level -= 1
245
+ else
246
+ units[i].match = units[j]
247
+ units[j].match = units[i]
248
+ break
249
+ end
250
+ end
251
+ end
252
+ fail 'Unmatched brackets!' if level > 0
253
+ end
215
254
  end
216
255
  end
217
256
 
257
+ private :construct_program_units, :match_brackets
258
+
218
259
  ##
219
- # Decrease the value of a cell.
220
- #
221
- # +decrement+:: Value to decrease by. Default is 1.
222
- # +wrap_around+:: Whether to wrap around. Default is false.
260
+ # Cell of the Brainfuck tape.
223
261
  #
224
- def decrease(decrement = 1, wrap_around = false)
225
- self.increase(-decrement, wrap_around)
262
+ class BfCell
263
+ attr_accessor :cell_size, :value
264
+
265
+ def initialize(cell_size = 8, value = 0)
266
+ @cell_size = cell_size
267
+ @value = value
268
+ end
269
+
270
+ ##
271
+ # Increase the value of a cell.
272
+ #
273
+ # +increment+:: Value to increase by. Default is 1.
274
+ # +wrap_around+:: Whether to wrap around. Default is false.
275
+ #
276
+ def increase(increment = 1, wrap_around = false)
277
+ if !wrap_around &&
278
+ (@value + increment < 0 || @value + increment >= (1 << @cell_size))
279
+ fail 'Overflow or underflow happened while forbidden!'
280
+ else
281
+ @value = (@value + increment) % (1 << @cell_size)
282
+ end
283
+ end
284
+
285
+ ##
286
+ # Decrease the value of a cell.
287
+ #
288
+ # +decrement+:: Value to decrease by. Default is 1.
289
+ # +wrap_around+:: Whether to wrap around. Default is false.
290
+ #
291
+ def decrease(decrement = 1, wrap_around = false)
292
+ self.increase(-decrement, wrap_around)
293
+ end
226
294
  end
227
- end
228
295
 
229
- ##
230
- # Program unit of Brainfuck.
231
- #
232
- class BfProgramUnit
233
- attr_reader :instruction
296
+ ##
297
+ # Program unit of Brainfuck.
298
+ #
299
+ class BfProgramUnit
300
+ attr_reader :instruction
234
301
 
235
- attr_accessor :match
236
- attr_accessor :next
302
+ attr_accessor :match
303
+ attr_accessor :next
237
304
 
238
- def initialize(instruction)
239
- @instruction = instruction
240
- @match = nil
241
- @next = nil
305
+ def initialize(instruction)
306
+ @instruction = instruction
307
+ @match = nil
308
+ @next = nil
309
+ end
242
310
  end
243
311
  end
244
312
  end
data/yarbf.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'yarbf/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'yarbf'
8
+ s.version = Yarbf::VERSION
9
+ s.authors = ['Chaos Shen']
10
+ s.email = ['chaosdefinition@hotmail.com']
11
+
12
+ s.summary = 'Yet another Brainfuck interpreter in Ruby'
13
+ s.description = 'yarbf is a simple Brainfuck interpreter in Ruby.'
14
+ s.homepage = 'http://github.com/chaosdefinition/yarbf'
15
+ s.license = 'MIT'
16
+
17
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ s.bindir = 'bin'
19
+ s.executables = ['yarbf']
20
+ s.require_paths = ['lib']
21
+
22
+ s.required_ruby_version = '>= 2.0.0'
23
+
24
+ # runtime dependencies
25
+ s.add_runtime_dependency 'io-console', '~> 0.4'
26
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yarbf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chaos Shen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-27 00:00:00.000000000 Z
11
+ date: 2015-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: io-console
@@ -25,14 +25,24 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.4'
27
27
  description: yarbf is a simple Brainfuck interpreter in Ruby.
28
- email: chaosdefinition@hotmail.com
28
+ email:
29
+ - chaosdefinition@hotmail.com
29
30
  executables:
30
31
  - yarbf
31
32
  extensions: []
32
33
  extra_rdoc_files: []
33
34
  files:
35
+ - ".gitignore"
36
+ - ".travis.yml"
37
+ - CODE_OF_CONDUCT.md
38
+ - Gemfile
39
+ - LICENSE.txt
40
+ - README.md
41
+ - Rakefile
34
42
  - bin/yarbf
35
43
  - lib/yarbf.rb
44
+ - lib/yarbf/version.rb
45
+ - yarbf.gemspec
36
46
  homepage: http://github.com/chaosdefinition/yarbf
37
47
  licenses:
38
48
  - MIT
@@ -45,7 +55,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
45
55
  requirements:
46
56
  - - ">="
47
57
  - !ruby/object:Gem::Version
48
- version: '0'
58
+ version: 2.0.0
49
59
  required_rubygems_version: !ruby/object:Gem::Requirement
50
60
  requirements:
51
61
  - - ">="