yarbf 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="