brainfuck 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/Gemfile.lock +6 -1
- data/Readme.md +9 -16
- data/bin/brainfuck +1 -1
- data/brainfuck.gemspec +1 -0
- data/lib/brainfuck.rb +14 -94
- data/lib/brainfuck/ast.rb +32 -0
- data/lib/brainfuck/interpreter.rb +20 -0
- data/lib/brainfuck/parser.rb +31 -0
- data/lib/brainfuck/stack.rb +51 -0
- data/lib/brainfuck/version.rb +1 -1
- data/spec/{acceptance_spec.rb → acceptance/acceptance_spec.rb} +24 -26
- data/spec/brainfuck/ast_spec.rb +36 -0
- data/spec/brainfuck/interpreter_spec.rb +25 -0
- data/spec/brainfuck/parser_spec.rb +27 -0
- data/spec/brainfuck/stack_spec.rb +81 -0
- data/spec/brainfuck_spec.rb +23 -0
- metadata +36 -11
- data/spec/interpreter_spec.rb +0 -65
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
brainfuck (0.0
|
4
|
+
brainfuck (0.1.0)
|
5
5
|
highline
|
6
|
+
parslet
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: http://rubygems.org/
|
9
10
|
specs:
|
11
|
+
blankslate (2.1.2.3)
|
10
12
|
diff-lcs (1.1.2)
|
11
13
|
highline (1.6.1)
|
14
|
+
parslet (1.0.0)
|
15
|
+
blankslate (~> 2.1.2.3)
|
12
16
|
rspec (2.2.0)
|
13
17
|
rspec-core (~> 2.2)
|
14
18
|
rspec-expectations (~> 2.2)
|
@@ -29,5 +33,6 @@ DEPENDENCIES
|
|
29
33
|
brainfuck!
|
30
34
|
bundler
|
31
35
|
highline
|
36
|
+
parslet
|
32
37
|
rspec
|
33
38
|
simplecov
|
data/Readme.md
CHANGED
@@ -2,22 +2,20 @@
|
|
2
2
|
|
3
3
|
Just another Brainfuck interpreter in Ruby!
|
4
4
|
(If you don't know what Brainfuck is, you definitely
|
5
|
-
[should]
|
5
|
+
[should](http://en.wikipedia.org/wiki/Brainfuck)).
|
6
6
|
|
7
7
|
This interpreter works with MRI 1.8.7, 1.9.2 and JRuby 1.5.5.
|
8
8
|
|
9
|
-
## Known caveats
|
9
|
+
## UPDATE: Known caveats solved since 0.1.0!
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Oh and why are nested loops tricky with this interpreter? I was tired and Civilization 5
|
16
|
-
was installed in my laptop and... You know, forks and pull requests are always welcome! :)
|
11
|
+
Thanks to a complete rewrite using Kaspar Schiess' `parslet` (which you should
|
12
|
+
definitely [check it out](http://github.com/kschiess/parslet)) nested loops
|
13
|
+
work flawlessly. So yes, you can now run that high-security online payment
|
14
|
+
system you wrote in Brainfuck :)
|
17
15
|
|
18
16
|
## Installation and usage
|
19
17
|
|
20
|
-
You just `gem install brainfuck
|
18
|
+
You just `gem install brainfuck` (or `gem 'brainfuck'` in your Gemfile)!
|
21
19
|
|
22
20
|
And then: `brainfuck my_file.bf`
|
23
21
|
|
@@ -25,14 +23,9 @@ You can also require the gem and use inline brainfuck in your ruby scripts like
|
|
25
23
|
|
26
24
|
require 'brainfuck'
|
27
25
|
|
28
|
-
|
29
|
-
interpreter.compile "+++>+++<---"
|
30
|
-
|
31
|
-
interpreter.cells
|
26
|
+
Brainfuck.run "+++>+++<---"
|
32
27
|
# => [0, 3]
|
33
28
|
|
34
|
-
It's very basic, and needs *a lot* of refactoring, but for now... there you go! ;)
|
35
|
-
|
36
29
|
## Note on Patches/Pull Requests
|
37
30
|
|
38
31
|
* Fork the project.
|
@@ -46,4 +39,4 @@ It's very basic, and needs *a lot* of refactoring, but for now... there you go!
|
|
46
39
|
|
47
40
|
## Copyright
|
48
41
|
|
49
|
-
Copyright (c)
|
42
|
+
Copyright (c) 2011 Josep M. Bach. See LICENSE for details.
|
data/bin/brainfuck
CHANGED
data/brainfuck.gemspec
CHANGED
@@ -15,6 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.rubyforge_project = "brainfuck"
|
16
16
|
|
17
17
|
s.add_runtime_dependency "highline"
|
18
|
+
s.add_runtime_dependency "parslet"
|
18
19
|
s.add_development_dependency "rspec"
|
19
20
|
s.add_development_dependency "bundler"
|
20
21
|
s.add_development_dependency "simplecov"
|
data/lib/brainfuck.rb
CHANGED
@@ -1,102 +1,22 @@
|
|
1
|
+
require 'parslet'
|
1
2
|
require 'highline/system_extensions'
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
include HighLine::SystemExtensions
|
6
|
-
|
7
|
-
INSTRUCTIONS = %w{> < + - [ ] . ,}
|
8
|
-
|
9
|
-
attr_accessor :cells
|
10
|
-
attr_reader :pointer
|
11
|
-
attr_reader :code
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
@cells = [0]
|
15
|
-
@pointer = 0
|
16
|
-
@code = nil
|
17
|
-
end
|
18
|
-
|
19
|
-
def current
|
20
|
-
@cells[pointer] ||= 0
|
21
|
-
end
|
22
|
-
def current=(value)
|
23
|
-
@cells[pointer] = (value % 255) rescue 0
|
24
|
-
end
|
4
|
+
require 'brainfuck/stack'
|
5
|
+
require 'brainfuck/ast'
|
25
6
|
|
26
|
-
|
27
|
-
|
28
|
-
initialize_cell_if_nil
|
29
|
-
end
|
30
|
-
def backward
|
31
|
-
@pointer -= 1
|
32
|
-
ensure_pointer_is_above_zero
|
33
|
-
initialize_cell_if_nil
|
34
|
-
end
|
35
|
-
def increase
|
36
|
-
@cells[pointer] = (@cells[pointer] + 1) % 255
|
37
|
-
end
|
38
|
-
def decrease
|
39
|
-
@cells[pointer] = (@cells[pointer] - 1) % 255
|
40
|
-
end
|
41
|
-
|
42
|
-
def compile(code)
|
43
|
-
@code = clean(code)
|
44
|
-
run
|
45
|
-
end
|
7
|
+
require 'brainfuck/parser'
|
8
|
+
require 'brainfuck/interpreter'
|
46
9
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
c = code.dup
|
52
|
-
|
53
|
-
if from && to
|
54
|
-
context = from
|
55
|
-
c = c.slice(from...to)
|
56
|
-
end
|
57
|
-
|
58
|
-
c.each_char do |char|
|
59
|
-
index += 1
|
60
|
-
case char
|
61
|
-
when '>'
|
62
|
-
forward
|
63
|
-
when '<'
|
64
|
-
backward
|
65
|
-
when '+'
|
66
|
-
increase
|
67
|
-
when '-'
|
68
|
-
decrease
|
69
|
-
when '.'
|
70
|
-
stdout.print current.chr
|
71
|
-
when ','
|
72
|
-
self.current = get_character
|
73
|
-
when '['
|
74
|
-
@start = index
|
75
|
-
when ']'
|
76
|
-
@end = index unless @end && @start < @end
|
77
|
-
run(@start, @end) unless current == 0
|
78
|
-
@start = context || nil
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
|
-
def clean(code)
|
86
|
-
code.chars.select {|c| INSTRUCTIONS.include? c }.join
|
87
|
-
end
|
88
|
-
|
89
|
-
def stdout
|
90
|
-
$stdout
|
91
|
-
end
|
92
|
-
|
93
|
-
def initialize_cell_if_nil
|
94
|
-
@cells[pointer] ||= 0
|
95
|
-
end
|
10
|
+
module Brainfuck
|
11
|
+
class << self
|
12
|
+
def run code
|
13
|
+
Interpreter.stack.clear
|
96
14
|
|
97
|
-
|
98
|
-
|
15
|
+
code = Parser.clean code
|
16
|
+
parsed = Parser.new.parse code
|
17
|
+
ast = Interpreter.new.apply parsed
|
18
|
+
ast.each(&:eval)
|
19
|
+
Interpreter.stack.to_a
|
99
20
|
end
|
100
|
-
|
101
21
|
end
|
102
22
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Brainfuck
|
2
|
+
module AST
|
3
|
+
class FwdNode < Struct.new(:stack)
|
4
|
+
def eval; stack.fwd ; end
|
5
|
+
end
|
6
|
+
class BwdNode < Struct.new(:stack)
|
7
|
+
def eval; stack.bwd ; end
|
8
|
+
end
|
9
|
+
|
10
|
+
class IncNode < Struct.new(:stack)
|
11
|
+
def eval; stack.inc ; end
|
12
|
+
end
|
13
|
+
class DecNode < Struct.new(:stack)
|
14
|
+
def eval; stack.dec ; end
|
15
|
+
end
|
16
|
+
class PutsNode < Struct.new(:stack)
|
17
|
+
def eval; stack.puts ; end
|
18
|
+
end
|
19
|
+
class GetsNode < Struct.new(:stack)
|
20
|
+
def eval; stack.gets ; end
|
21
|
+
end
|
22
|
+
class IterationNode < Struct.new(:stack, :exp)
|
23
|
+
def eval
|
24
|
+
until stack.current == 0
|
25
|
+
exp.each do |node|
|
26
|
+
node.eval
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Brainfuck
|
2
|
+
class Interpreter < Parslet::Transform
|
3
|
+
def self.stack
|
4
|
+
@@stack ||= Stack.new
|
5
|
+
end
|
6
|
+
|
7
|
+
rule(:fwd => simple(:fwd)) { AST::FwdNode.new(Interpreter.stack) }
|
8
|
+
rule(:bwd => simple(:bwd)) { AST::BwdNode.new(Interpreter.stack) }
|
9
|
+
|
10
|
+
rule(:inc => simple(:inc)) { AST::IncNode.new(Interpreter.stack) }
|
11
|
+
rule(:dec => simple(:dec)) { AST::DecNode.new(Interpreter.stack) }
|
12
|
+
|
13
|
+
rule(:puts => simple(:puts)) { AST::PutsNode.new(Interpreter.stack) }
|
14
|
+
rule(:gets => simple(:gets)) { AST::GetsNode.new(Interpreter.stack) }
|
15
|
+
|
16
|
+
rule(:iteration => subtree(:iteration)) { AST::IterationNode.new(Interpreter.stack, iteration) }
|
17
|
+
|
18
|
+
rule(:exp => subtree(:exp)) { exp }
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Brainfuck
|
2
|
+
class Parser < Parslet::Parser
|
3
|
+
INSTRUCTIONS = %w{> < + - [ ] . ,}
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def clean code
|
7
|
+
code.chars.select {|c| INSTRUCTIONS.include? c }.join
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
rule(:lparen) { str('[') >> space? }
|
12
|
+
rule(:rparen) { str(']') >> space? }
|
13
|
+
|
14
|
+
rule(:space) { match('\s').repeat(1) }
|
15
|
+
rule(:space?) { space.maybe }
|
16
|
+
|
17
|
+
rule(:fwd) { str('>') >> space? }
|
18
|
+
rule(:bwd) { str('<') >> space? }
|
19
|
+
|
20
|
+
rule(:inc) { str('+') >> space? }
|
21
|
+
rule(:dec) { str('-') >> space? }
|
22
|
+
|
23
|
+
rule(:puts) { str('.') >> space? }
|
24
|
+
rule(:gets) { str(',') >> space? }
|
25
|
+
|
26
|
+
rule(:iteration) { lparen >> expression >> rparen }
|
27
|
+
|
28
|
+
rule(:expression) { (iteration.as(:iteration) | fwd.as(:fwd) | bwd.as(:bwd) | inc.as(:inc) | dec.as(:dec) | puts.as(:puts) | gets.as(:gets)).repeat.as(:exp) }
|
29
|
+
root :expression
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Brainfuck
|
2
|
+
class Stack
|
3
|
+
include HighLine::SystemExtensions
|
4
|
+
|
5
|
+
attr_reader :current
|
6
|
+
def initialize
|
7
|
+
@pointer = 0
|
8
|
+
@stack = [0]
|
9
|
+
end
|
10
|
+
def current
|
11
|
+
@stack[@pointer]
|
12
|
+
end
|
13
|
+
def fwd
|
14
|
+
@pointer += 1
|
15
|
+
initialize_cell_if_nil
|
16
|
+
end
|
17
|
+
def bwd
|
18
|
+
@pointer -= 1
|
19
|
+
ensure_pointer_is_above_zero
|
20
|
+
initialize_cell_if_nil
|
21
|
+
end
|
22
|
+
def inc
|
23
|
+
@stack[@pointer] = (current + 1) % 255
|
24
|
+
end
|
25
|
+
def dec
|
26
|
+
@stack[@pointer] = (current - 1) % 255
|
27
|
+
end
|
28
|
+
def puts
|
29
|
+
$stdout.print current.chr
|
30
|
+
end
|
31
|
+
def gets
|
32
|
+
@stack[@pointer] = (get_character % 255) rescue 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_a
|
36
|
+
@stack
|
37
|
+
end
|
38
|
+
|
39
|
+
def clear
|
40
|
+
initialize
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def initialize_cell_if_nil
|
45
|
+
@stack[@pointer] ||= 0
|
46
|
+
end
|
47
|
+
def ensure_pointer_is_above_zero
|
48
|
+
raise "Tried to access cell #{@pointer}." if @pointer < 0
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/brainfuck/version.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module Brainfuck
|
4
|
-
describe
|
4
|
+
describe "Acceptance specs" do
|
5
|
+
|
6
|
+
subject { Brainfuck }
|
5
7
|
|
6
8
|
describe "without loops nor user input" do
|
7
9
|
let(:code) do
|
@@ -10,17 +12,11 @@ module Brainfuck
|
|
10
12
|
EOS
|
11
13
|
end
|
12
14
|
it "sets two cells to 2 and 1" do
|
13
|
-
subject.
|
14
|
-
subject.compile(code)
|
15
|
-
subject.cells.should == [2,1]
|
15
|
+
subject.run(code).should == [2,1]
|
16
16
|
end
|
17
17
|
it "prints 2 and 1" do
|
18
|
-
|
19
|
-
subject.
|
20
|
-
output.should_receive(:print).with(1.chr).ordered
|
21
|
-
output.should_receive(:print).with(2.chr).ordered
|
22
|
-
|
23
|
-
subject.compile(code)
|
18
|
+
$stdout.should_receive(:print).twice
|
19
|
+
subject.run code
|
24
20
|
end
|
25
21
|
end
|
26
22
|
|
@@ -31,11 +27,12 @@ module Brainfuck
|
|
31
27
|
EOS
|
32
28
|
end
|
33
29
|
it "sets the first cell to a + 4" do
|
34
|
-
|
35
|
-
|
30
|
+
stack = Stack.new
|
31
|
+
Interpreter.stub(:stack).and_return stack
|
36
32
|
|
37
|
-
|
38
|
-
|
33
|
+
stack.should_receive(:get_character).once.and_return 97
|
34
|
+
|
35
|
+
subject.run(code).should == [101]
|
39
36
|
end
|
40
37
|
end
|
41
38
|
|
@@ -46,29 +43,30 @@ module Brainfuck
|
|
46
43
|
EOS
|
47
44
|
end
|
48
45
|
it "runs the loop 4 times" do
|
49
|
-
subject.
|
50
|
-
subject.current.should == 1
|
46
|
+
subject.run(code).should == [1]
|
51
47
|
end
|
52
48
|
end
|
53
49
|
|
54
50
|
describe "cell hopping examples" do
|
55
51
|
|
56
52
|
it "transfers the content from one cell to another" do
|
57
|
-
subject.
|
58
|
-
subject.compile("[>+<-]")
|
59
|
-
subject.cells.should == [0,10]
|
53
|
+
subject.run("++++++++++ [>+<-]").should == [0,10]
|
60
54
|
end
|
61
55
|
|
62
56
|
it "transfers the content from one cell to the third" do
|
63
|
-
subject.
|
64
|
-
subject.compile("[>+<-]>[>+<-]")
|
65
|
-
subject.cells.should == [0,0,10]
|
57
|
+
subject.run("++++++++++ [>+<-]>[>+<-]").should == [0,0,10]
|
66
58
|
end
|
67
59
|
|
68
60
|
it "transfers the content from one cell to the third and back to the second" do
|
69
|
-
subject.
|
70
|
-
|
71
|
-
|
61
|
+
subject.run("++++++++++ [>+<-]>[>+<-]>[<+>-]").should == [0,10,0]
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "nested loop examples" do
|
67
|
+
|
68
|
+
it "work flawlessly" do
|
69
|
+
subject.run("[++++++++++[-]+-+-]").should == [0]
|
72
70
|
end
|
73
71
|
|
74
72
|
end
|
@@ -76,7 +74,7 @@ module Brainfuck
|
|
76
74
|
describe "hello world" do
|
77
75
|
|
78
76
|
it "displays hello world" do
|
79
|
-
subject.
|
77
|
+
subject.run <<-EOS
|
80
78
|
+++++ +++++
|
81
79
|
[
|
82
80
|
> +++++ ++
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Brainfuck
|
4
|
+
describe AST do
|
5
|
+
let(:stack) { double('stack') }
|
6
|
+
|
7
|
+
%w{fwd bwd inc dec puts gets}.each do |node|
|
8
|
+
describe "AST::#{node.capitalize}Node" do
|
9
|
+
subject { eval("AST::#{node.capitalize}Node").new stack }
|
10
|
+
describe "#eval" do
|
11
|
+
it 'calls stack##{node}' do
|
12
|
+
subject.stack.should_receive(node)
|
13
|
+
subject.eval
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe AST::IterationNode do
|
20
|
+
let(:nodes) do
|
21
|
+
[ double('node'), double('node2') ]
|
22
|
+
end
|
23
|
+
subject { AST::IterationNode.new stack, nodes }
|
24
|
+
describe "#eval" do
|
25
|
+
it 'evaluates the expression until the stack cell is 0' do
|
26
|
+
subject.stack.stub(:current).and_return 3, 2, 1, 0
|
27
|
+
nodes.each do |node|
|
28
|
+
node.should_receive(:eval).exactly(3).times
|
29
|
+
end
|
30
|
+
subject.eval
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Brainfuck
|
4
|
+
describe Interpreter do
|
5
|
+
|
6
|
+
describe ".stack" do
|
7
|
+
it 'returns a new or cached Stack' do
|
8
|
+
stack = Interpreter.stack
|
9
|
+
stack.should be_kind_of(Stack)
|
10
|
+
Interpreter.stack.should === stack
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "rules" do
|
15
|
+
%w{fwd bwd inc dec puts gets iteration exp}.each do |rule|
|
16
|
+
it 'implements a rule for :#{rule} node' do
|
17
|
+
subject.rules.map(&:first).map do |pattern|
|
18
|
+
pattern.instance_variable_get(:@pattern)
|
19
|
+
end.map(&:keys).flatten.should include(:"#{rule}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Brainfuck
|
4
|
+
describe Parser do
|
5
|
+
|
6
|
+
describe "INSTRUCTIONS constant" do
|
7
|
+
it 'returns valid symbols' do
|
8
|
+
Parser::INSTRUCTIONS.should == %w{> < + - [ ] . ,}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe ".clean" do
|
13
|
+
it 'cleans all non-valid symbols from a string' do
|
14
|
+
Parser.clean(">3< 223+fn - ()()[r23-] .bdn*& ,").should == '><+-[-].,'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "rules" do
|
19
|
+
%w{lparen rparen space space? fwd bwd inc dec puts gets iteration expression}.each do |rule|
|
20
|
+
it 'implements a rule for :#{rule} node' do
|
21
|
+
subject.should respond_to(:"#{rule}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Brainfuck
|
4
|
+
describe Stack do
|
5
|
+
|
6
|
+
describe "#current" do
|
7
|
+
it 'returns the current value of the stack' do
|
8
|
+
subject.current.should == 0
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#fwd" do
|
13
|
+
it 'advanced the pointer' do
|
14
|
+
subject.fwd
|
15
|
+
subject.instance_variable_get(:@pointer).should == 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#bwd" do
|
20
|
+
it 'decreases the pointer' do
|
21
|
+
subject.instance_variable_set(:@pointer, 4)
|
22
|
+
subject.bwd
|
23
|
+
subject.instance_variable_get(:@pointer).should == 3
|
24
|
+
end
|
25
|
+
context "if the pointer is trying to get below zero" do
|
26
|
+
it 'raises' do
|
27
|
+
subject.instance_variable_set(:@pointer, 0)
|
28
|
+
expect {
|
29
|
+
subject.bwd
|
30
|
+
}.to raise_error(RuntimeError, "Tried to access cell -1.")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#inc" do
|
36
|
+
it 'increases the current cell' do
|
37
|
+
subject.instance_variable_set(:@stack, [4])
|
38
|
+
subject.inc
|
39
|
+
subject.current.should == 5
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#dec" do
|
44
|
+
it 'decreases the current cell' do
|
45
|
+
subject.instance_variable_set(:@stack, [4])
|
46
|
+
subject.dec
|
47
|
+
subject.current.should == 3
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#puts" do
|
52
|
+
it 'prints the current character' do
|
53
|
+
subject.stub_chain('current.chr').and_return 'A'
|
54
|
+
$stdout.should_receive(:print).with 'A'
|
55
|
+
subject.puts
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#gets" do
|
60
|
+
it 'prints the current character' do
|
61
|
+
subject.should_receive(:get_character).and_return 97
|
62
|
+
subject.gets
|
63
|
+
subject.current.should == 97
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#to_a" do
|
68
|
+
it 'returns the stack array' do
|
69
|
+
subject.to_a.should == [0]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#clear" do
|
74
|
+
it 'clears the stack' do
|
75
|
+
subject.should_receive(:initialize)
|
76
|
+
subject.clear
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brainfuck do
|
4
|
+
|
5
|
+
describe ".run" do
|
6
|
+
it 'runs the code and returns the stack' do
|
7
|
+
code = double('code')
|
8
|
+
parsed = double('parsed')
|
9
|
+
ast = [double('node'), double('node')]
|
10
|
+
|
11
|
+
Brainfuck::Interpreter.stack.should_receive(:clear)
|
12
|
+
|
13
|
+
Brainfuck::Parser.should_receive(:clean).with("code").and_return(code)
|
14
|
+
Brainfuck::Parser.stub_chain('new.parse').with(code).and_return parsed
|
15
|
+
Brainfuck::Interpreter.stub_chain('new.apply').with(parsed).and_return ast
|
16
|
+
|
17
|
+
ast.each { |n| n.should_receive(:eval) }
|
18
|
+
|
19
|
+
subject.run("code").should === Brainfuck::Interpreter.stack.to_a
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 0
|
8
7
|
- 1
|
9
|
-
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Josep M. Bach
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date:
|
17
|
+
date: 2011-01-12 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -31,7 +31,7 @@ dependencies:
|
|
31
31
|
type: :runtime
|
32
32
|
version_requirements: *id001
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: parslet
|
35
35
|
prerelease: false
|
36
36
|
requirement: &id002 !ruby/object:Gem::Requirement
|
37
37
|
none: false
|
@@ -41,10 +41,10 @@ dependencies:
|
|
41
41
|
segments:
|
42
42
|
- 0
|
43
43
|
version: "0"
|
44
|
-
type: :
|
44
|
+
type: :runtime
|
45
45
|
version_requirements: *id002
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
47
|
+
name: rspec
|
48
48
|
prerelease: false
|
49
49
|
requirement: &id003 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
@@ -57,7 +57,7 @@ dependencies:
|
|
57
57
|
type: :development
|
58
58
|
version_requirements: *id003
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
|
-
name:
|
60
|
+
name: bundler
|
61
61
|
prerelease: false
|
62
62
|
requirement: &id004 !ruby/object:Gem::Requirement
|
63
63
|
none: false
|
@@ -69,6 +69,19 @@ dependencies:
|
|
69
69
|
version: "0"
|
70
70
|
type: :development
|
71
71
|
version_requirements: *id004
|
72
|
+
- !ruby/object:Gem::Dependency
|
73
|
+
name: simplecov
|
74
|
+
prerelease: false
|
75
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
type: :development
|
84
|
+
version_requirements: *id005
|
72
85
|
description: Another Brainfuck interpreter in Ruby
|
73
86
|
email:
|
74
87
|
- josep.m.bach@gmail.com
|
@@ -88,9 +101,17 @@ files:
|
|
88
101
|
- brainfuck.gemspec
|
89
102
|
- examples/hello_world.bf
|
90
103
|
- lib/brainfuck.rb
|
104
|
+
- lib/brainfuck/ast.rb
|
105
|
+
- lib/brainfuck/interpreter.rb
|
106
|
+
- lib/brainfuck/parser.rb
|
107
|
+
- lib/brainfuck/stack.rb
|
91
108
|
- lib/brainfuck/version.rb
|
92
|
-
- spec/acceptance_spec.rb
|
93
|
-
- spec/
|
109
|
+
- spec/acceptance/acceptance_spec.rb
|
110
|
+
- spec/brainfuck/ast_spec.rb
|
111
|
+
- spec/brainfuck/interpreter_spec.rb
|
112
|
+
- spec/brainfuck/parser_spec.rb
|
113
|
+
- spec/brainfuck/stack_spec.rb
|
114
|
+
- spec/brainfuck_spec.rb
|
94
115
|
- spec/spec_helper.rb
|
95
116
|
has_rdoc: true
|
96
117
|
homepage: http://github.com/txus/brainfuck
|
@@ -125,6 +146,10 @@ signing_key:
|
|
125
146
|
specification_version: 3
|
126
147
|
summary: Another Brainfuck interpreter in Ruby
|
127
148
|
test_files:
|
128
|
-
- spec/acceptance_spec.rb
|
129
|
-
- spec/
|
149
|
+
- spec/acceptance/acceptance_spec.rb
|
150
|
+
- spec/brainfuck/ast_spec.rb
|
151
|
+
- spec/brainfuck/interpreter_spec.rb
|
152
|
+
- spec/brainfuck/parser_spec.rb
|
153
|
+
- spec/brainfuck/stack_spec.rb
|
154
|
+
- spec/brainfuck_spec.rb
|
130
155
|
- spec/spec_helper.rb
|
data/spec/interpreter_spec.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Brainfuck
|
4
|
-
describe Interpreter do
|
5
|
-
subject { Interpreter.new }
|
6
|
-
|
7
|
-
it "initializes with an array of cells with a single 0 element" do
|
8
|
-
subject.cells.should == [0]
|
9
|
-
end
|
10
|
-
|
11
|
-
describe "#clean" do
|
12
|
-
it "cleans the code from comments and whitespace" do
|
13
|
-
subject.send(:clean, " < > h - + ..,hb [lk]f").should == '<>-+..,[]'
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe "#compile" do
|
18
|
-
it "cleans the code first and runs it second" do
|
19
|
-
code = double('code')
|
20
|
-
subject.should_receive(:clean).once.with(code).ordered.and_return("")
|
21
|
-
subject.should_receive(:run).once
|
22
|
-
|
23
|
-
subject.compile(code)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
describe "#run" do
|
28
|
-
it "converts > into forward" do
|
29
|
-
subject.stub(:code).and_return ">"
|
30
|
-
subject.should_receive(:forward).once
|
31
|
-
subject.run
|
32
|
-
end
|
33
|
-
it "converts < into backward" do
|
34
|
-
subject.stub(:code).and_return "<"
|
35
|
-
subject.should_receive(:backward).once
|
36
|
-
subject.run
|
37
|
-
end
|
38
|
-
it "converts + into increase" do
|
39
|
-
subject.stub(:code).and_return "+"
|
40
|
-
subject.should_receive(:increase).once
|
41
|
-
subject.run
|
42
|
-
end
|
43
|
-
it "converts - into decrease" do
|
44
|
-
subject.stub(:code).and_return "-"
|
45
|
-
subject.should_receive(:decrease).once
|
46
|
-
subject.run
|
47
|
-
end
|
48
|
-
it "converts , into get_character" do
|
49
|
-
subject.stub(:code).and_return ","
|
50
|
-
subject.should_receive(:get_character).once
|
51
|
-
subject.run
|
52
|
-
end
|
53
|
-
it "converts . into stdout.print" do
|
54
|
-
subject.stub(:code).and_return "."
|
55
|
-
output = double('output')
|
56
|
-
subject.should_receive(:stdout).once.and_return(output)
|
57
|
-
subject.should_receive(:current).once.and_return(117)
|
58
|
-
output.should_receive(:print).once
|
59
|
-
|
60
|
-
subject.run
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
65
|
-
end
|