rpiet 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/Gemfile +12 -0
  4. data/bin/color_wheel +84 -0
  5. data/bin/image_gen +39 -0
  6. data/bin/rpiet +68 -11
  7. data/images/counter.txt +7 -0
  8. data/lib/rpiet/asg/graph_interpreter.rb +39 -0
  9. data/lib/rpiet/asg/parser.rb +156 -0
  10. data/lib/rpiet/asg/visitor.rb +66 -0
  11. data/lib/rpiet/asg.rb +336 -0
  12. data/lib/rpiet/codel_chooser.rb +32 -4
  13. data/lib/rpiet/color.rb +70 -25
  14. data/lib/rpiet/cycle.rb +18 -7
  15. data/lib/rpiet/debugger/debugger.rb +298 -0
  16. data/lib/rpiet/debugger/stylesheet.css +88 -0
  17. data/lib/rpiet/direction_pointer.rb +49 -18
  18. data/lib/rpiet/event_handler.rb +62 -7
  19. data/lib/rpiet/group.rb +25 -8
  20. data/lib/rpiet/image/ascii_image.rb +8 -20
  21. data/lib/rpiet/image/image.rb +8 -3
  22. data/lib/rpiet/image/url_image.rb +28 -14
  23. data/lib/rpiet/interpreter.rb +72 -72
  24. data/lib/rpiet/ir/assembler.rb +87 -0
  25. data/lib/rpiet/ir/builder.rb +255 -0
  26. data/lib/rpiet/ir/cfg.rb +494 -0
  27. data/lib/rpiet/ir/instructions.rb +536 -0
  28. data/lib/rpiet/ir/ir_cfg_interpreter.rb +23 -0
  29. data/lib/rpiet/ir/ir_interpreter.rb +101 -0
  30. data/lib/rpiet/ir/ir_native_interpreter.rb +77 -0
  31. data/lib/rpiet/ir/jruby_backend.rb +279 -0
  32. data/lib/rpiet/ir/operands.rb +28 -0
  33. data/lib/rpiet/ir/passes/data_flow_problem.rb +32 -0
  34. data/lib/rpiet/ir/passes/flow_graph_node.rb +76 -0
  35. data/lib/rpiet/ir/passes/peephole.rb +214 -0
  36. data/lib/rpiet/ir/passes/push_pop_elimination_pass.rb +112 -0
  37. data/lib/rpiet/live_machine_state.rb +15 -0
  38. data/lib/rpiet/machine.rb +62 -32
  39. data/lib/rpiet/source.rb +83 -0
  40. data/lib/rpiet/version.rb +1 -1
  41. data/lib/rpiet.rb +2 -2
  42. data/rpiet.gemspec +19 -0
  43. data/spec/asg/visitor_spec.rb +41 -0
  44. data/spec/cycle_spec.rb +34 -34
  45. data/spec/direction_pointer_spec.rb +33 -6
  46. data/spec/group_spec.rb +73 -48
  47. data/spec/interpreter_spec.rb +161 -12
  48. data/spec/ir/assembler_spec.rb +122 -0
  49. data/spec/ir/builder_spec.rb +20 -0
  50. data/spec/ir/cfg_spec.rb +151 -0
  51. data/spec/ir/ir_interpreter_spec.rb +102 -0
  52. data/spec/ir/passes/push_pop_elimination_pass_spec.rb +34 -0
  53. data/spec/machine_spec.rb +5 -3
  54. data/spec/source_spec.rb +69 -0
  55. data/spec/spec_helper.rb +78 -0
  56. metadata +54 -16
  57. data/images/nfib.png +0 -0
data/lib/rpiet/asg.rb ADDED
@@ -0,0 +1,336 @@
1
+ module RPiet
2
+ module ASG
3
+ ##
4
+ # Base class of all nodes
5
+ class Node
6
+ attr_accessor :next_node
7
+ attr_reader :step, :x, :y
8
+
9
+ def initialize(step, x, y, *)
10
+ @step, @x, @y = step, x, y
11
+ end
12
+
13
+ def eql?(o)
14
+ x == o.x && y == o.y && operation == o.operation
15
+ end
16
+
17
+ def hash
18
+ [x, y, operation].hash
19
+ end
20
+
21
+ def visit(visitor) = visitor.visit self
22
+
23
+ # Does this node represent a branching operation?
24
+ def branch? = false
25
+
26
+ ##
27
+ # Is this node hidden from the perspective of calling next_step?
28
+ # In simpler interpreter noop, cc, and dp will change during next_step
29
+ # while in graph and ir interpreters they are explicit actions.
30
+ def hidden? = false
31
+
32
+ # What possible paths can this node navigate to next
33
+ def paths = [@next_node]
34
+
35
+ def add_path(node, *)
36
+ @next_node = node
37
+ end
38
+
39
+ def operation = self.class.operation_name.to_sym
40
+
41
+ def self.operation_name = name.sub(/.*::/, '').sub('Node', '').downcase
42
+
43
+ def exec(machine)
44
+ value = execute(machine)
45
+ return value if branch?
46
+ next_node
47
+ end
48
+
49
+ def inspect
50
+ "p##{@step} [#{@x}, #{@y}](#{operation})"
51
+ end
52
+ alias :to_s :inspect
53
+
54
+ def self.create(step, x, y, operation, *extra_args)
55
+ Nodes[operation].new step, x, y, *extra_args
56
+ end
57
+
58
+ # Overhead of event handler is extreme so only reference it if there is one.
59
+ def self.handle_event_handler(event_handler)
60
+ prepend NodeEventHandler if event_handler
61
+ end
62
+ end
63
+
64
+ module NodeEventHandler
65
+ def exec(machine)
66
+ machine.event_handler.operation(machine, @step, operation)
67
+ super
68
+ end
69
+ end
70
+
71
+ ##
72
+ # Perform common mathematical binary operation
73
+ class MathNode < Node
74
+ def initialize(step, x, y, operation, *)
75
+ super(step, x, y)
76
+ @operation = operation
77
+ end
78
+
79
+ def execute(machine)
80
+ stack = machine.stack
81
+ return nil unless stack.length >= 2
82
+ a, b = stack.pop(2)
83
+ stack << a.send(@operation, b)
84
+ end
85
+ end
86
+
87
+ ##
88
+ # Add two values from stack
89
+ class AddNode < MathNode
90
+ def initialize(step, x, y, *) = super(step, x, y, :+)
91
+ end
92
+
93
+ ##
94
+ # When dp changes due to natural navigation we update it
95
+ # to the new value
96
+ class CcNode < Node
97
+ attr_reader :value
98
+
99
+ def initialize(step, x, y, cc_ordinal, *)
100
+ super(step, x, y)
101
+ @value = cc_ordinal
102
+ end
103
+
104
+ def hidden? = true
105
+
106
+ def execute(machine) = machine.cc.from_ordinal!(@value)
107
+ end
108
+
109
+ ##
110
+ # Read in character from the console and push on the stack
111
+ class CinNode < Node
112
+ def execute(machine)
113
+ machine.output.write "> "
114
+ machine.stack << machine.input.read(1).ord
115
+ end
116
+ end
117
+
118
+ ##
119
+ # Display top element of the stack to the console as a character.
120
+ class CoutNode < Node
121
+ def execute(machine) = print machine.stack.pop.chr
122
+ end
123
+
124
+ ##
125
+ # Add two values from stack
126
+ class DivNode < MathNode
127
+ def initialize(step, x, y, *)= super(step, x, y, :/)
128
+
129
+ # Note: Following npiet's div by zero value.
130
+ def execute(machine)
131
+ stack = machine.stack
132
+ return nil unless stack.length >= 2
133
+ a, b = stack.pop(2)
134
+ stack << (b == 0 ? DIV_BY_ZERO_VALUE : a / b)
135
+ end
136
+ end
137
+
138
+ ##
139
+ # When dp changes due to natural navigation we update it
140
+ # to the new value
141
+ class DpNode < Node
142
+ attr_reader :value
143
+
144
+ def initialize(step, x, y, _, dp_ordinal)
145
+ super(step, x, y)
146
+ @value = dp_ordinal
147
+ end
148
+
149
+ def hidden? = true
150
+
151
+ def execute(machine) = machine.dp.from_ordinal!(@value)
152
+ end
153
+
154
+ ##
155
+ # Duplicate top element of the stack.
156
+ class DupNode < Node
157
+ def execute(machine)
158
+ stack = machine.stack
159
+ stack << stack[-1] if stack[-1]
160
+ end
161
+ end
162
+
163
+ class ExitNode < Node
164
+ def execute(machine)
165
+ end
166
+ end
167
+
168
+ ##
169
+ # Greater than operation on top two stack values
170
+ class GtrNode < Node
171
+ def execute(machine)
172
+ stack = machine.stack
173
+ return nil unless stack.length >= 2
174
+ a, b = stack.pop(2)
175
+ stack << (a > b ? 1 : 0)
176
+ end
177
+ end
178
+
179
+ ##
180
+ # Modulos two values from stack
181
+ class ModNode < MathNode
182
+ def initialize(step, x, y, *) = super(step, x, y, :%)
183
+ end
184
+
185
+ ##
186
+ # Multiply two values from stack
187
+ class MultNode < MathNode
188
+ def initialize(step, x, y, *)= super(step, x, y, :*)
189
+ end
190
+
191
+ ##
192
+ # Read in number from the console and push on the stack
193
+ class NinNode < Node
194
+ def execute(machine)
195
+ machine.output.puts "Enter an integer: "
196
+ machine.stack << machine.input.gets.to_i
197
+ end
198
+ end
199
+
200
+ ##
201
+ # Entry point into the program. Not strictly necessary
202
+ # but we will kill this during analysis
203
+ class NoopNode < Node
204
+ def hidden? = true
205
+
206
+ def execute(_); end # No-op
207
+ end
208
+
209
+ ##
210
+ # Greater than operation on top two stack values
211
+ class NotNode < Node
212
+ def execute(machine)
213
+ stack = machine.stack
214
+ top = stack.pop
215
+ stack << (!top || top == 0 ? 1 : 0)
216
+ end
217
+ end
218
+
219
+ ##
220
+ # Diplay top element of the stack to the console.
221
+ class NoutNode < Node
222
+ def execute(machine) = print machine.stack.pop
223
+ end
224
+
225
+ ##
226
+ # Rotate the direction based on top stack value and
227
+ # change execution flow.
228
+ class PntrNode < Node
229
+ def branch? = true
230
+
231
+ def add_path(node, _, dp_value)
232
+ @values ||= []
233
+ @values[dp_value] = node
234
+ end
235
+
236
+ # What possible paths can this node navigate to next
237
+ def paths = @values
238
+
239
+ def execute(machine)
240
+ top = machine.stack.pop
241
+ @values[machine.dp.rotate!(top).ordinal]
242
+ end
243
+ end
244
+
245
+ ##
246
+ # Pop a value from the stack
247
+ class PopNode < Node
248
+ def execute(machine) = machine.stack.pop
249
+ end
250
+
251
+ ##
252
+ # Push a value onto the stack
253
+ class PushNode < Node
254
+ attr_reader :value
255
+
256
+ def initialize(step, x, y, value)
257
+ super(step, x, y)
258
+ @value = value
259
+ end
260
+
261
+ def execute(machine) = machine.stack << @value
262
+ end
263
+
264
+ ##
265
+ # Roll the stack
266
+ class RollNode < Node
267
+ def execute(machine)
268
+ stack = machine.stack
269
+ depth, num = stack.pop(2)
270
+ num %= depth
271
+ return if depth <= 0 || num == 0
272
+ if num > 0
273
+ stack[-depth..-1] = stack[-num..-1] + stack[-depth...-num]
274
+ elsif num < 0
275
+ stack[-depth..-1] = stack[-depth...-num] + stack[-num..-1]
276
+ end
277
+ end
278
+ end
279
+
280
+ ##
281
+ # Subtract two values from stack
282
+ class SubNode < MathNode
283
+ def initialize(step, x, y, *)= super(step, x, y, :-)
284
+ end
285
+
286
+ ##
287
+ # Rotate the codel chooser based on top stack value and
288
+ # change execution flow.
289
+ class SwchNode < Node
290
+ def branch? = true
291
+
292
+ def add_path(node, cc, _)
293
+ if cc == RPiet::CodelChooser::LEFT
294
+ @left = node
295
+ else
296
+ @right = node
297
+ end
298
+ end
299
+
300
+ # What possible paths can this node navigate to next
301
+ def paths = [@left, @right]
302
+
303
+ def execute(machine)
304
+ top = machine.stack.pop
305
+ if machine.cc.switch!(top) == RPiet::CodelChooser::LEFT
306
+ @left
307
+ else
308
+ @right
309
+ end
310
+ end
311
+ end
312
+
313
+ Nodes = {
314
+ noop: NoopNode,
315
+ push: PushNode,
316
+ pop: PopNode,
317
+ add: AddNode,
318
+ sub: SubNode,
319
+ mult: MultNode,
320
+ div: DivNode,
321
+ mod: ModNode,
322
+ not: NotNode,
323
+ gtr: GtrNode,
324
+ pntr: PntrNode,
325
+ swch: SwchNode,
326
+ dup: DupNode,
327
+ roll: RollNode,
328
+ nin: NinNode,
329
+ cin: CinNode,
330
+ nout: NoutNode,
331
+ cout: CoutNode,
332
+ dp: DpNode,
333
+ cc: CcNode
334
+ }
335
+ end
336
+ end
@@ -1,11 +1,15 @@
1
- require 'rpiet/direction_pointer'
1
+ require_relative 'direction_pointer'
2
2
 
3
3
  module RPiet
4
4
  class CodelChooser
5
5
  LEFT, RIGHT = -1, 1
6
- attr_reader :direction
6
+ attr_accessor :direction
7
+
7
8
  def initialize; @direction = LEFT; end
8
- def switch!(amount = 1); @direction *= -1.**(amount % 2); end
9
+ def switch!(amount = 1)
10
+ @direction *= -1.**(amount % 2)
11
+ self
12
+ end
9
13
 
10
14
  def ascii(dp)
11
15
  case dp.direction
@@ -20,8 +24,32 @@ module RPiet
20
24
  end
21
25
  end
22
26
 
27
+ def degrees(dp)
28
+ case dp.direction
29
+ when RPiet::Direction::RIGHT
30
+ @direction == LEFT ? 270 : 90
31
+ when RPiet::Direction::UP
32
+ @direction == LEFT ? 180 : 0
33
+ when RPiet::Direction::LEFT
34
+ @direction == LEFT ? 90 : 270
35
+ when RPiet::Direction::DOWN
36
+ @direction == LEFT ? 0 : 180
37
+ end
38
+ end
39
+
40
+ def as_constant
41
+ @direction == LEFT ? "LEFT" : "RIGHT"
42
+ end
43
+
44
+ alias :ordinal :direction
45
+
46
+ def from_ordinal!(ordinal)
47
+ @direction = ordinal == LEFT ? LEFT : RIGHT
48
+ end
49
+
23
50
  def inspect
24
- (@direction == LEFT ? "left(0)" : "right(1)")
51
+ @direction.to_s
52
+ #(@direction == LEFT ? "<-" : "->")
25
53
  end
26
54
  alias :to_s :inspect
27
55
  end
data/lib/rpiet/color.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'rpiet/cycle'
1
+ require_relative 'cycle'
2
2
 
3
3
  module RPiet
4
4
  class Color
@@ -7,48 +7,93 @@ module RPiet
7
7
  cycle :LIGHTNESS, :LIGHT, :NORMAL, :DARK
8
8
  cycle :HUE, :RED, :YELLOW, :GREEN, :CYAN, :BLUE, :MAGENTA
9
9
 
10
- attr_reader :hue, :lightness
10
+ attr_reader :hue, :lightness, :rgb
11
11
 
12
- def initialize(lightness, hue)
13
- @lightness, @hue = lightness, hue
12
+ def initialize(lightness, hue, rgb)
13
+ @lightness, @hue, @rgb = lightness, hue, rgb
14
14
  end
15
15
 
16
+ def color_from(lightness_delta: 0, hue_delta: 0)
17
+ LIGHTNESS_HUE[lightness.incr(lightness_delta)][hue.incr(hue_delta)]
18
+ end
19
+
20
+ # Display color as initial of lightness followed by hue (normal blue == nb).
16
21
  def to_initial
17
22
  @lightness.to_initial + @hue.to_initial
18
23
  end
19
24
 
20
25
  def to_s
21
- (@lightness == LIGHTNESS::NORMAL ? "" : @lightness.to_s + ' ') + @hue.to_s
26
+ [@lightness == LIGHTNESS::NORMAL ? nil : @lightness.to_s,
27
+ @hue.to_s].compact.join('_')
22
28
  end
23
29
  alias :inspect :to_s
24
30
 
25
- RGB_WHITE, WHITE = '0xffffff', Color.new('white', '')
26
- RGB_BLACK, BLACK = '0x000000', Color.new('black', '')
31
+ def self.color_for(rgb_hex)
32
+ RGB[rgb_hex]
33
+ end
34
+
35
+ RGB_WHITE, WHITE = '0xffffff', Color.new(LIGHTNESS::NORMAL, 'white', '0xffffff')
36
+ RGB_BLACK, BLACK = '0x000000', Color.new(LIGHTNESS::NORMAL, 'black', '0x000000')
27
37
 
28
38
  class << WHITE; def to_initial; '..'; end; end
29
39
  class << BLACK; def to_initial; '++'; end; end
30
40
 
41
+ LIGHT_RED = Color.new(LIGHTNESS::LIGHT, HUE::RED, '0xffc0c0')
42
+ LIGHT_YELLOW = Color.new(LIGHTNESS::LIGHT, HUE::YELLOW, '0xffffc0')
43
+ LIGHT_GREEN = Color.new(LIGHTNESS::LIGHT, HUE::GREEN, '0xc0ffc0')
44
+ LIGHT_CYAN = Color.new(LIGHTNESS::LIGHT, HUE::CYAN, '0xc0ffff')
45
+ LIGHT_BLUE = Color.new(LIGHTNESS::LIGHT, HUE::BLUE, '0xc0c0ff')
46
+ LIGHT_MAGENTA = Color.new(LIGHTNESS::LIGHT, HUE::MAGENTA, '0xffc0ff')
47
+ RED = Color.new(LIGHTNESS::NORMAL, HUE::RED, '0xff0000')
48
+ YELLOW = Color.new(LIGHTNESS::NORMAL, HUE::YELLOW, '0xffff00')
49
+ GREEN = Color.new(LIGHTNESS::NORMAL, HUE::GREEN, '0x00ff00')
50
+ CYAN = Color.new(LIGHTNESS::NORMAL, HUE::CYAN, '0x00ffff')
51
+ BLUE = Color.new(LIGHTNESS::NORMAL, HUE::BLUE, '0x0000ff')
52
+ MAGENTA = Color.new(LIGHTNESS::NORMAL, HUE::MAGENTA, '0xff00ff')
53
+ DARK_RED = Color.new(LIGHTNESS::DARK, HUE::RED, '0xc00000')
54
+ DARK_YELLOW = Color.new(LIGHTNESS::DARK, HUE::YELLOW, '0xc0c000')
55
+ DARK_GREEN = Color.new(LIGHTNESS::DARK, HUE::GREEN, '0x00c000')
56
+ DARK_CYAN = Color.new(LIGHTNESS::DARK, HUE::CYAN, '0x00c0c0')
57
+ DARK_BLUE = Color.new(LIGHTNESS::DARK, HUE::BLUE, '0x0000c0')
58
+ DARK_MAGENTA = Color.new(LIGHTNESS::DARK, HUE::MAGENTA, '0xc000c0')
59
+
31
60
  RGB = {
32
- '0xffc0c0' => Color.new(LIGHTNESS::LIGHT, HUE::RED),
33
- '0xffffc0' => Color.new(LIGHTNESS::LIGHT, HUE::YELLOW),
34
- '0xc0ffc0' => Color.new(LIGHTNESS::LIGHT, HUE::GREEN),
35
- '0xc0ffff' => Color.new(LIGHTNESS::LIGHT, HUE::CYAN),
36
- '0xc0c0ff' => Color.new(LIGHTNESS::LIGHT, HUE::BLUE),
37
- '0xffc0ff' => Color.new(LIGHTNESS::LIGHT, HUE::MAGENTA),
38
- '0xff0000' => Color.new(LIGHTNESS::NORMAL, HUE::RED),
39
- '0xffff00' => Color.new(LIGHTNESS::NORMAL, HUE::YELLOW),
40
- '0x00ff00' => Color.new(LIGHTNESS::NORMAL, HUE::GREEN),
41
- '0x00ffff' => Color.new(LIGHTNESS::NORMAL, HUE::CYAN),
42
- '0x0000ff' => Color.new(LIGHTNESS::NORMAL, HUE::BLUE),
43
- '0xff00ff' => Color.new(LIGHTNESS::NORMAL, HUE::MAGENTA),
44
- '0xc00000' => Color.new(LIGHTNESS::DARK, HUE::RED),
45
- '0xc0c000' => Color.new(LIGHTNESS::DARK, HUE::YELLOW),
46
- '0x00c000' => Color.new(LIGHTNESS::DARK, HUE::GREEN),
47
- '0x00c0c0' => Color.new(LIGHTNESS::DARK, HUE::CYAN),
48
- '0x0000c0' => Color.new(LIGHTNESS::DARK, HUE::BLUE),
49
- '0xc000c0' => Color.new(LIGHTNESS::DARK, HUE::MAGENTA),
61
+ '0xffc0c0' => LIGHT_RED, '0xffffc0' => LIGHT_YELLOW, '0xc0ffc0' => LIGHT_GREEN,
62
+ '0xc0ffff' => LIGHT_CYAN, '0xc0c0ff' => LIGHT_BLUE, '0xffc0ff' => LIGHT_MAGENTA,
63
+ '0xff0000' => RED, '0xffff00' => YELLOW, '0x00ff00' => GREEN,
64
+ '0x00ffff' => CYAN, '0x0000ff' => BLUE, '0xff00ff' => MAGENTA,
65
+ '0xc00000' => DARK_RED, '0xc0c000' => DARK_YELLOW, '0x00c000' => DARK_GREEN,
66
+ '0x00c0c0' => DARK_CYAN, '0x0000c0' => DARK_BLUE, '0xc000c0' => DARK_MAGENTA,
50
67
  RGB_WHITE => WHITE,
51
68
  RGB_BLACK => BLACK
52
69
  }
70
+
71
+ LIGHTNESS_HUE = {
72
+ LIGHTNESS::LIGHT => {
73
+ HUE::RED => LIGHT_RED,
74
+ HUE::YELLOW => LIGHT_YELLOW,
75
+ HUE::GREEN => LIGHT_GREEN,
76
+ HUE::CYAN => LIGHT_CYAN,
77
+ HUE::BLUE => LIGHT_BLUE,
78
+ HUE::MAGENTA => LIGHT_MAGENTA
79
+ },
80
+ LIGHTNESS::NORMAL => {
81
+ HUE::RED => RED,
82
+ HUE::YELLOW => YELLOW,
83
+ HUE::GREEN => GREEN,
84
+ HUE::CYAN => CYAN,
85
+ HUE::BLUE => BLUE,
86
+ HUE::MAGENTA => MAGENTA
87
+ },
88
+ LIGHTNESS::DARK => {
89
+ HUE::RED => DARK_RED,
90
+ HUE::YELLOW => DARK_YELLOW,
91
+ HUE::GREEN => DARK_GREEN,
92
+ HUE::CYAN => DARK_CYAN,
93
+ HUE::BLUE => DARK_BLUE,
94
+ HUE::MAGENTA => DARK_MAGENTA
95
+ }
96
+
97
+ }
53
98
  end
54
99
  end
data/lib/rpiet/cycle.rb CHANGED
@@ -1,29 +1,34 @@
1
1
  module RPiet
2
2
  class Cycle
3
- attr_reader :name, :value
3
+ attr_reader :type, :value
4
+ attr_accessor :length
4
5
 
5
6
  def initialize(value, name, list)
6
7
  @value, @name, @list = value, name.to_s.downcase, list
7
8
  end
8
9
 
9
10
  def delta(other)
10
- (@value - other.value) % @list.length
11
+ (@value - other.value) % @length
11
12
  end
12
13
 
13
14
  def -(other)
14
- @list[(@value - other.value) % @list.length]
15
+ @list[(@value - other.value) % @length]
15
16
  end
16
17
 
17
18
  def +(other)
18
- @list[(@value + other.value) % @list.length]
19
+ @list[(@value + other.value) % @length]
19
20
  end
20
21
 
21
22
  def incr(amount = 1)
22
- @list[(@value + amount) % @list.length]
23
+ @list[(@value + amount) % @length]
23
24
  end
24
25
 
25
26
  def decr(amount = 1)
26
- @list[(@value - amount) % @list.length]
27
+ @list[(@value - amount) % @length]
28
+ end
29
+
30
+ def abs(amount)
31
+ @list[amount % @length]
27
32
  end
28
33
 
29
34
  def to_initial
@@ -31,9 +36,12 @@ module RPiet
31
36
  end
32
37
 
33
38
  def to_s
39
+ "#{@name}"
40
+ end
41
+
42
+ def inspect
34
43
  "#{@name}(#{@value})"
35
44
  end
36
- alias :inspect :to_s
37
45
  end
38
46
 
39
47
  module CycleMethod
@@ -53,6 +61,9 @@ module RPiet
53
61
  list[i] = Cycle.new(i, name, list)
54
62
  holder_module.const_set name, list[i]
55
63
  end
64
+
65
+ # Micro-opt...@list.length is slower than @length
66
+ list.each { |element| element.length = list.length}
56
67
  end
57
68
  module_function :cycle
58
69
  end