rbfk 1.0.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.
- 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
|
+
|