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
@@ -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