rbfk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Sean McCarthy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,43 @@
1
+ == Brainfuck, in Ruby.
2
+
3
+ See http://en.wikipedia.org/wiki/Brainfuck
4
+
5
+ == Usage
6
+
7
+ rbfk.rb <brainfuck source file>
8
+
9
+ OR
10
+
11
+ echo '++++++[>++++++++++<-]+++++.' | rbfk.rb
12
+
13
+ although piping in a script this way that uses the "," operation won't work,
14
+ since the "," operation reads from STDIN.
15
+
16
+ Also supports Ook![http://esolangs.org/wiki/Ook!] and Spoon[http://esolangs.org/wiki/Spoon] scripts.
17
+
18
+ You can use the BrainFuck class in your own programs (why?) like so:
19
+
20
+ require 'brain_fuck'
21
+
22
+ program = File.open('test.bf', 'r')
23
+ #
24
+ # OR any IO stream, e.g.
25
+ # program = StringIO.new('++++++[>++++++++++<-]+++++.')
26
+ #
27
+ bf = BrainFuck.new(program)
28
+ bf.run
29
+
30
+ === Options
31
+
32
+ options = { :memory_size => 50_000 }
33
+ bf = BrainFuck.new(program, options)
34
+ bf.run
35
+
36
+ Where options is a hash with one or more of the following options:
37
+
38
+ [:debug] will emit debugging information to STDOUT
39
+ [:execution_limit] the maximum operations to execute (including repetition in loops) before forced termination (to prevent runaway scripts). Defaults to 1,000,000.
40
+ [:memory_size] the size of the "tape" (the array) size used for the BrainFuck machine's memory. Defaults to 30,000.
41
+ [:input_stream] the IO stream from which the "," operation reads
42
+ [:output_stream] the IO stream to which the "." operation will write
43
+
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gem|
8
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
9
+ gem.name = "rbfk"
10
+ gem.homepage = "http://github.com/seandmccarthy/rbfk"
11
+ gem.license = "MIT"
12
+ gem.summary = %Q{BrainFuck interpreter}
13
+ gem.description = %Q{An interpreter for BrainFuck that also be embedded in your programs}
14
+ gem.email = "sean@clanmcccarthy.net"
15
+ gem.authors = ["Sean McCarthy"]
16
+ gem.add_development_dependency "rspec", "~> 2.8.0"
17
+ gem.add_development_dependency "rdoc", "~> 3.12"
18
+ gem.add_development_dependency "bundler", "~> 1.0.0"
19
+ gem.add_development_dependency "jeweler", "~> 1.8.3"
20
+ end
21
+ Jeweler::RubygemsDotOrgTasks.new
22
+
23
+ require 'rspec/core'
24
+ require 'rspec/core/rake_task'
25
+ RSpec::Core::RakeTask.new(:spec) do |spec|
26
+ spec.pattern = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ task :default => :spec
30
+
31
+ require 'rdoc/task'
32
+ Rake::RDocTask.new do |rdoc|
33
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
34
+
35
+ rdoc.rdoc_dir = 'rdoc'
36
+ rdoc.title = "rbfk #{version}"
37
+ rdoc.rdoc_files.include('README*')
38
+ rdoc.rdoc_files.include('lib/**/*.rb')
39
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require File.expand_path(File.join('..', '..', 'lib', 'BrainFuck'), __FILE__)
3
+
4
+ src = ARGF.read
5
+ puts BrainFuck.bf_to_ook(src)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require File.expand_path(File.join('..', '..', 'lib', 'BrainFuck'), __FILE__)
3
+
4
+ bf = BrainFuck.new(ARGF)
5
+ bf.run
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env ruby -w
2
+
3
+ class BrainFuck
4
+ DEFAULT_MEMORY_SIZE = 30_000
5
+ DEFAULT_EXEC_LIMIT = 1_000_000 # Stop badly constructed scripts going forever
6
+
7
+ OPS = {
8
+ '+' => :increment,
9
+ '-' => :decrement,
10
+ '>' => :move_pointer_left,
11
+ '<' => :move_pointer_right,
12
+ ',' => :read_in,
13
+ '.' => :print_out,
14
+ '[' => :loop_start,
15
+ ']' => :loop_end
16
+ }
17
+
18
+ DIALECTS = {
19
+ :ook => {
20
+ 'Ook. Ook.' => '+',
21
+ 'Ook! Ook!' => '-',
22
+ 'Ook. Ook?' => '>',
23
+ 'Ook? Ook.' => '<',
24
+ 'Ook! Ook?' => '[',
25
+ 'Ook? Ook!' => ']',
26
+ 'Ook! Ook.' => '.',
27
+ 'Ook. Ook!' => ','
28
+ },
29
+ :spoon => {
30
+ 1 => '+',
31
+ 0 => {
32
+ 0 => {
33
+ 0 => '-',
34
+ 1 => {
35
+ 0 => {
36
+ 0 => '[',
37
+ 1 => {
38
+ 0 => '.',
39
+ 1 => {
40
+ 0 => ','
41
+ }
42
+ }
43
+ },
44
+ 1 => ']'
45
+ }
46
+ },
47
+ 1 => {
48
+ 0 => '>',
49
+ 1 => '<'
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ attr_accessor :memory,
56
+ :data_pointer,
57
+ :instruction_pointer,
58
+ :pointer_stack
59
+
60
+ def initialize(program_input_stream, options={})
61
+ @debug = options[:debug] || false
62
+ @exec_limit = options[:execution_limit] || DEFAULT_EXEC_LIMIT
63
+ @execution_count = 0
64
+
65
+ @instruction_pointer = -1
66
+ @memory_size = options[:memory_size] || DEFAULT_MEMORY_SIZE
67
+ @memory = Array.new(@memory_size){ 0 }
68
+ @data_pointer = 0
69
+ @pointer_stack = []
70
+
71
+ @input_stream = options[:input_stream] || STDIN
72
+ @output_stream = options[:output_stream] || STDOUT
73
+
74
+ @program = get_program(program_input_stream)
75
+ @program_end = @program.size
76
+ end
77
+
78
+ def execute(op)
79
+ return unless OPS.has_key?(op)
80
+ puts op if @debug
81
+ send(OPS[op])
82
+ @execution_count += 1
83
+ end
84
+
85
+ def increment
86
+ @memory[@data_pointer] += 1
87
+ end
88
+
89
+ def decrement
90
+ @memory[@data_pointer] -= 1
91
+ end
92
+
93
+ def read_in
94
+ @memory[@data_pointer] = @input_stream.getc.ord
95
+ end
96
+
97
+ def print_out
98
+ @output_stream.putc @memory[@data_pointer]
99
+ end
100
+
101
+ def move_pointer_left
102
+ @data_pointer += 1
103
+ if @data_pointer == @memory_size
104
+ @data_pointer = 0
105
+ end
106
+ end
107
+
108
+ def move_pointer_right
109
+ if @data_pointer > 0
110
+ @data_pointer -= 1
111
+ else
112
+ @data_pointer = @memory_size - 1
113
+ end
114
+ end
115
+
116
+ def loop_start
117
+ if @memory[@data_pointer] > 0
118
+ @pointer_stack.push(@instruction_pointer-1)
119
+ else
120
+ @instruction_pointer = matching_brace_position(@instruction_pointer)
121
+ end
122
+ end
123
+
124
+ def loop_end
125
+ if @pointer_stack.empty?
126
+ raise "Bracket mismatch"
127
+ end
128
+ if @memory[@data_pointer] == 0
129
+ @pointer_stack.pop
130
+ else
131
+ @instruction_pointer = @pointer_stack.pop
132
+ end
133
+ end
134
+
135
+ def self.ook_to_bf(ook)
136
+ bf = ''
137
+ ook.scan(/(Ook[\.\?\!])\s*(Ook[\.\?\!])/) do |m|
138
+ command = "#{m[0]} #{m[1]}"
139
+ unless DIALECTS[:ook].include?(command)
140
+ raise "Got confused, thought it was Ook!"
141
+ end
142
+ bf << DIALECTS[:ook][command]
143
+ end
144
+ bf
145
+ end
146
+
147
+ def self.bf_to_ook(bf)
148
+ ook = ''
149
+ ook_bf = DIALECTS[:ook].invert
150
+ bf.each_char do |op|
151
+ next unless op.match(/[\>\<\+\-\.,\[\]]/)
152
+ ook << ook_bf[op]
153
+ ook << ' '
154
+ end
155
+ ook
156
+ end
157
+
158
+ def self.spoon_to_bf(spoon)
159
+ src = spoon.gsub(/[^01]/, '')
160
+ bf = ''
161
+ current = DIALECTS[:spoon]
162
+ spoon.each_char do |i|
163
+ current = current[i.to_i]
164
+ unless current.is_a?(Hash)
165
+ bf << current
166
+ current = DIALECTS[:spoon]
167
+ end
168
+ end
169
+ bf
170
+ end
171
+
172
+ def run
173
+ while not ended? do
174
+ dump if @debug
175
+ op = next_instruction
176
+ puts "op = #{op}" if @debug
177
+ execute(op)
178
+ dump if @debug
179
+ end
180
+ end
181
+
182
+ def next_instruction
183
+ @instruction_pointer += 1
184
+ @program[@instruction_pointer]
185
+ end
186
+
187
+ def ended?
188
+ (@instruction_pointer >= @program_end or
189
+ @execution_count >= @exec_limit)
190
+ end
191
+
192
+ def dump
193
+ puts
194
+ puts "instruction_pointer = #{@instruction_pointer}"
195
+ puts "data_pointer = #{@data_pointer}"
196
+ puts "memory@data_pointer = #{@memory[@data_pointer]}"
197
+ puts "pointer_stack = #{@pointer_stack.inspect}"
198
+ puts
199
+ end
200
+
201
+ private
202
+
203
+ def get_program(program_input_stream)
204
+ src = program_input_stream.read
205
+ if src.match(/(Ook[\.\?\!]\s*){2}/) # Probably Ook
206
+ src = src.gsub(/[\r\n]/, ' ')
207
+ program = BrainFuck.ook_to_bf(src)
208
+ elsif src.match(/^[01]+\s*$/m) # Probably Spoon
209
+ program = BrainFuck.spoon_to_bf(src)
210
+ else
211
+ program = src.gsub(/[^\>\<\+\-\.,\[\]]/, '')
212
+ end
213
+ program
214
+ end
215
+
216
+ def matching_brace_position(pointer)
217
+ begin
218
+ pointer += 1
219
+ raise "Bracket mismatch" if pointer >= @program_end
220
+ if @program[pointer] == '['
221
+ pointer = matching_brace_position(pointer) + 1
222
+ end
223
+ end until @program[pointer] == ']'
224
+ pointer
225
+ end
226
+
227
+ end
228
+
@@ -0,0 +1,67 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rbfk}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{Sean McCarthy}]
12
+ s.date = %q{2012-07-06}
13
+ s.description = %q{An interpreter for BrainFuck that also be embedded in your programs}
14
+ s.email = %q{sean@clanmcccarthy.net}
15
+ s.executables = [%q{bf2ook}, %q{rbfk}]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ "LICENSE.txt",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "bin/bf2ook",
26
+ "bin/rbfk",
27
+ "lib/brain_fuck.rb",
28
+ "rbfk.gemspec",
29
+ "samples/a.bf",
30
+ "samples/get_put.bf",
31
+ "samples/hello_world.bf",
32
+ "samples/hello_world.ook",
33
+ "samples/hello_world.spoon",
34
+ "samples/nested_iteration.bf",
35
+ "samples/quine.bf",
36
+ "samples/rot13.bf",
37
+ "samples/text2bf.bf",
38
+ "spec/brainfuck_spec.rb"
39
+ ]
40
+ s.homepage = %q{http://github.com/seandmccarthy/rbfk}
41
+ s.licenses = [%q{MIT}]
42
+ s.require_paths = [%q{lib}]
43
+ s.rubygems_version = %q{1.8.5}
44
+ s.summary = %q{BrainFuck interpreter}
45
+
46
+ if s.respond_to? :specification_version then
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
51
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
52
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
53
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
54
+ else
55
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
56
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
57
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
58
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
59
+ end
60
+ else
61
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
62
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
63
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
64
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
65
+ end
66
+ end
67
+
@@ -0,0 +1 @@
1
+ ++++++[>++++++++++<-]>+++++.>++++++++++.
@@ -0,0 +1 @@
1
+ ,.>++++++++++.
@@ -0,0 +1,21 @@
1
+ +++++ +++++ initialize counter (cell #0) to 10
2
+ [ use loop to set the next four cells to 70/100/30/10
3
+ > +++++ ++ add 7 to cell #1
4
+ > +++++ +++++ add 10 to cell #2
5
+ > +++ add 3 to cell #3
6
+ > + add 1 to cell #4
7
+ <<<< - decrement counter (cell #0)
8
+ ]
9
+ > ++ . print 'H'
10
+ > + . print 'e'
11
+ +++++ ++ . print 'l'
12
+ . print 'l'
13
+ +++ . print 'o'
14
+ > ++ . print ' '
15
+ << +++++ +++++ +++++ . print 'W'
16
+ > . print 'o'
17
+ +++ . print 'r'
18
+ ----- - . print 'l'
19
+ ----- --- . print 'd'
20
+ > + . print '!'
21
+ > . print '\n'
@@ -0,0 +1,20 @@
1
+ Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook. Ook?
2
+ Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
3
+ Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook! Ook! Ook? Ook! Ook. Ook?
4
+ Ook! Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
5
+ Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook. Ook? Ook. Ook. Ook. Ook.
6
+ Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
7
+ Ook? Ook. Ook! Ook! Ook? Ook! Ook. Ook? Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook.
8
+ Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook. Ook. Ook.
9
+ Ook. Ook. Ook. Ook. Ook! Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
10
+ Ook! Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
11
+ Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook! Ook! Ook? Ook!
12
+ Ook. Ook? Ook! Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook. Ook?
13
+ Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook! Ook! Ook? Ook! Ook. Ook?
14
+ Ook! Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook.
15
+ Ook. Ook. Ook! Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
16
+ Ook? Ook. Ook! Ook! Ook? Ook! Ook. Ook? Ook! Ook. Ook. Ook? Ook. Ook? Ook! Ook.
17
+ Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
18
+ Ook! Ook! Ook! Ook! Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
19
+ Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook. Ook! Ook.
20
+ Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook.
@@ -0,0 +1,2 @@
1
+ 11111100100010111111111111011000001101000101001011111111110010001011111111110110000011010100101011111110010100010101110010100101111001000101111111111101100000110100010100111110010001000000000000001100000110100010100110110110110111110010001011111011000001101000101001001000101011100101000000000000000000000101000000000000000000000000000101001001010010100101111111111001010
2
+
@@ -0,0 +1 @@
1
+ ++[>+++++[>++++++<-]<-]>>+++++.>++++++++++.
@@ -0,0 +1 @@
1
+ >>>++++>+>+>+>+>+>+>+>+>+++++>++++>+>+>+>+>+>+>+>+>+++>++>++++++>++++>++>++>+++++++>+++++++>+++>+++>+++++>+++>++++++>++++>+++++>+++>+>+>+>+>+>+>+>+>+++++>+++>+>+>+>+>+>+>+>+>++++>++>++++++>+++>++>++>+++++++>+>++++>+>+>+>+>+++++>+++>++>++>++>++>++>++++>++>++++++>++++>+++++>+++>+++>+++++++>++++>+>++++>++>++++++>+++>++>+++++>++>+++>+>+>++++>+++++>++>+++>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>++++>+++++>++>+++>+>+>++++>+++++>++>+++>++>++++>++++>+>+>+>+>+>+++++>+++>+++>+>+>+>+>+>+>++++>++++>++>++++++>+++>+++++>++>+++>+>+>++++>+++++>++>+++>+>++++>++++>+>+>+>+>+>+>+++++>+++>+++>++>++>++>++>++>++>++>++>++++>++++>++>++++++>+++>++++++>++++++>++++++>++++++>++++++>++++++>++++>++++>++++++>+++>+++>+++>+++++>+++>++++++>++++>+++++>+++++++>++++>++++++>++++++++[>++++++++<-]>--..<<[<]>[<++++++++[<++++++++>-]<--.+>++++[<----->-]>[<<.>+>-]<-[-<++>[-<+++++++++++++++>[-<++>[-<->>+++++[<<++++++>>-]<[-<++>[-<+>>++++++[<<-------->>-]<]]]]]]>>]<<<[<]>[.>]
@@ -0,0 +1,42 @@
1
+ -,+[ Read first character and start outer character
2
+ reading loop
3
+ -[ Skip forward if character is 0
4
+ >>++++[>++++++++<-] Set up divisor (32) for division loop
5
+ (MEMORY LAYOUT: dividend copy remainder divisor
6
+ quotient zero zero)
7
+ <+<-[ Set up dividend (x minus 1) and enter division loop
8
+ >+>+>-[>>>] Increase copy and remainder / reduce divisor /
9
+ Normal case: skip forward
10
+ <[[>+<-]>>+>] Special case: move remainder back to divisor and
11
+ increase quotient
12
+ <<<<<- Decrement dividend
13
+ ] End division loop
14
+ ]>>>[-]+ End skip loop; zero former divisor and reuse space
15
+ for a flag
16
+ >--[-[<->+++[-]]]<[ Zero that flag unless quotient was 2 or 3;
17
+ zero quotient; check flag
18
+ ++++++++++++<[ If flag then set up divisor (13) for second
19
+ division loop
20
+ (MEMORY LAYOUT: zero copy dividend divisor
21
+ remainder quotient zero zero)
22
+ >-[>+>>] Reduce divisor; Normal case: increase remainder
23
+ >[+[<+>-]>+>>] Special case: increase remainder / move it back to
24
+ divisor / increase quotient
25
+ <<<<<- Decrease dividend
26
+ ] End division loop
27
+ >>[<+>-] Add remainder back to divisor to get a useful 13
28
+ >[ Skip forward if quotient was 0
29
+ -[ Decrement quotient and skip forward if quotient
30
+ was 1
31
+ -<<[-]>> Zero quotient and divisor if quotient was 2
32
+ ]<<[<<->>-]>> Zero divisor and subtract 13 from copy if quotient
33
+ was 1
34
+ ]<<[<<+>>-] Zero divisor and add 13 to copy if quotient was 0
35
+ ] End outer skip loop (jump to here if
36
+ ((character minus 1)/32) was not 2 or 3)
37
+ <[-] Clear remainder from first division if second
38
+ division was skipped
39
+ <.[-] Output ROT13ed character from copy and clear it
40
+ <-,+ Read next character
41
+ ] End character reading loop
42
+
@@ -0,0 +1 @@
1
+ ,[>>++++++[-<+++++++>]<+<[->.<]>+++.<++++[->++++<]>.>,]
@@ -0,0 +1,127 @@
1
+ require File.expand_path(File.join('..', '..', 'lib', 'brain_fuck'), __FILE__)
2
+
3
+ describe BrainFuck do
4
+ describe "The Brain Fuck instruction set" do
5
+ before :each do
6
+ @bf = BrainFuck.new(StringIO.new(''))
7
+ end
8
+
9
+ it "should increment the value at the current memory location" do
10
+ @bf.execute('+')
11
+ @bf.memory[0].should == 1
12
+ end
13
+
14
+ it "should decrement the value at the current memory location" do
15
+ @bf.execute('-')
16
+ @bf.memory[0].should == -1
17
+ end
18
+
19
+ it "should increment the data pointer" do
20
+ @bf.execute('>')
21
+ @bf.data_pointer.should == 1
22
+ end
23
+
24
+ it "should decrement the data pointer" do
25
+ @bf.execute('>')
26
+ @bf.data_pointer.should == 1
27
+ @bf.execute('<')
28
+ @bf.data_pointer.should == 0
29
+ end
30
+
31
+ it "should read a char into the current memory location" do
32
+ STDIN.should_receive(:getc).and_return('A')
33
+ @bf.execute(',')
34
+ @bf.memory[0].should == 65
35
+ end
36
+
37
+ it "output the value at the current memory location" do
38
+ @bf.memory[0] = 65
39
+ STDOUT.should_receive(:putc).with(65)
40
+ @bf.execute('.')
41
+ end
42
+
43
+ describe "loops" do
44
+ it "should begin a loop when the current memory value is > 0" do
45
+ @bf.execute('+')
46
+ @bf.execute('[')
47
+ @bf.pointer_stack.should_not be_empty
48
+ end
49
+
50
+ it "should skip the loop when the current memory value is = 0" do
51
+ @bf.should_receive(:matching_brace_position).and_return(1)
52
+ @bf.execute('[')
53
+ @bf.pointer_stack.should == []
54
+ @bf.instruction_pointer.should == 1
55
+ end
56
+
57
+ it "should begin a new iteration when the current memory value is > 0" do
58
+
59
+ # Increment current memory position
60
+ @bf.execute('+')
61
+
62
+ # Start loop
63
+ @bf.instruction_pointer = 1
64
+ @bf.execute('[')
65
+
66
+ # The pointer stack should have (instruction_pointer - 1) pushed on
67
+ @bf.pointer_stack.should == [0]
68
+
69
+ # End of loop, should see memory position > zero, then
70
+ # - pop the last value from pointer_stack
71
+ # - instruction_pointer should be set to this value
72
+ @bf.execute(']')
73
+ @bf.pointer_stack.should == []
74
+ @bf.instruction_pointer.should == 0
75
+ end
76
+
77
+ it "should finish iterating when the current memory value is = 0" do
78
+
79
+ # Increment current memory position
80
+ @bf.execute('+')
81
+
82
+ # Start loop
83
+ @bf.instruction_pointer = 1
84
+ @bf.execute('[')
85
+
86
+ # The pointer stack should have (instruction_pointer - 1) pushed on
87
+ @bf.pointer_stack.should == [0]
88
+
89
+ # Decrement current memory position, making it zero
90
+ @bf.execute('-')
91
+
92
+ # End of loop, should notice memory position is zero, then
93
+ # - pop the last value from pointer_stack
94
+ # - instruction_pointer should (remain) at index for ']' in @program
95
+ @bf.instruction_pointer = 3
96
+ @bf.execute(']')
97
+ @bf.pointer_stack.should == []
98
+ @bf.instruction_pointer.should == 3
99
+ end
100
+
101
+ it "should permit nested loops" do
102
+ pending
103
+ end
104
+
105
+ it "should raise an exception for mismatched braces" do
106
+ lambda { @bf.execute(']') }.should raise_error
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ describe "running programs" do
113
+ it "should indicate when a program execution should end" do
114
+ @bf = BrainFuck.new(StringIO.new(''))
115
+ @bf.ended?.should be_false
116
+ @bf.next_instruction
117
+ @bf.ended?.should be_true
118
+ end
119
+ end
120
+
121
+ describe "Translating and executing other dialects" do
122
+ pending
123
+ #self.ook_to_bf(ook)
124
+ #self.bf_to_ook(bf)
125
+ #self.spoon_to_bf(spoon)
126
+ end
127
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rbfk
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.0.0
6
+ platform: ruby
7
+ authors:
8
+ - Sean McCarthy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-07-06 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 2.8.0
24
+ type: :development
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: rdoc
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: "3.12"
35
+ type: :development
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: bundler
39
+ prerelease: false
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0
46
+ type: :development
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: jeweler
50
+ prerelease: false
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 1.8.3
57
+ type: :development
58
+ version_requirements: *id004
59
+ description: An interpreter for BrainFuck that also be embedded in your programs
60
+ email: sean@clanmcccarthy.net
61
+ executables:
62
+ - bf2ook
63
+ - rbfk
64
+ extensions: []
65
+
66
+ extra_rdoc_files:
67
+ - LICENSE.txt
68
+ - README.rdoc
69
+ files:
70
+ - LICENSE.txt
71
+ - README.rdoc
72
+ - Rakefile
73
+ - VERSION
74
+ - bin/bf2ook
75
+ - bin/rbfk
76
+ - lib/brain_fuck.rb
77
+ - rbfk.gemspec
78
+ - samples/a.bf
79
+ - samples/get_put.bf
80
+ - samples/hello_world.bf
81
+ - samples/hello_world.ook
82
+ - samples/hello_world.spoon
83
+ - samples/nested_iteration.bf
84
+ - samples/quine.bf
85
+ - samples/rot13.bf
86
+ - samples/text2bf.bf
87
+ - spec/brainfuck_spec.rb
88
+ homepage: http://github.com/seandmccarthy/rbfk
89
+ licenses:
90
+ - MIT
91
+ post_install_message:
92
+ rdoc_options: []
93
+
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: "0"
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: "0"
108
+ requirements: []
109
+
110
+ rubyforge_project:
111
+ rubygems_version: 1.8.5
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: BrainFuck interpreter
115
+ test_files: []
116
+