brainclusterfuck 0.0.1 → 0.2.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/.travis.yml +6 -0
- data/Gemfile +1 -1
- data/README.md +4 -0
- data/lib/brainclusterfuck/compile_error.rb +10 -0
- data/lib/brainclusterfuck/compiler.rb +57 -35
- data/lib/brainclusterfuck/error.rb +5 -0
- data/lib/brainclusterfuck/interpreter.rb +84 -0
- data/lib/brainclusterfuck/interpreter_error.rb +4 -0
- data/lib/brainclusterfuck/lexer.rb +2 -2
- data/lib/brainclusterfuck/loop_error.rb +6 -0
- data/lib/brainclusterfuck/memory.rb +33 -0
- data/lib/brainclusterfuck/memory_error.rb +6 -0
- data/lib/brainclusterfuck/opcodes.rb +5 -0
- data/lib/brainclusterfuck/opcodes/base.rb +19 -0
- data/lib/brainclusterfuck/opcodes/loop_base.rb +27 -0
- data/lib/brainclusterfuck/opcodes/loop_end.rb +10 -0
- data/lib/brainclusterfuck/opcodes/loop_end_placeholder.rb +6 -0
- data/lib/brainclusterfuck/opcodes/loop_placeholder.rb +20 -0
- data/lib/brainclusterfuck/opcodes/loop_start.rb +10 -0
- data/lib/brainclusterfuck/opcodes/loop_start_placeholder.rb +6 -0
- data/lib/brainclusterfuck/opcodes/modify_pointer.rb +9 -0
- data/lib/brainclusterfuck/opcodes/modify_value.rb +6 -0
- data/lib/brainclusterfuck/opcodes/modifying_base.rb +27 -0
- data/lib/brainclusterfuck/opcodes/print.rb +13 -0
- data/lib/brainclusterfuck/terminal.rb +4 -0
- data/lib/brainclusterfuck/version.rb +1 -1
- data/spec/compiler_spec.rb +82 -16
- data/spec/integration/basic_loops_spec.rb +55 -0
- data/spec/integration/basic_print_spec.rb +65 -0
- data/spec/integration/boundary_conditions_spec.rb +81 -0
- data/spec/interpreter_spec.rb +154 -0
- data/spec/lexer_spec.rb +2 -2
- data/spec/memory_spec.rb +33 -0
- data/spec/opcodes/base_spec.rb +26 -0
- data/spec/opcodes/loop_end_placeholder_spec.rb +5 -0
- data/spec/opcodes/loop_end_spec.rb +7 -0
- data/spec/opcodes/loop_start_placeholder_spec.rb +5 -0
- data/spec/opcodes/loop_start_spec.rb +7 -0
- data/spec/opcodes/modify_pointer_spec.rb +6 -0
- data/spec/opcodes/modify_value_spec.rb +6 -0
- data/spec/{opcode → opcodes}/print_spec.rb +3 -3
- data/spec/spec_helper.rb +2 -0
- data/spec/support/loop_opcode_shared_behavior.rb +22 -0
- data/spec/support/loop_placeholder_shared_behavior.rb +12 -0
- data/spec/{opcode/modify_value_spec.rb → support/modifying_opcode_shared_behavior.rb} +4 -7
- data/spec/terminal_spec.rb +11 -0
- metadata +60 -20
- data/lib/brainclusterfuck/cells.rb +0 -9
- data/lib/brainclusterfuck/opcode.rb +0 -11
- data/lib/brainclusterfuck/opcode/modify_pointer.rb +0 -27
- data/lib/brainclusterfuck/opcode/modify_value.rb +0 -27
- data/lib/brainclusterfuck/opcode/print.rb +0 -13
- data/lib/brainclusterfuck/vm.rb +0 -11
- data/spec/cells_spec.rb +0 -8
- data/spec/opcode/modify_pointer_spec.rb +0 -37
- data/spec/opcode_spec.rb +0 -10
- data/spec/vm_spec.rb +0 -22
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'brainclusterfuck/opcodes/base'
|
2
|
+
|
3
|
+
module Brainclusterfuck::Opcodes
|
4
|
+
class ModifyingBase < Base
|
5
|
+
attr_reader :modify_by
|
6
|
+
|
7
|
+
def initialize(modify_by, cycles)
|
8
|
+
@modify_by = modify_by.to_i
|
9
|
+
@cycles = cycles.to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
other.class == self.class &&
|
14
|
+
other.modify_by == modify_by &&
|
15
|
+
other.cycles == cycles
|
16
|
+
end
|
17
|
+
|
18
|
+
def can_squeeze_with?(other)
|
19
|
+
other.class == self.class
|
20
|
+
end
|
21
|
+
|
22
|
+
def squeeze_with(other)
|
23
|
+
raise "Cannot squeeze: #{self}, #{other}" unless can_squeeze_with?(other)
|
24
|
+
self.class.new(modify_by + other.modify_by, cycles + other.cycles)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/spec/compiler_spec.rb
CHANGED
@@ -1,31 +1,78 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'brainclusterfuck/compiler'
|
3
|
-
require 'brainclusterfuck/
|
3
|
+
require 'brainclusterfuck/compile_error'
|
4
|
+
require 'brainclusterfuck/opcodes'
|
4
5
|
|
5
6
|
describe Brainclusterfuck::Compiler do
|
6
|
-
|
7
|
-
|
7
|
+
describe 'invalid token streams' do
|
8
|
+
it 'raises if there are no tokens' do
|
9
|
+
expect { Brainclusterfuck::Compiler.new([]) }.to raise_error(Brainclusterfuck::CompileError)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'raises if there are unrecognized tokens' do
|
13
|
+
expect { Brainclusterfuck::Compiler.new([:foo]) }.to raise_error(Brainclusterfuck::CompileError)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'raises if an end loop occurs before an open loop' do
|
17
|
+
expect { Brainclusterfuck::Compiler.new([:loop_end]) }.to raise_error(Brainclusterfuck::PrematurelyTerminatedLoopError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'raises if an open loop does not have an end loop' do
|
21
|
+
expect { Brainclusterfuck::Compiler.new([:loop_start]) }.to raise_error(Brainclusterfuck::UnterminatedLoopError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'raises if there are too many end loops' do
|
25
|
+
expect { Brainclusterfuck::Compiler.new([:loop_start, :loop_end, :loop_end]) }.to raise_error(Brainclusterfuck::PrematurelyTerminatedLoopError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'raises if there are not enough open loops' do
|
29
|
+
expect { Brainclusterfuck::Compiler.new([:loop_start, :loop_start, :loop_end]) }.to raise_error(Brainclusterfuck::UnterminatedLoopError)
|
30
|
+
end
|
8
31
|
end
|
9
32
|
|
10
33
|
it 'converts tokens to the correct operations' do
|
11
|
-
compiler = Brainclusterfuck::Compiler.new([:v_incr, :p_incr, :v_decr, :p_decr])
|
34
|
+
compiler = Brainclusterfuck::Compiler.new([:v_incr, :p_incr, :loop_start, :v_decr, :loop_end, :p_decr, :print])
|
12
35
|
expect(compiler.bytecode).to eq([
|
13
|
-
Brainclusterfuck::
|
14
|
-
Brainclusterfuck::
|
15
|
-
Brainclusterfuck::
|
16
|
-
Brainclusterfuck::
|
36
|
+
Brainclusterfuck::Opcodes::ModifyValue.new(1, 1),
|
37
|
+
Brainclusterfuck::Opcodes::ModifyPointer.new(1, 1),
|
38
|
+
Brainclusterfuck::Opcodes::LoopStart.new(1),
|
39
|
+
Brainclusterfuck::Opcodes::ModifyValue.new(-1, 1),
|
40
|
+
Brainclusterfuck::Opcodes::LoopEnd.new(1),
|
41
|
+
Brainclusterfuck::Opcodes::ModifyPointer.new(-1, 1),
|
42
|
+
Brainclusterfuck::Opcodes::Print.new
|
17
43
|
])
|
18
44
|
end
|
19
45
|
|
20
46
|
describe '#squeeze_operations' do
|
21
|
-
it 'compresses the incr/decr operations' do
|
47
|
+
it 'compresses the lone incr/decr operations' do
|
22
48
|
tokens = (Array.new(8) { :v_incr }).concat(Array.new(3) { :p_decr })
|
23
49
|
compiler = Brainclusterfuck::Compiler.new(tokens)
|
24
50
|
compiler.squeeze_operations!
|
25
51
|
|
26
52
|
expect(compiler.bytecode).to eq([
|
27
|
-
Brainclusterfuck::
|
28
|
-
Brainclusterfuck::
|
53
|
+
Brainclusterfuck::Opcodes::ModifyValue.new(8, 8),
|
54
|
+
Brainclusterfuck::Opcodes::ModifyPointer.new(-3, 3)
|
55
|
+
])
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'merges opposite-direction value modifiers' do
|
59
|
+
tokens = (Array.new(8) { :v_incr }).concat(Array.new(3) { :v_decr })
|
60
|
+
compiler = Brainclusterfuck::Compiler.new(tokens)
|
61
|
+
compiler.squeeze_operations!
|
62
|
+
|
63
|
+
expect(compiler.bytecode).to eq([
|
64
|
+
Brainclusterfuck::Opcodes::ModifyValue.new(5, 11)
|
65
|
+
])
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'does not merge opposite-direction pointer modifiers' do
|
69
|
+
tokens = (Array.new(8) { :p_incr }).concat(Array.new(3) { :p_decr })
|
70
|
+
compiler = Brainclusterfuck::Compiler.new(tokens)
|
71
|
+
compiler.squeeze_operations!
|
72
|
+
|
73
|
+
expect(compiler.bytecode).to eq([
|
74
|
+
Brainclusterfuck::Opcodes::ModifyPointer.new(8, 8),
|
75
|
+
Brainclusterfuck::Opcodes::ModifyPointer.new(-3, 3)
|
29
76
|
])
|
30
77
|
end
|
31
78
|
|
@@ -39,8 +86,9 @@ describe Brainclusterfuck::Compiler do
|
|
39
86
|
compiler.squeeze_operations!
|
40
87
|
|
41
88
|
expect(compiler.bytecode).to eq([
|
42
|
-
Brainclusterfuck::
|
43
|
-
Brainclusterfuck::
|
89
|
+
Brainclusterfuck::Opcodes::ModifyValue.new(2, 4),
|
90
|
+
Brainclusterfuck::Opcodes::ModifyPointer.new(-6, 6),
|
91
|
+
Brainclusterfuck::Opcodes::ModifyPointer.new(2, 2)
|
44
92
|
])
|
45
93
|
end
|
46
94
|
|
@@ -49,9 +97,27 @@ describe Brainclusterfuck::Compiler do
|
|
49
97
|
compiler.squeeze_operations!
|
50
98
|
|
51
99
|
expect(compiler.bytecode).to eq([
|
52
|
-
Brainclusterfuck::
|
53
|
-
Brainclusterfuck::
|
54
|
-
Brainclusterfuck::
|
100
|
+
Brainclusterfuck::Opcodes::Print.new,
|
101
|
+
Brainclusterfuck::Opcodes::Print.new,
|
102
|
+
Brainclusterfuck::Opcodes::Print.new
|
103
|
+
])
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'handles loops properly' do
|
107
|
+
compiler = Brainclusterfuck::Compiler.new([
|
108
|
+
:loop_start,
|
109
|
+
:v_decr,
|
110
|
+
:v_decr,
|
111
|
+
:v_decr,
|
112
|
+
:loop_end
|
113
|
+
])
|
114
|
+
|
115
|
+
compiler.squeeze_operations!
|
116
|
+
|
117
|
+
expect(compiler.bytecode).to eq([
|
118
|
+
Brainclusterfuck::Opcodes::LoopStart.new(1),
|
119
|
+
Brainclusterfuck::Opcodes::ModifyValue.new(-3, 3),
|
120
|
+
Brainclusterfuck::Opcodes::LoopEnd.new(1)
|
55
121
|
])
|
56
122
|
end
|
57
123
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Basic loops' do
|
4
|
+
let(:lexer) { Brainclusterfuck::Lexer.new(program) }
|
5
|
+
let(:compiler) { Brainclusterfuck::Compiler.new(lexer.tokens) }
|
6
|
+
let(:terminal) { Brainclusterfuck::Terminal.new }
|
7
|
+
let(:memory) { Brainclusterfuck::Memory.new(1) }
|
8
|
+
|
9
|
+
let(:interpreter) do
|
10
|
+
Brainclusterfuck::Interpreter.new(
|
11
|
+
bytecode: compiler.bytecode,
|
12
|
+
terminal: terminal,
|
13
|
+
memory: memory
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'the decrement macro' do
|
18
|
+
let(:program) { '[-]' }
|
19
|
+
let(:expected_cycles) do
|
20
|
+
%w(
|
21
|
+
open_loop
|
22
|
+
decr
|
23
|
+
end_loop
|
24
|
+
decr
|
25
|
+
end_loop
|
26
|
+
decr
|
27
|
+
end_loop
|
28
|
+
).size
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.it_clears_the_cell
|
32
|
+
it 'reduces the value of that cell to zero' do
|
33
|
+
memory.modify_value(3)
|
34
|
+
interpreter.step(expected_cycles)
|
35
|
+
expect(interpreter).to be_finished
|
36
|
+
|
37
|
+
expect(interpreter.cycles).to eq(expected_cycles)
|
38
|
+
expect(memory.current_value).to eq(0)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'without optimizing' do
|
43
|
+
it_clears_the_cell
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with optimizing' do
|
47
|
+
before do
|
48
|
+
compiler.squeeze_operations!
|
49
|
+
end
|
50
|
+
|
51
|
+
it_clears_the_cell
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Printing simple text' do
|
4
|
+
let(:lexer) { Brainclusterfuck::Lexer.new(program) }
|
5
|
+
let(:compiler) { Brainclusterfuck::Compiler.new(lexer.tokens) }
|
6
|
+
let(:terminal) { Brainclusterfuck::Terminal.new }
|
7
|
+
let(:memory) { Brainclusterfuck::Memory.new(5) }
|
8
|
+
|
9
|
+
let(:interpreter) do
|
10
|
+
Brainclusterfuck::Interpreter.new(
|
11
|
+
bytecode: compiler.bytecode,
|
12
|
+
terminal: terminal,
|
13
|
+
memory: memory
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'printing "!"' do
|
18
|
+
let(:program) { ('+' * 33) + '.' }
|
19
|
+
|
20
|
+
def self.it_prints
|
21
|
+
it 'sends "!" to the terminal object' do
|
22
|
+
interpreter.step(35)
|
23
|
+
expect(interpreter).to be_finished
|
24
|
+
expect(interpreter.cycles).to eq(34) # 33 increments + 1 print
|
25
|
+
expect(terminal.text).to eq('!')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'without optimizing' do
|
30
|
+
it_prints
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when optimizing' do
|
34
|
+
before do
|
35
|
+
compiler.squeeze_operations!
|
36
|
+
end
|
37
|
+
|
38
|
+
it_prints
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'printing "X" using multiple memory locations' do
|
43
|
+
let(:program) { '>++++++++[<+++++++++++>-]<.' }
|
44
|
+
|
45
|
+
def self.it_prints_x
|
46
|
+
it 'sends "X" to the terminal object' do
|
47
|
+
interpreter.step(500)
|
48
|
+
expect(interpreter).to be_finished
|
49
|
+
expect(terminal.text).to eq('X')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'without optimizing' do
|
54
|
+
it_prints_x
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when optimizing' do
|
58
|
+
before do
|
59
|
+
compiler.squeeze_operations!
|
60
|
+
end
|
61
|
+
|
62
|
+
it_prints_x
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Boundary conditions' do
|
4
|
+
let(:lexer) { Brainclusterfuck::Lexer.new(program) }
|
5
|
+
let(:compiler) { Brainclusterfuck::Compiler.new(lexer.tokens) }
|
6
|
+
let(:terminal) { Brainclusterfuck::Terminal.new }
|
7
|
+
let(:memory) { Brainclusterfuck::Memory.new(5) }
|
8
|
+
|
9
|
+
let(:interpreter) do
|
10
|
+
Brainclusterfuck::Interpreter.new(
|
11
|
+
bytecode: compiler.bytecode,
|
12
|
+
terminal: terminal,
|
13
|
+
memory: memory
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'adjusting pointer out of bounds' do
|
18
|
+
let(:program) { '<>>' }
|
19
|
+
|
20
|
+
def self.it_raises
|
21
|
+
it 'raises an exception' do
|
22
|
+
expect { interpreter.step(3) }.to raise_error(Brainclusterfuck::MemoryError)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'without optimizing' do
|
27
|
+
it_raises
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when optimizing' do
|
31
|
+
before do
|
32
|
+
compiler.squeeze_operations!
|
33
|
+
end
|
34
|
+
|
35
|
+
it_raises
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'overflowing memory value' do
|
40
|
+
def self.it_rolls_over
|
41
|
+
it 'rolls over' do
|
42
|
+
interpreter.step(num_steps)
|
43
|
+
expect(memory.current_value).to eq(255)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'going negative' do
|
48
|
+
let(:program) { '-' }
|
49
|
+
let(:num_steps) { 1 }
|
50
|
+
|
51
|
+
context 'without optimizing' do
|
52
|
+
it_rolls_over
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when optimizing' do
|
56
|
+
before do
|
57
|
+
compiler.squeeze_operations!
|
58
|
+
end
|
59
|
+
|
60
|
+
it_rolls_over
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'going positive' do
|
65
|
+
let(:program) { '+' * num_steps }
|
66
|
+
let(:num_steps) { 255 + 256 }
|
67
|
+
|
68
|
+
context 'without optimizing' do
|
69
|
+
it_rolls_over
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'when optimizing' do
|
73
|
+
before do
|
74
|
+
compiler.squeeze_operations!
|
75
|
+
end
|
76
|
+
|
77
|
+
it_rolls_over
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'brainclusterfuck/interpreter'
|
3
|
+
|
4
|
+
describe Brainclusterfuck::Interpreter do
|
5
|
+
let(:bytecode) { double(:bytecode, size: 5) }
|
6
|
+
let(:terminal) { double(:terminal) }
|
7
|
+
let(:memory) { double(:memory) }
|
8
|
+
|
9
|
+
let(:interpreter) do
|
10
|
+
described_class.new(
|
11
|
+
bytecode: bytecode,
|
12
|
+
terminal: terminal,
|
13
|
+
memory: memory
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'exposes terminal and memory' do
|
18
|
+
expect(interpreter.terminal).to equal(terminal)
|
19
|
+
expect(interpreter.memory).to equal(memory)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'exposes cycles, starting at 0' do
|
23
|
+
expect(interpreter.cycles).to eq(0)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'exposes the instruction pointer, starting at 0' do
|
27
|
+
expect(interpreter.instruction_pointer).to eq(0)
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#step' do
|
31
|
+
it 'raises if step is 0 or negative' do
|
32
|
+
expect { interpreter.step(0) }.to raise_error(ArgumentError)
|
33
|
+
expect { interpreter.step(-1) }.to raise_error(ArgumentError)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'raises an ArgumentError if attempting to execute an unknown opcode' do
|
37
|
+
weird_opcode = Brainclusterfuck::Opcodes::Base.new
|
38
|
+
weird_opcode.stub(cycles: 1)
|
39
|
+
bytecode.should_receive(:[]).with(0).and_return(weird_opcode)
|
40
|
+
|
41
|
+
expect { interpreter.step(1) }.to raise_error(ArgumentError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'does nothing when stepping past a fully-executed program' do
|
45
|
+
bytecode.should_receive(:[]).with(0).and_return(nil)
|
46
|
+
|
47
|
+
interpreter.step(1)
|
48
|
+
|
49
|
+
expect(interpreter).to be_finished
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'will step exactly that number of cycles if possible' do
|
53
|
+
bytecode.should_receive(:[]).with(0).and_return(Brainclusterfuck::Opcodes::ModifyValue.new(1, 1))
|
54
|
+
memory.should_receive(:modify_value).with(1)
|
55
|
+
|
56
|
+
interpreter.step(1)
|
57
|
+
expect(interpreter.cycles).to eq(1)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'will step more than the asked cycles if necessary' do
|
61
|
+
bytecode.should_receive(:[]).with(0).and_return(Brainclusterfuck::Opcodes::ModifyValue.new(1, 5))
|
62
|
+
memory.should_receive(:modify_value).with(1)
|
63
|
+
|
64
|
+
interpreter.step(1)
|
65
|
+
expect(interpreter.cycles).to eq(5)
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'opcodes' do
|
69
|
+
def set_up_bytecode(hash)
|
70
|
+
hash.each do |index, op|
|
71
|
+
bytecode.stub(:[]).with(index).and_return(op)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'handles ModifyValue' do
|
76
|
+
set_up_bytecode(0 => Brainclusterfuck::Opcodes::ModifyValue.new(1, 9))
|
77
|
+
memory.should_receive(:modify_value).with(1)
|
78
|
+
|
79
|
+
interpreter.step(1)
|
80
|
+
expect(interpreter.cycles).to eq(9)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'handles ModifyValue' do
|
84
|
+
set_up_bytecode(0 => Brainclusterfuck::Opcodes::ModifyPointer.new(1, 9))
|
85
|
+
memory.should_receive(:modify_pointer).with(1)
|
86
|
+
|
87
|
+
interpreter.step(1)
|
88
|
+
expect(interpreter.cycles).to eq(9)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'handles Print' do
|
92
|
+
set_up_bytecode(0 => Brainclusterfuck::Opcodes::Print.new)
|
93
|
+
memory.stub(current_char: 'x')
|
94
|
+
terminal.should_receive(:print).with('x')
|
95
|
+
|
96
|
+
interpreter.step(1)
|
97
|
+
expect(interpreter.cycles).to eq(1)
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'loops' do
|
101
|
+
before do
|
102
|
+
set_up_bytecode(
|
103
|
+
0 => Brainclusterfuck::Opcodes::LoopStart.new(1),
|
104
|
+
1 => Brainclusterfuck::Opcodes::ModifyPointer.new(1, 1),
|
105
|
+
2 => Brainclusterfuck::Opcodes::LoopEnd.new(1),
|
106
|
+
3 => Brainclusterfuck::Opcodes::ModifyValue.new(1, 1),
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
describe 'LoopStart' do
|
111
|
+
it 'stays in the loop when the current value is non-zero' do
|
112
|
+
memory.stub(current_value: 1)
|
113
|
+
memory.should_receive(:modify_pointer).with(1)
|
114
|
+
memory.should_not_receive(:modify_value)
|
115
|
+
|
116
|
+
interpreter.step(2)
|
117
|
+
expect(interpreter.cycles).to eq(2)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'jumps past the end loop when the current value is zero' do
|
121
|
+
memory.stub(current_value: 0)
|
122
|
+
memory.should_receive(:modify_value).with(1)
|
123
|
+
memory.should_not_receive(:modify_pointer)
|
124
|
+
|
125
|
+
interpreter.step(2)
|
126
|
+
expect(interpreter.cycles).to eq(2)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'LoopEnd' do
|
131
|
+
it 'goes past the loop when the value is zero' do
|
132
|
+
memory.stub(current_value: 1)
|
133
|
+
memory.should_receive(:modify_pointer).with(1) do
|
134
|
+
memory.stub(current_value: 0)
|
135
|
+
memory.should_receive(:modify_value).with(1)
|
136
|
+
end
|
137
|
+
|
138
|
+
interpreter.step(4)
|
139
|
+
|
140
|
+
expect(interpreter.cycles).to eq(4)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'goes back to the beginning of the loop when the value is non-zero' do
|
144
|
+
memory.stub(current_value: 1)
|
145
|
+
memory.should_receive(:modify_pointer).with(1).twice
|
146
|
+
|
147
|
+
interpreter.step(4)
|
148
|
+
expect(interpreter.cycles).to eq(4)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|