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
data/lib/rpiet/image/image.rb
CHANGED
@@ -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
|
22
|
-
|
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
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
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
|
-
|
24
|
-
|
36
|
+
def raw_pixel(x, y)
|
37
|
+
@raw[y][x]
|
38
|
+
end
|
25
39
|
end
|
26
40
|
end
|
27
41
|
end
|
data/lib/rpiet/interpreter.rb
CHANGED
@@ -1,63 +1,99 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
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, :
|
8
|
+
attr_reader :pvm, :source, :pixels, :x, :y, :step, :event_handler
|
9
9
|
|
10
|
-
def initialize(
|
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
|
-
|
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
|
-
|
33
|
-
|
63
|
+
return false if @abort
|
64
|
+
@pvm.block_value = @source.group_at(@x, @y).size
|
65
|
+
attempt = 1
|
34
66
|
seen_white = false
|
35
|
-
@
|
36
|
-
ex, ey
|
37
|
-
while
|
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
|
41
|
-
|
42
|
-
|
77
|
+
if !valid
|
78
|
+
@pvm.orient_elsewhere(attempt)
|
79
|
+
attempt += 1
|
43
80
|
|
44
|
-
ex, ey = @
|
81
|
+
ex, ey = @source.group_at(@x, @y).point_for(@pvm) if !seen_white
|
45
82
|
@event_handler.trying_again(self, ex, ey)
|
46
|
-
|
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
|
-
|
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
|
-
|
71
|
-
|
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
|
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
|