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.
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
@@ -15,11 +15,16 @@ module RPiet
15
15
 
16
16
  def pixel(x, y)
17
17
  r,g,b = raw_pixel(x * @codel_size, y * @codel_size)
18
- color_for(format "0x%02x%02x%02x" % [r,g,b])
18
+ RPiet::Color.color_for(format "0x%02x%02x%02x" % [r,g,b])
19
19
  end
20
20
 
21
- def color_for(rgb_hex)
22
- RPiet::Color::RGB[rgb_hex]
21
+ def each
22
+ w, h = size
23
+ h.times do |j|
24
+ w.times do |i|
25
+ yield i, j, pixel(i, j)
26
+ end
27
+ end
23
28
  end
24
29
 
25
30
  def ascii(group = [])
@@ -1,27 +1,41 @@
1
- require 'java'
1
+
2
2
  require 'rpiet/image/image'
3
3
 
4
4
  # Raw in Java is BufferendImage. In MRI it just needs to define:
5
5
  # width, height, getRGB(x, y)
6
6
  module RPiet
7
7
  module Image
8
- class URLImage < RPiet::Image::Image
9
- def initialize(file, codel_size=1)
10
- super(codel_size)
11
- @raw = javax.imageio.ImageIO.read(java.net.URL.new(file))
12
- end
8
+ if RUBY_ENGINE == "jruby"
9
+ class URLImage < RPiet::Image::Image
10
+ attr_reader :raw_width, :raw_height
13
11
 
14
- def raw_pixel(x, y)
15
- rgb = @raw.getRGB(x, y)
16
- [(rgb >> 16 ) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff]
17
- end
12
+ def initialize(file, codel_size=1)
13
+ super(codel_size)
14
+ image = javax.imageio.ImageIO.read(java.net.URL.new(file))
15
+ @raw_width, @raw_height, @raw = image.width, image.height, image
16
+ end
18
17
 
19
- def raw_width
20
- @raw.width
18
+ def raw_pixel(x, y)
19
+ rgb = java.awt.Color.new(@raw.get_rgb(x, y))
20
+
21
+ [rgb.red, rgb.green, rgb.blue]
22
+ end
21
23
  end
24
+ else
25
+ require 'mini_magick'
26
+ class URLImage < RPiet::Image::Image
27
+ attr_reader :raw_width, :raw_height
28
+
29
+ def initialize(file, codel_size=1)
30
+ super(codel_size)
31
+ file = file[5..-1] if file.start_with?('file:')
32
+ image = MiniMagick::Image.open(file)
33
+ @raw_width, @raw_height, @raw = image.width, image.height, image.get_pixels
34
+ end
22
35
 
23
- def raw_height
24
- @raw.height
36
+ def raw_pixel(x, y)
37
+ @raw[y][x]
38
+ end
25
39
  end
26
40
  end
27
41
  end
@@ -1,63 +1,99 @@
1
- require 'rpiet/color'
2
- require 'rpiet/machine'
3
- require 'rpiet/group'
4
- require 'rpiet/event_handler'
1
+ require_relative 'color'
2
+ require_relative 'machine'
3
+ require_relative 'source'
4
+ require_relative 'event_handler'
5
5
 
6
6
  module RPiet
7
7
  class Interpreter
8
- attr_reader :pvm, :source, :groups, :x, :y, :step
8
+ attr_reader :pvm, :source, :pixels, :x, :y, :step, :event_handler
9
9
 
10
- def initialize(source, event_handler=RPiet::Logger::NoOutput.new)
10
+ def initialize(image, event_handler=RPiet::Logger::NoOutput.new)
11
+ @interpreter_thread = Thread.current
12
+ @source, @event_handler = RPiet::Source.new(image), event_handler
13
+ @delay = 0
14
+ end
15
+
16
+ def delay=(time)
17
+ @delay = time
18
+ end
19
+
20
+ def pause
21
+ @paused = true
22
+ end
23
+
24
+ def resume
25
+ @paused = false
26
+ @interpreter_thread.run
27
+ end
28
+
29
+ def advance
30
+ @paused = true
31
+ @interpreter_thread.run
32
+ end
33
+
34
+ def restart
35
+ @restart = true
36
+ abort
37
+ end
38
+
39
+ def abort
40
+ @abort = true
41
+ resume
42
+ end
43
+
44
+ def reset
11
45
  @x, @y, @pvm, @step = 0, 0, RPiet::Machine.new, 1
12
- @source, @event_handler = source, event_handler
13
- @rows, @cols = @source.size
14
- @pixels = alloc_matrix { |i, j| @source.pixel(i, j)}
15
- @groups = calculate_groups(alloc_matrix { |i, j| 0 })
16
46
  @event_handler.initialized(self)
17
47
  end
18
48
 
19
49
  def run
20
- while(next_step) do
50
+ @restart = true
51
+ while @restart
52
+ @restart = false
53
+ reset
54
+ Thread.stop if @paused
55
+ while(next_step) do
56
+ Thread.stop if @paused
57
+ end
58
+ @abort = false
21
59
  end
22
60
  end
23
61
 
24
- ##
25
- # Is this point on the image and not black?
26
- def valid?(x, y)
27
- x >= 0 && x < @rows && y >= 0 && y < @cols &&
28
- @pixels[x][y] != RPiet::Color::BLACK
29
- end
30
-
31
62
  def next_step
32
- @pvm.block_value = @groups[@x][@y].size
33
- i = 0
63
+ return false if @abort
64
+ @pvm.block_value = @source.group_at(@x, @y).size
65
+ attempt = 1
34
66
  seen_white = false
35
- @event_handler.step_begin(self)
36
- ex, ey = @groups[@x][@y].point_for(@pvm)
37
- while i < 8 do
38
- nx, ny = @pvm.next_possible(ex, ey)
67
+ ex, ey = @source.group_at(@x, @y).point_for(@pvm) # Exit point from group
68
+ @event_handler.step_begin(self, ex, ey)
69
+ while attempt <= 8 do
70
+ nx, ny = @pvm.next_possible(ex, ey) # where we enter Next group
71
+ valid = @source.valid?(nx, ny)
72
+ @event_handler.next_possible(self, ex, ey, nx, ny, valid)
73
+ sleep @delay if @delay != 0
74
+ Thread.stop if @paused
75
+ return false if @abort
39
76
 
40
- if !valid?(nx, ny)
41
- i += 1
42
- @pvm.orient_elsewhere(i)
77
+ if !valid
78
+ @pvm.orient_elsewhere(attempt)
79
+ attempt += 1
43
80
 
44
- ex, ey = @groups[@x][@y].point_for(@pvm) if !seen_white
81
+ ex, ey = @source.group_at(@x, @y).point_for(@pvm) if !seen_white
45
82
  @event_handler.trying_again(self, ex, ey)
46
- next
47
- elsif @pixels[nx][ny] == RPiet::Color::WHITE
83
+ elsif @source.pixels[nx][ny] == RPiet::Color::WHITE
48
84
  if !seen_white
49
85
  seen_white = true
50
- i = 0
86
+ attempt = 1
51
87
  @event_handler.seen_white(self)
52
88
  end
53
89
  ex, ey = nx, ny
54
90
  else
55
91
  if !seen_white
56
- operation = @pvm.execute(@pixels[nx][ny], @pixels[@x][@y])
92
+ operation = @pvm.execute(@source.pixels[nx][ny], @source.pixels[@x][@y])
57
93
  else
58
94
  operation = 'noop'
59
95
  end
60
- @event_handler.operation(self, operation)
96
+ @event_handler.operation(self, @step, operation)
61
97
  @x, @y = nx, ny
62
98
  @step += 1
63
99
  return true
@@ -67,46 +103,10 @@ module RPiet
67
103
  false
68
104
  end
69
105
 
70
- # always look up, left, or make new group
71
- def calculate_groups(groups)
72
- all_groups = []
73
- walk_matrix(groups) do |i, j|
74
- rgb = @pixels[i][j]
75
- up = j-1 >= 0 ? groups[i][j-1] : nil
76
- left = i-1 >= 0 ? groups[i-1][j] : nil
77
- if up && up.rgb == rgb
78
- up << [i, j]
79
- groups[i][j] = up
80
- # disjoint groups to merge
81
- up.merge(groups, left) if left && left != up && left.rgb == rgb
82
- end
83
-
84
- if groups[i][j] == 0 && left && left.rgb == rgb
85
- left << [i, j]
86
- groups[i][j] = left
87
- end
88
-
89
- if groups[i][j] == 0
90
- groups[i][j] = RPiet::Group.new(rgb, [i, j])
91
- all_groups << groups[i][j]
92
- end
93
- end
94
- all_groups.each { |group| group.finish }
95
- groups
96
- end
97
-
98
- def alloc_matrix
99
- Array.new(@rows) { Array.new(@cols) {nil} }.tap do |matrix|
100
- walk_matrix(matrix) { |i, j| matrix[i][j] = yield i, j }
101
- end
106
+ def to_s
107
+ @pvm.to_s
102
108
  end
103
109
 
104
- def walk_matrix(matrix)
105
- 0.upto(@rows-1) do |i|
106
- 0.upto(@cols-1) do |j|
107
- yield i, j
108
- end
109
- end
110
- end
110
+ def stack = @pvm.stack
111
111
  end
112
112
  end
@@ -0,0 +1,87 @@
1
+ require_relative 'instructions'
2
+ require_relative 'operands'
3
+
4
+ module RPiet
5
+ module IR
6
+ class Assembler
7
+ class << self
8
+ include Instructions, Operands
9
+
10
+ def assemble(code)
11
+ instructions = []
12
+ assignments = {}
13
+
14
+ code.split($/).each do |line|
15
+ result, rhs = line.split(/\s+=\s+/, 2)
16
+ rhs, result = result, nil unless rhs
17
+ if rhs =~ /\s*(\S+)\s*(==|!=|\*\*|[>\*\/\-%+])\s+(\S+)(?:\s*(\S+))?\s*/
18
+ operation, *operands = $2, *[$1, $3, $4].compact
19
+ else
20
+ operation, *operands = rhs.split(/\s+/)
21
+ end
22
+
23
+ instructions << create(assignments, result, operation, *operands)
24
+ end
25
+
26
+ instructions
27
+ end
28
+
29
+ def create(assignments, result, operation, *operands)
30
+ result = create_assignment(assignments, result) if result
31
+ operands = create_operands(assignments, *operands)
32
+
33
+ # Assumes assembly is valid.
34
+ case operation
35
+ when 'push' then PushInstr.new(operands[0])
36
+ when 'dpset' then DPSetInstr.new(operands[0])
37
+ when 'ccset' then CCSetInstr.new(operands[0])
38
+ when 'pop' then PopInstr.new(result)
39
+ when 'dpget' then DPGetInstr.new(result)
40
+ when 'ccset' then CCGetInstr.new(result)
41
+ when '+' then AddInstr.new(result, *operands)
42
+ when '-' then SubInstr.new(result, *operands)
43
+ when '*' then MultInstr.new(result, *operands)
44
+ when '/' then DivInstr.new(result, *operands)
45
+ when '%' then ModInstr.new(result, *operands)
46
+ when '**' then PowInstr.new(result, *operands)
47
+ when 'nout' then NoutInstr.new(*operands)
48
+ when 'cout' then CoutInstr.new(*operands)
49
+ when '>' then GTInstr.new(result, *operands)
50
+ when '==' then BEQInstr.new(*operands)
51
+ when '!=' then BNEInstr.new(*operands)
52
+ when 'jump' then JumpInstr.new(*operands)
53
+ when 'cin' then CinInstr.new(result)
54
+ when 'nin' then NinInstr.new(result)
55
+ when 'roll' then RollInstr.new *operands
56
+ when 'copy' then CopyInstr.new(result, *operands)
57
+ when 'label' then LabelInstr.new(*operands)
58
+ when 'exit' then ExitInstr.new
59
+ else
60
+ raise ArgumentError.new("unknown operation: #{operation}")
61
+ end
62
+ end
63
+
64
+ def create_assignment(assignments, result)
65
+ raise ArgumentError.new("result must be variable") unless result[0] == 'v'
66
+ raise ArgumentError.new("Same assignment found: #{result}. Not in SSA form") if assignments[result]
67
+ assignments[result] = VariableOperand.new(result)
68
+ end
69
+
70
+ def create_operands(assignments, *operands)
71
+ operands.map do |operand|
72
+ case operand[0]
73
+ when 'v' then
74
+ variable = assignments[operand]
75
+ raise ArgumentError.new("Variable without assignment found: #{operand}. Not in SSA form") unless variable
76
+ variable
77
+ when /[\d-]/ then Integer(operand)
78
+ when '\'' then operand[1..-2]
79
+ when /\S/ then operand.to_sym
80
+ else raise ArgumentError.new("unknown operand: #{operand}")
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,255 @@
1
+ require_relative '../asg/visitor'
2
+ require_relative 'instructions'
3
+ require_relative 'operands'
4
+
5
+ module RPiet
6
+ # Visit each node from ASG and translate into simpler more
7
+ # traditional instructions. These instructions will be
8
+ # translated into different back-ends.
9
+ class Builder < Visitor
10
+ DEBUG = false
11
+ include RPiet::IR::Instructions, RPiet::IR::Operands
12
+
13
+ attr_reader :instructions, :dp, :cc
14
+
15
+ def initialize
16
+ super
17
+ @instructions = []
18
+ @node_mappings = {} # instruction -> node
19
+ @jump_labels = {} # node -> label
20
+ @variable_counter = 0
21
+ @current_node = nil
22
+ add DPInstr.new(num(0))
23
+ add CCInstr.new(num(-1))
24
+ end
25
+
26
+ def run(graph)
27
+ super
28
+ # We need to preserve noops while building initial instrs because backward
29
+ # jumps may target the first instr for a graph node and that can be a noop.
30
+ #@instructions.delete_if { |instr| instr.operation == :noop }
31
+ end
32
+
33
+ def copy(variable, value, comment=nil)
34
+ add(CopyInstr.new(variable, value).tap do |instr|
35
+ instr.comment = comment if comment
36
+ end)
37
+ end
38
+
39
+ def visit_first(node)
40
+ @graph_node = node
41
+ @current_node = node
42
+ instructions_for node
43
+ super node
44
+ end
45
+
46
+ # def visit_first_pntr(node, worklist)
47
+ # # In stack make pntr = (pntr + 1) % 4
48
+ # @current_node = node
49
+ # @graph_node = node
50
+ # variable, dp = pop, acquire_variable
51
+ # add DPRotateInstr.new(dp, variable)
52
+ # # FIXME: n paths can go to same location so this should consider emitting to produce less jumps
53
+ # label(:"end_pntr#{@graph_node.step}") do |end_label|
54
+ # 4.times do |i|
55
+ # if i == 3
56
+ # next_label = end_label
57
+ # else
58
+ # segment_label = LabelInstr.new(:"pntr[#{i}]#{@graph_node.step}")
59
+ # next_label = segment_label.value
60
+ # end
61
+ # add BNEInstr.new dp, DirectionPointer.from_ordinal(i), next_label
62
+ # visit(worklist << node.paths[i])
63
+ # @graph_node = node
64
+ # add segment_label unless i == 3
65
+ # end
66
+ # end
67
+ #
68
+ # nil
69
+ # end
70
+
71
+ def visit_first_pntr(node, worklist)
72
+ # In stack make pntr = (pntr + 1) % 4
73
+ @current_node = node
74
+ @graph_node = node
75
+ variable, dp = pop, acquire_variable
76
+ add DPRotateInstr.new(dp, variable)
77
+ labels = []
78
+ 3.times do |i|
79
+ label = LabelInstr.new(:"pntr_#{i}_#{@graph_node.step}")
80
+ add BEQInstr.new dp, DirectionPointer.new(i), label.value
81
+ labels << label
82
+ end
83
+ label = LabelInstr.new(:"pntr_3_#{@graph_node.step}")
84
+ labels << label
85
+ jump label.value
86
+
87
+ 4.times do |i|
88
+ @graph_node = node
89
+ add labels[i]
90
+ visit(worklist << node.paths[i])
91
+ end
92
+ nil
93
+ end
94
+
95
+ def visit_first_swch(node, worklist)
96
+ @current_node = node
97
+ @graph_node = node
98
+ value, cc = pop, acquire_variable
99
+ add CCToggleInstr.new(cc, value)
100
+
101
+ # both swch paths goes to same location so eliminate the jumping logic
102
+ if node.paths[0] == node.paths[1]
103
+ visit(worklist << node.paths[0])
104
+ else
105
+ label(:"swch_left_#{@graph_node.step}") do |next_label|
106
+ add BNEInstr.new(cc, CodelChooser::LEFT, next_label)
107
+ visit(worklist << node.paths[0])
108
+ end
109
+ visit(worklist << node.paths[1])
110
+ end
111
+ nil
112
+ end
113
+
114
+ def visit_again(node)
115
+ label = @jump_labels[node]
116
+ @graph_node = node
117
+
118
+ unless label # first time to insert label
119
+ label_operand = :"re_#{node.object_id}"
120
+ label = LabelInstr.new(label_operand)
121
+ label.graph_node = node
122
+ @jump_labels[node] = label
123
+ index = @instructions.find_index(@node_mappings[node])
124
+ @instructions.insert index, label if index
125
+ else
126
+ label_operand = label.value
127
+ end
128
+
129
+ # This will be in proper place because all new nodes are added to
130
+ # end of instruction list.
131
+ jump = JumpInstr.new(label_operand)
132
+ jump.comment = "back to visited #{node} => #{label_operand}"
133
+ add(jump)
134
+ end
135
+
136
+ def add(instruction)
137
+ # For first instruction that represents a node from ASG we record it
138
+ # so when we encounter a cycle we know where we should jump.
139
+ if @current_node
140
+ @node_mappings[@current_node] = instruction
141
+ @current_node = nil
142
+ end
143
+
144
+ instruction.graph_node = @graph_node
145
+
146
+ @instructions << instruction
147
+ end
148
+
149
+ def num(value)
150
+ Integer(value)
151
+ end
152
+
153
+ def string(value)
154
+ value
155
+ end
156
+
157
+ def acquire_variable
158
+ VariableOperand.new("v#{@variable_counter += 1}")
159
+ end
160
+
161
+ def jump(label)
162
+ add JumpInstr.new label
163
+ end
164
+
165
+ def label(label_name)
166
+ saved_node = @current_node
167
+ label = LabelInstr.new(label_name)
168
+ yield label.value
169
+ @current_node = saved_node
170
+ add label
171
+ end
172
+
173
+ def mod(operand1, operand2)
174
+ acquire_variable.tap { |result| add ModInstr.new result, operand1, operand2 }
175
+ end
176
+
177
+ def mult(operand1, operand2)
178
+ acquire_variable.tap { |result| add MultInstr.new result, operand1, operand2 }
179
+ end
180
+
181
+ def plus(operand1, operand2)
182
+ acquire_variable.tap { |result| add AddInstr.new result, operand1, operand2 }
183
+ end
184
+
185
+ def pop
186
+ acquire_variable.tap { |variable| add PopInstr.new variable }
187
+ end
188
+
189
+ def pow(operand1, operand2)
190
+ acquire_variable.tap { |result| add PowInstr.new result, operand1, operand2 }
191
+ end
192
+
193
+ def push(operand)
194
+ add PushInstr.new operand
195
+ end
196
+
197
+ def instructions_for(node)
198
+ case node.operation
199
+ when :noop then add(NoopInstr.new)
200
+ when :push then push(num(node.value))
201
+ when :pop then pop
202
+ when :add then bin_op AddInstr
203
+ when :sub then bin_op SubInstr
204
+ when :mult then bin_op MultInstr
205
+ when :div then bin_op DivInstr
206
+ when :mod then bin_op ModInstr
207
+ when :nout then unary_op NoutInstr
208
+ when :cout then unary_op CoutInstr
209
+ when :gtr then
210
+ value2, value1, result = pop, pop, acquire_variable
211
+ add GTInstr.new result, value1, value2
212
+ push(result)
213
+ when :not then # REWRITE AS BEQ/BNE
214
+ value1, result = pop, acquire_variable
215
+ add NEInstr.new result, value1, num(0)
216
+ push(result)
217
+ when :dup then
218
+ variable = pop
219
+ push(variable)
220
+ push(variable)
221
+ when :cin then
222
+ # Can be written in terms in nin
223
+ variable = acquire_variable
224
+ add CinInstr.new variable
225
+ push(variable)
226
+ when :nin then
227
+ variable = acquire_variable
228
+ add NinInstr.new variable
229
+ push(variable)
230
+ when :swch then
231
+ @in_branch_oper, @in_branch = :swch, -1
232
+ when :roll then
233
+ variable1, variable2 = pop, pop
234
+ add RollInstr.new variable2, variable1
235
+ when :cc then
236
+ add CCInstr.new(num(node.value))
237
+ when :dp then
238
+ add DPInstr.new(num(node.value))
239
+ when :exit then
240
+ add ExitInstr.new
241
+ end
242
+
243
+ end
244
+
245
+ def bin_op(instr_class)
246
+ variable1, variable2, variable3 = pop, pop, acquire_variable
247
+ add instr_class.new variable3, variable2, variable1
248
+ push(variable3)
249
+ end
250
+
251
+ def unary_op(instr_class)
252
+ add instr_class.new(pop)
253
+ end
254
+ end
255
+ end