rpiet 0.1 → 0.2
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.
- 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
|