rpiet 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +12 -0
- data/bin/color_wheel +84 -0
- data/bin/image_gen +39 -0
- data/bin/rpiet +68 -11
- data/images/counter.txt +7 -0
- data/lib/rpiet/asg/graph_interpreter.rb +39 -0
- data/lib/rpiet/asg/parser.rb +156 -0
- data/lib/rpiet/asg/visitor.rb +66 -0
- data/lib/rpiet/asg.rb +336 -0
- data/lib/rpiet/codel_chooser.rb +32 -4
- data/lib/rpiet/color.rb +70 -25
- data/lib/rpiet/cycle.rb +18 -7
- data/lib/rpiet/debugger/debugger.rb +298 -0
- data/lib/rpiet/debugger/stylesheet.css +88 -0
- data/lib/rpiet/direction_pointer.rb +49 -18
- data/lib/rpiet/event_handler.rb +62 -7
- data/lib/rpiet/group.rb +25 -8
- data/lib/rpiet/image/ascii_image.rb +8 -20
- data/lib/rpiet/image/image.rb +8 -3
- data/lib/rpiet/image/url_image.rb +28 -14
- data/lib/rpiet/interpreter.rb +72 -72
- data/lib/rpiet/ir/assembler.rb +87 -0
- data/lib/rpiet/ir/builder.rb +255 -0
- data/lib/rpiet/ir/cfg.rb +494 -0
- data/lib/rpiet/ir/instructions.rb +536 -0
- data/lib/rpiet/ir/ir_cfg_interpreter.rb +23 -0
- data/lib/rpiet/ir/ir_interpreter.rb +101 -0
- data/lib/rpiet/ir/ir_native_interpreter.rb +77 -0
- data/lib/rpiet/ir/jruby_backend.rb +279 -0
- data/lib/rpiet/ir/operands.rb +28 -0
- data/lib/rpiet/ir/passes/data_flow_problem.rb +32 -0
- data/lib/rpiet/ir/passes/flow_graph_node.rb +76 -0
- data/lib/rpiet/ir/passes/peephole.rb +214 -0
- data/lib/rpiet/ir/passes/push_pop_elimination_pass.rb +112 -0
- data/lib/rpiet/live_machine_state.rb +15 -0
- data/lib/rpiet/machine.rb +62 -32
- data/lib/rpiet/source.rb +83 -0
- data/lib/rpiet/version.rb +1 -1
- data/lib/rpiet.rb +2 -2
- data/rpiet.gemspec +19 -0
- data/spec/asg/visitor_spec.rb +41 -0
- data/spec/cycle_spec.rb +34 -34
- data/spec/direction_pointer_spec.rb +33 -6
- data/spec/group_spec.rb +73 -48
- data/spec/interpreter_spec.rb +161 -12
- data/spec/ir/assembler_spec.rb +122 -0
- data/spec/ir/builder_spec.rb +20 -0
- data/spec/ir/cfg_spec.rb +151 -0
- data/spec/ir/ir_interpreter_spec.rb +102 -0
- data/spec/ir/passes/push_pop_elimination_pass_spec.rb +34 -0
- data/spec/machine_spec.rb +5 -3
- data/spec/source_spec.rb +69 -0
- data/spec/spec_helper.rb +78 -0
- metadata +54 -16
- data/images/nfib.png +0 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
require_relative 'data_flow_problem'
|
2
|
+
require_relative 'flow_graph_node'
|
3
|
+
|
4
|
+
module RPiet
|
5
|
+
module IR
|
6
|
+
module Passes
|
7
|
+
class PushInfo
|
8
|
+
attr_reader :bb, :instr
|
9
|
+
def initialize(bb, instr)
|
10
|
+
@bb, @instr = bb, instr
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
other && operand == other.operand
|
15
|
+
end
|
16
|
+
|
17
|
+
def operand = instr&.operand
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"#{bb.label}:#{instr}"
|
21
|
+
end
|
22
|
+
alias inspect to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
class PushPopEliminationNode < FlowGraphNode
|
26
|
+
attr_reader :ins, :outs
|
27
|
+
TOP, BOTTOM = nil, PushInfo.new(nil, nil)
|
28
|
+
|
29
|
+
def initialize(problem, bb)
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
def replace_instr(instr, new_instr)
|
34
|
+
index = bb.instrs.index(instr)
|
35
|
+
bb.instrs[index] = new_instr
|
36
|
+
end
|
37
|
+
|
38
|
+
def apply_transfer_function(instr)
|
39
|
+
if instr.operation == :push
|
40
|
+
@ins ||= []
|
41
|
+
@ins << PushInfo.new(bb, instr)
|
42
|
+
elsif instr.operation == :pop
|
43
|
+
push = @ins.pop # Can ASG create invalid instrs combos of push/pop?
|
44
|
+
if push != TOP && push != BOTTOM
|
45
|
+
puts "replace pop #{instr} with #{push.instr} in #{push.bb.label}" if debug
|
46
|
+
replace_instr(instr, Instructions::CopyInstr.new(instr.result, push.operand))
|
47
|
+
@remove_instrs << [push.bb, push.instr]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def assign_outs
|
53
|
+
puts "assign_outs: #{bb.label}" if debug
|
54
|
+
puts "ins: #{@ins}" if debug
|
55
|
+
@remove_instrs.each { |bb, instr| bb.instrs.delete(instr) }
|
56
|
+
@outs = @ins.dup
|
57
|
+
puts "outs: #{@outs}" if debug
|
58
|
+
end
|
59
|
+
|
60
|
+
def compute_meet(other)
|
61
|
+
if @ins.nil?
|
62
|
+
puts "initial meet: #{self} +++ #{other.outs.dup}" if debug
|
63
|
+
@ins = other.outs.dup
|
64
|
+
return
|
65
|
+
end
|
66
|
+
|
67
|
+
puts "continued meet: #{self} +++ #{other}" if debug
|
68
|
+
oins = other.outs
|
69
|
+
|
70
|
+
# if one branch has more pushes recorded than another branch then obviously
|
71
|
+
# all branches do not have a push to eliminate. We truncate to shortest of n
|
72
|
+
# last pushes.
|
73
|
+
if @ins.size > oins.size
|
74
|
+
@ins = @ins[-oins.size..-1]
|
75
|
+
elsif @ins.size < oins.size
|
76
|
+
oins = oins[-@ins.size..-1]
|
77
|
+
end
|
78
|
+
|
79
|
+
@ins.zip(oins).each_with_index do |(joined_in, other_in), index|
|
80
|
+
if joined_in != other_in
|
81
|
+
if other_in != TOP # Any mismatched value lowers to unsolved
|
82
|
+
@ins[index] = BOTTOM
|
83
|
+
elsif joined_in == TOP # if meet has nothing yet then we can give it incoming value
|
84
|
+
@ins[index] = other_in # Note: I doubt this is possible for this pass
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def solution_changed?
|
91
|
+
!@remove_instrs.empty?
|
92
|
+
end
|
93
|
+
|
94
|
+
def solution_init
|
95
|
+
@remove_instrs = []
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_s
|
99
|
+
"#{bb.label}\n#{bb.instrs.map {|i| i.disasm}.join("\n")}\nins: #{@ins}\nouts: #{@outs}"
|
100
|
+
end
|
101
|
+
alias inspect to_s
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
class PushPopEliminationProblem < DataFlowProblem
|
106
|
+
def initialize(cfg)
|
107
|
+
super(cfg, PushPopEliminationNode)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RPiet
|
2
|
+
module LiveMachineState
|
3
|
+
attr_reader :stack, :event_handler
|
4
|
+
attr_accessor :dp, :cc, :input, :output
|
5
|
+
|
6
|
+
def reset_machine
|
7
|
+
@stack, @dp, @cc, @input, @output = [], DirectionPointer.new, CodelChooser.new, $stdin, $stdout
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
"dp: #{dp}, cc: #{cc}, st: #{stack}"
|
12
|
+
end
|
13
|
+
alias :to_s :inspect
|
14
|
+
end
|
15
|
+
end
|
data/lib/rpiet/machine.rb
CHANGED
@@ -1,18 +1,25 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative 'direction_pointer'
|
2
|
+
require_relative 'codel_chooser'
|
3
|
+
require_relative 'live_machine_state'
|
3
4
|
|
4
5
|
module RPiet
|
6
|
+
# Using the value npiet decided to use if division by zero happens.
|
7
|
+
DIV_BY_ZERO_VALUE = 99999999
|
8
|
+
|
5
9
|
##
|
6
10
|
# This is a simple piet runtime to be controled by interp.
|
7
11
|
#
|
8
12
|
# dp - Direction Pointer (right, down, left, up)
|
9
13
|
# cc - Codel Chooser (left, right)
|
10
14
|
class Machine
|
11
|
-
|
15
|
+
##
|
16
|
+
# Each group's size represents a block value which can be used by the push operation.
|
12
17
|
attr_accessor :block_value
|
13
18
|
|
19
|
+
include LiveMachineState
|
20
|
+
|
14
21
|
def initialize
|
15
|
-
|
22
|
+
reset_machine
|
16
23
|
@block_value = 1
|
17
24
|
end
|
18
25
|
|
@@ -25,35 +32,53 @@ module RPiet
|
|
25
32
|
##
|
26
33
|
# Change either codel chooser or direction pointer to try and look
|
27
34
|
# at a different codel.
|
28
|
-
def orient_elsewhere(
|
29
|
-
if
|
35
|
+
def orient_elsewhere(attempt)
|
36
|
+
if attempt.even?
|
30
37
|
dp.rotate!
|
31
38
|
else
|
32
39
|
cc.switch!
|
33
40
|
end
|
34
41
|
end
|
35
42
|
|
36
|
-
def noop
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def
|
41
|
-
|
42
|
-
def
|
43
|
-
def
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
def
|
48
|
-
|
49
|
-
def
|
50
|
-
|
51
|
-
def
|
52
|
-
def
|
53
|
-
|
54
|
-
def
|
43
|
+
def noop
|
44
|
+
end
|
45
|
+
|
46
|
+
def push = @stack << @block_value
|
47
|
+
def pop = @stack.pop
|
48
|
+
|
49
|
+
def add = math_op :+
|
50
|
+
def sub = math_op :-
|
51
|
+
def mult = math_op :*
|
52
|
+
|
53
|
+
# Note: Following npiet's div by zero value.
|
54
|
+
def div = bin_op { |a, b| @stack << (b == 0 ? DIV_BY_ZERO_VALUE : a / b) }
|
55
|
+
|
56
|
+
def mod = math_op :%
|
57
|
+
|
58
|
+
def gtr = bin_op { |a, b| @stack << (a > b ? 1 : 0) }
|
59
|
+
def not = unary_op { |top| @stack << (!top || top == 0 ? 1 : 0) }
|
60
|
+
|
61
|
+
def dup
|
62
|
+
@stack << @stack[-1] if @stack[-1]
|
63
|
+
end
|
64
|
+
|
65
|
+
def nout = unary_op { |top| print top }
|
66
|
+
def cout = unary_op { |top| print top.chr }
|
67
|
+
def pntr = unary_op { |top| @dp.rotate! top }
|
68
|
+
def swch = unary_op { |top| @cc.switch! top }
|
69
|
+
|
70
|
+
def nin
|
71
|
+
output.puts "Enter an integer: "
|
72
|
+
@stack << input.gets.to_i
|
73
|
+
end
|
74
|
+
|
75
|
+
def cin
|
76
|
+
output.write "> "
|
77
|
+
@stack << input.read(1).ord
|
78
|
+
end
|
79
|
+
|
55
80
|
def roll
|
56
|
-
bin_op do |depth, num|
|
81
|
+
bin_op do |depth, num|
|
57
82
|
num %= depth
|
58
83
|
return if depth <= 0 || num == 0
|
59
84
|
if num > 0
|
@@ -65,17 +90,22 @@ module RPiet
|
|
65
90
|
end
|
66
91
|
|
67
92
|
def inspect
|
68
|
-
|
93
|
+
super + ", bv: #@block_value"
|
69
94
|
end
|
70
95
|
alias :to_s :inspect
|
71
96
|
|
72
97
|
##
|
73
98
|
# Execute the operation represented by the two colors
|
74
99
|
def execute(color1, color2)
|
100
|
+
operation = calculate_operation(color1, color2)
|
101
|
+
__send__(operation)
|
102
|
+
operation
|
103
|
+
end
|
104
|
+
|
105
|
+
def calculate_operation(color1, color2)
|
75
106
|
dh = color1.hue.delta color2.hue
|
76
107
|
dd = color1.lightness.delta color2.lightness
|
77
|
-
|
78
|
-
__send__(operation)
|
108
|
+
OPERATION[dh][dd]
|
79
109
|
end
|
80
110
|
|
81
111
|
private
|
@@ -85,7 +115,7 @@ module RPiet
|
|
85
115
|
end
|
86
116
|
|
87
117
|
def unary_op(&block)
|
88
|
-
return
|
118
|
+
return if @stack.length < 1
|
89
119
|
block.call(@stack.pop)
|
90
120
|
end
|
91
121
|
|
@@ -105,7 +135,7 @@ module RPiet
|
|
105
135
|
[:add, :sub, :mult],
|
106
136
|
[:div, :mod, :not],
|
107
137
|
[:gtr, :pntr, :swch],
|
108
|
-
[:dup, :roll, :
|
109
|
-
[:
|
138
|
+
[:dup, :roll, :nin],
|
139
|
+
[:cin, :nout, :cout]]
|
110
140
|
end
|
111
141
|
end
|
data/lib/rpiet/source.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require_relative 'color'
|
2
|
+
require_relative 'group'
|
3
|
+
require_relative 'image/image'
|
4
|
+
|
5
|
+
|
6
|
+
module RPiet
|
7
|
+
class Source
|
8
|
+
attr_reader :rows, :cols, :groups, :pixels, :codel_size
|
9
|
+
|
10
|
+
def initialize(image)
|
11
|
+
@cols, @rows = image.size.map &:to_i
|
12
|
+
@codel_size = image.codel_size
|
13
|
+
@pixels = alloc_matrix { |i, j| image.pixel(i, j)}
|
14
|
+
@groups_matrix, @groups = calculate_groups
|
15
|
+
end
|
16
|
+
|
17
|
+
def entry_group
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Is this point on the image and not black?
|
22
|
+
def valid?(x, y)
|
23
|
+
x >= 0 && x < @cols && y >= 0 && y < @rows &&
|
24
|
+
@pixels[x][y] != RPiet::Color::BLACK
|
25
|
+
end
|
26
|
+
|
27
|
+
def group_at(x, y)
|
28
|
+
@groups_matrix[x][y]
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# With grid of pixels start in upper left corner processing each pixel
|
33
|
+
# rightwards and downwards. As you encounter a pixel look up and left to
|
34
|
+
# see if it is a new color or part of an existing neighboring group.
|
35
|
+
def calculate_groups
|
36
|
+
groups_matrix = alloc_matrix { |i, j| 0 }
|
37
|
+
group = []
|
38
|
+
walk_matrix do |i, j|
|
39
|
+
color = @pixels[i][j]
|
40
|
+
up = j-1 >= 0 ? groups_matrix[i][j-1] : nil
|
41
|
+
left = i-1 >= 0 ? groups_matrix[i-1][j] : nil
|
42
|
+
if up && up.color == color
|
43
|
+
up << [i, j]
|
44
|
+
groups_matrix[i][j] = up
|
45
|
+
# disjoint groups to merge
|
46
|
+
if left && left != up && left.color == color
|
47
|
+
up.merge(groups_matrix, left)
|
48
|
+
left.points.each do |x, y|
|
49
|
+
groups_matrix[x][y] = up
|
50
|
+
group.delete left
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
if groups_matrix[i][j] == 0 && left && left.color == color
|
56
|
+
left << [i, j]
|
57
|
+
groups_matrix[i][j] = left
|
58
|
+
end
|
59
|
+
|
60
|
+
if groups_matrix[i][j] == 0 # Create new group for first codel found
|
61
|
+
groups_matrix[i][j] = RPiet::Group.new(color, [i, j])
|
62
|
+
group << groups_matrix[i][j]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
group.each { |group| group.calculate_corners }
|
66
|
+
return groups_matrix, group
|
67
|
+
end
|
68
|
+
|
69
|
+
private def alloc_matrix
|
70
|
+
Array.new(@cols) { Array.new(@rows) {nil} }.tap do |matrix|
|
71
|
+
walk_matrix {|i, j| matrix[i][j] = yield i, j }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private def walk_matrix
|
76
|
+
0.upto(@cols-1) do |i|
|
77
|
+
0.upto(@rows-1) do |j|
|
78
|
+
yield i, j
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/rpiet/version.rb
CHANGED
data/lib/rpiet.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative 'rpiet/version'
|
2
|
+
require_relative 'rpiet/interpreter'
|
data/rpiet.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rpiet/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'rpiet'
|
7
|
+
s.version = RPiet::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = 'Thomas E. Enebo'
|
10
|
+
s.email = 'tom.enebo@gmail.com'
|
11
|
+
s.homepage = 'http://github.com/enebo/rpiet'
|
12
|
+
s.summary = 'A Piet runtime'
|
13
|
+
s.description = 'An implementation of the esoteric programming language Piet'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative '../../lib/rpiet/asg/parser'
|
2
|
+
require_relative '../../lib/rpiet/asg/visitor'
|
3
|
+
require_relative '../../lib/rpiet/image/ascii_image'
|
4
|
+
|
5
|
+
describe "RPiet::Visitor" do
|
6
|
+
let(:cycle) do
|
7
|
+
RPiet::Image::AsciiImage.new <<-EOS
|
8
|
+
nb db nb
|
9
|
+
db ++ nb
|
10
|
+
db db db
|
11
|
+
EOS
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:my_visitor) do
|
15
|
+
Class.new(RPiet::Visitor) {
|
16
|
+
attr_reader :nodes
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
super
|
20
|
+
@nodes = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def visit_first(node)
|
24
|
+
@nodes[node] = 1
|
25
|
+
super
|
26
|
+
end
|
27
|
+
alias :visit_first_swch :visit_first
|
28
|
+
alias :visit_first_pntr :visit_first
|
29
|
+
|
30
|
+
def visit_again(node)
|
31
|
+
@nodes[node] += 1
|
32
|
+
end
|
33
|
+
}.new
|
34
|
+
end
|
35
|
+
|
36
|
+
it "can visit all nodes once plus one extra visit for a cycle" do
|
37
|
+
my_visitor.run RPiet::ASG::Parser.new(cycle).run
|
38
|
+
expect(my_visitor.nodes.size).to eq(10)
|
39
|
+
expect(my_visitor.nodes.values.inject(0) { |s, e| s += e; s}).to eq(11) # 1 node visited twice
|
40
|
+
end
|
41
|
+
end
|
data/spec/cycle_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative '../lib/rpiet/cycle'
|
2
2
|
|
3
3
|
module A
|
4
4
|
extend RPiet::CycleMethod
|
@@ -7,50 +7,50 @@ end
|
|
7
7
|
|
8
8
|
describe "RPiet::Cycle" do
|
9
9
|
it "can do cycle addition" do
|
10
|
-
(A::Letter::A + A::Letter::A).
|
11
|
-
(A::Letter::A + A::Letter::B).
|
12
|
-
(A::Letter::A + A::Letter::C).
|
13
|
-
(A::Letter::B + A::Letter::A).
|
14
|
-
(A::Letter::B + A::Letter::B).
|
15
|
-
(A::Letter::B + A::Letter::C).
|
16
|
-
(A::Letter::C + A::Letter::A).
|
17
|
-
(A::Letter::C + A::Letter::B).
|
18
|
-
(A::Letter::C + A::Letter::C).
|
10
|
+
expect(A::Letter::A + A::Letter::A).to eq(A::Letter::A)
|
11
|
+
expect(A::Letter::A + A::Letter::B).to eq(A::Letter::B)
|
12
|
+
expect(A::Letter::A + A::Letter::C).to eq(A::Letter::C)
|
13
|
+
expect(A::Letter::B + A::Letter::A).to eq(A::Letter::B)
|
14
|
+
expect(A::Letter::B + A::Letter::B).to eq(A::Letter::C)
|
15
|
+
expect(A::Letter::B + A::Letter::C).to eq(A::Letter::A)
|
16
|
+
expect(A::Letter::C + A::Letter::A).to eq(A::Letter::C)
|
17
|
+
expect(A::Letter::C + A::Letter::B).to eq(A::Letter::A)
|
18
|
+
expect(A::Letter::C + A::Letter::C).to eq(A::Letter::B)
|
19
19
|
end
|
20
20
|
|
21
21
|
it "can do cycle subtraction" do
|
22
|
-
(A::Letter::A - A::Letter::A).
|
23
|
-
(A::Letter::A - A::Letter::B).
|
24
|
-
(A::Letter::A - A::Letter::C).
|
25
|
-
(A::Letter::B - A::Letter::A).
|
26
|
-
(A::Letter::B - A::Letter::B).
|
27
|
-
(A::Letter::B - A::Letter::C).
|
28
|
-
(A::Letter::C - A::Letter::A).
|
29
|
-
(A::Letter::C - A::Letter::B).
|
30
|
-
(A::Letter::C - A::Letter::C).
|
22
|
+
expect(A::Letter::A - A::Letter::A).to eq(A::Letter::A)
|
23
|
+
expect(A::Letter::A - A::Letter::B).to eq(A::Letter::C)
|
24
|
+
expect(A::Letter::A - A::Letter::C).to eq(A::Letter::B)
|
25
|
+
expect(A::Letter::B - A::Letter::A).to eq(A::Letter::B)
|
26
|
+
expect(A::Letter::B - A::Letter::B).to eq(A::Letter::A)
|
27
|
+
expect(A::Letter::B - A::Letter::C).to eq(A::Letter::C)
|
28
|
+
expect(A::Letter::C - A::Letter::A).to eq(A::Letter::C)
|
29
|
+
expect(A::Letter::C - A::Letter::B).to eq(A::Letter::B)
|
30
|
+
expect(A::Letter::C - A::Letter::C).to eq(A::Letter::A)
|
31
31
|
end
|
32
32
|
|
33
33
|
it "can increment" do
|
34
|
-
A::Letter::A.incr.
|
35
|
-
A::Letter::B.incr.
|
36
|
-
A::Letter::C.incr.
|
34
|
+
expect(A::Letter::A.incr).to eq(A::Letter::B)
|
35
|
+
expect(A::Letter::B.incr).to eq(A::Letter::C)
|
36
|
+
expect(A::Letter::C.incr).to eq(A::Letter::A)
|
37
37
|
end
|
38
38
|
|
39
39
|
it "can decrement" do
|
40
|
-
A::Letter::A.decr.
|
41
|
-
A::Letter::B.decr.
|
42
|
-
A::Letter::C.decr.
|
40
|
+
expect(A::Letter::A.decr).to eq(A::Letter::C)
|
41
|
+
expect(A::Letter::B.decr).to eq(A::Letter::A)
|
42
|
+
expect(A::Letter::C.decr).to eq(A::Letter::B)
|
43
43
|
end
|
44
44
|
|
45
45
|
it "can calculate deltas" do
|
46
|
-
A::Letter::A.delta(A::Letter::A).
|
47
|
-
A::Letter::A.delta(A::Letter::B).
|
48
|
-
A::Letter::A.delta(A::Letter::C).
|
49
|
-
A::Letter::B.delta(A::Letter::A).
|
50
|
-
A::Letter::B.delta(A::Letter::B).
|
51
|
-
A::Letter::B.delta(A::Letter::C).
|
52
|
-
A::Letter::C.delta(A::Letter::A).
|
53
|
-
A::Letter::C.delta(A::Letter::B).
|
54
|
-
A::Letter::C.delta(A::Letter::C).
|
46
|
+
expect(A::Letter::A.delta(A::Letter::A)).to eq(0)
|
47
|
+
expect(A::Letter::A.delta(A::Letter::B)).to eq(2)
|
48
|
+
expect(A::Letter::A.delta(A::Letter::C)).to eq(1)
|
49
|
+
expect(A::Letter::B.delta(A::Letter::A)).to eq(1)
|
50
|
+
expect(A::Letter::B.delta(A::Letter::B)).to eq(0)
|
51
|
+
expect(A::Letter::B.delta(A::Letter::C)).to eq(2)
|
52
|
+
expect(A::Letter::C.delta(A::Letter::A)).to eq(2)
|
53
|
+
expect(A::Letter::C.delta(A::Letter::B)).to eq(1)
|
54
|
+
expect(A::Letter::C.delta(A::Letter::C)).to eq(0)
|
55
55
|
end
|
56
56
|
end
|
@@ -1,17 +1,44 @@
|
|
1
|
-
|
1
|
+
require_relative '../lib/rpiet/direction_pointer'
|
2
2
|
|
3
3
|
describe "DirectionPointer" do
|
4
4
|
it "can rotate" do
|
5
5
|
dp = RPiet::DirectionPointer.new
|
6
6
|
|
7
|
-
dp.direction.
|
7
|
+
expect(dp.direction).to eq(RPiet::Direction::RIGHT)
|
8
8
|
dp.rotate!
|
9
|
-
dp.direction.
|
9
|
+
expect(dp.direction).to eq(RPiet::Direction::DOWN)
|
10
10
|
dp.rotate!
|
11
|
-
dp.direction.
|
11
|
+
expect(dp.direction).to eq(RPiet::Direction::LEFT)
|
12
12
|
dp.rotate!
|
13
|
-
dp.direction.
|
13
|
+
expect(dp.direction).to eq(RPiet::Direction::UP)
|
14
14
|
dp.rotate!(2)
|
15
|
-
dp.direction.
|
15
|
+
expect(dp.direction).to eq(RPiet::Direction::DOWN)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "knows it's ordinal values" do
|
19
|
+
dp = RPiet::DirectionPointer.new
|
20
|
+
expect(dp.ordinal).to eq(0)
|
21
|
+
dp.rotate!
|
22
|
+
expect(dp.ordinal).to eq(1)
|
23
|
+
dp.rotate!
|
24
|
+
expect(dp.ordinal).to eq(2)
|
25
|
+
dp.rotate!
|
26
|
+
expect(dp.ordinal).to eq(3)
|
27
|
+
dp.rotate!
|
28
|
+
expect(dp.ordinal).to eq(0)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "can restore from it's ordinal values" do
|
32
|
+
dp = RPiet::DirectionPointer.new
|
33
|
+
dp.from_ordinal!(0)
|
34
|
+
expect(dp.direction).to eq(RPiet::Direction::RIGHT)
|
35
|
+
dp.from_ordinal!(1)
|
36
|
+
expect(dp.direction).to eq(RPiet::Direction::DOWN)
|
37
|
+
dp.from_ordinal!(2)
|
38
|
+
expect(dp.direction).to eq(RPiet::Direction::LEFT)
|
39
|
+
dp.from_ordinal!(3)
|
40
|
+
expect(dp.direction).to eq(RPiet::Direction::UP)
|
41
|
+
dp.from_ordinal!(4)
|
42
|
+
expect(dp.direction).to eq(RPiet::Direction::RIGHT)
|
16
43
|
end
|
17
44
|
end
|