rbfk 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +20 -0
- data/README.rdoc +43 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/bin/bf2ook +5 -0
- data/bin/rbfk +5 -0
- data/lib/brain_fuck.rb +228 -0
- data/rbfk.gemspec +67 -0
- data/samples/a.bf +1 -0
- data/samples/get_put.bf +1 -0
- data/samples/hello_world.bf +21 -0
- data/samples/hello_world.ook +20 -0
- data/samples/hello_world.spoon +2 -0
- data/samples/nested_iteration.bf +1 -0
- data/samples/quine.bf +1 -0
- data/samples/rot13.bf +42 -0
- data/samples/text2bf.bf +1 -0
- data/spec/brainfuck_spec.rb +127 -0
- metadata +116 -0
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.rdoc
ADDED
@@ -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
|
+
|
data/Rakefile
ADDED
@@ -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
|
data/bin/bf2ook
ADDED
data/bin/rbfk
ADDED
data/lib/brain_fuck.rb
ADDED
@@ -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
|
+
|
data/rbfk.gemspec
ADDED
@@ -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
|
+
|
data/samples/a.bf
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
++++++[>++++++++++<-]>+++++.>++++++++++.
|
data/samples/get_put.bf
ADDED
@@ -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
|
+
++[>+++++[>++++++<-]<-]>>+++++.>++++++++++.
|
data/samples/quine.bf
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
>>>++++>+>+>+>+>+>+>+>+>+++++>++++>+>+>+>+>+>+>+>+>+++>++>++++++>++++>++>++>+++++++>+++++++>+++>+++>+++++>+++>++++++>++++>+++++>+++>+>+>+>+>+>+>+>+>+++++>+++>+>+>+>+>+>+>+>+>++++>++>++++++>+++>++>++>+++++++>+>++++>+>+>+>+>+++++>+++>++>++>++>++>++>++++>++>++++++>++++>+++++>+++>+++>+++++++>++++>+>++++>++>++++++>+++>++>+++++>++>+++>+>+>++++>+++++>++>+++>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>++++>+++++>++>+++>+>+>++++>+++++>++>+++>++>++++>++++>+>+>+>+>+>+++++>+++>+++>+>+>+>+>+>+>++++>++++>++>++++++>+++>+++++>++>+++>+>+>++++>+++++>++>+++>+>++++>++++>+>+>+>+>+>+>+++++>+++>+++>++>++>++>++>++>++>++>++>++++>++++>++>++++++>+++>++++++>++++++>++++++>++++++>++++++>++++++>++++>++++>++++++>+++>+++>+++>+++++>+++>++++++>++++>+++++>+++++++>++++>++++++>++++++++[>++++++++<-]>--..<<[<]>[<++++++++[<++++++++>-]<--.+>++++[<----->-]>[<<.>+>-]<-[-<++>[-<+++++++++++++++>[-<++>[-<->>+++++[<<++++++>>-]<[-<++>[-<+>>++++++[<<-------->>-]<]]]]]]>>]<<<[<]>[.>]
|
data/samples/rot13.bf
ADDED
@@ -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
|
+
|
data/samples/text2bf.bf
ADDED
@@ -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
|
+
|