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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f899ca8b426c587720e0f92d070276e4f4290f6c06129768d7ce76f0f4c8aa44
4
+ data.tar.gz: a595f1cd225d3bfe78212e9d637023fd88edad148ce5376b0e57bea27f3d4844
5
+ SHA512:
6
+ metadata.gz: 494ead01ba5971cf780765a0527bd5be690897701a9e025a94943b3e3bc1a07a63bdd98ba0b4ee4455935de44b56c0b462c8c8de0ddaf49ab9fc700f5345dd39
7
+ data.tar.gz: 9d65d740f440808d57854cceceede0a8e07b7713833501ae8bf14c0b5680515188e7e7b4e1b4ca767c8cc0bc5fec123b260ab41af3fd34ceea1bc692ca9eec9b
data/.gitignore CHANGED
@@ -7,3 +7,4 @@ manifest.mf
7
7
  dist
8
8
  build
9
9
  nbproject
10
+ .idea/
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'jrubyfx', platform: :jruby
6
+ gem 'image_voodoo', platform: :jruby
7
+ gem 'mini_magick', platform: :ruby
8
+ gem 'rgl'
9
+
10
+ group :development do
11
+ gem 'rspec'
12
+ end
data/bin/color_wheel ADDED
@@ -0,0 +1,84 @@
1
+ #!/bin/env ruby
2
+ require 'rpiet/color'
3
+ require 'rpiet/image/ascii_image'
4
+
5
+
6
+ def die(message)
7
+ puts message
8
+ exit -1
9
+ end
10
+
11
+ def decode(item)
12
+ item, dimensions = item.split(':') if item.include? ':'
13
+
14
+ if dimensions
15
+ if dimensions.include? 'x'
16
+ rows, cols = dimensions.split('x').map &:to_i
17
+ else
18
+ rows, cols = 1, dimensions.to_i
19
+ end
20
+ else
21
+ rows, cols = 1, 1
22
+ end
23
+
24
+ [item, rows, cols]
25
+ end
26
+
27
+ # Starting color
28
+ color_name = ARGV.shift
29
+ commands = ARGV
30
+ color_name, rows, cols = decode(color_name)
31
+
32
+ rgb = RPiet::Image::AsciiImage::STR2RGB[color_name]
33
+ die "Invalid color name: '#{color_name}'" unless rgb
34
+
35
+ color = RPiet::Color.color_for(format "0x%02x%02x%02x" % rgb)
36
+
37
+ CHANGES = {
38
+ noop: [0, 0],
39
+ push: [0, 1],
40
+ pop: [0, 2],
41
+ add: [1, 0],
42
+ sub: [1, 1],
43
+ mult: [1, 2],
44
+ div: [2, 0],
45
+ mod: [2, 1],
46
+ not: [2, 2],
47
+ gtr: [3, 0],
48
+ pntr: [3, 1],
49
+ swch: [3, 2],
50
+ dup: [4, 0],
51
+ roll: [4, 1],
52
+ nin: [4, 2],
53
+ cin: [5, 0],
54
+ nout: [5, 1],
55
+ cout: [5, 2]
56
+ }
57
+
58
+
59
+ total_rows, total_cols = rows, cols
60
+ groups = [[color, rows, cols]]
61
+ commands.each do |command|
62
+ command, rows, cols = decode command
63
+ deltas = CHANGES[command.to_sym]
64
+ die "Invalid command: '#{command}'" unless deltas
65
+ color = color.color_from(hue_delta: deltas[0], lightness_delta: deltas[1])
66
+ groups << [color, rows, cols]
67
+ total_rows = rows if rows > total_rows
68
+ total_cols += cols
69
+ end
70
+
71
+ image = Array.new(total_rows) { Array.new(total_cols) {'++'} }
72
+
73
+ col_index, row_index = 0, 0
74
+ groups.each do |color, rows, cols|
75
+ id = color.to_initial
76
+ rows.times do |j|
77
+ cols.times do |i|
78
+ image[row_index + j][col_index + i] = id
79
+ end
80
+ end
81
+ col_index += cols
82
+ end
83
+
84
+ image.each { |rows| puts rows.join(' ') }
data/bin/image_gen ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'rpiet/image/ascii_image'
5
+ require 'image_voodoo'
6
+
7
+ input_codel_size = 1
8
+ output_codel_size = input_codel_size
9
+ image_type = "png"
10
+ opts = OptionParser.new do |opts|
11
+ opts.on("-i", "--input-codel-size w", "size of input codel") { |w| input_codel_size = w.to_i }
12
+ opts.on("-o", "--output-odel-size w", "size of output codel") { |w| output_codel_size = w.to_i }
13
+ opts.on("-t", "--image-type suffix", "image format") { |t| image_type = t }
14
+ end
15
+
16
+ opts.parse!(ARGV)
17
+ unless ARGV.first
18
+ puts "You need to supply a source image filename."
19
+ puts opts
20
+ exit -1
21
+ end
22
+
23
+ filename = ARGV.first
24
+ file_contents = filename == '-' ? $stdin.read(nil) : File.read(filename)
25
+
26
+ image = RPiet::Image::AsciiImage.new(file_contents, input_codel_size)
27
+ width, height = image.size
28
+
29
+ canvas = ImageVoodoo.canvas(output_codel_size * width, output_codel_size * height, 'ffffff')
30
+
31
+ image.each do |i, j, color|
32
+ canvas.rect i * output_codel_size, j * output_codel_size, output_codel_size, output_codel_size, color.rgb[2..-1]
33
+ end
34
+
35
+ if filename == '-'
36
+ $stdout.write canvas.bytes('png')
37
+ else
38
+ canvas.save(File.basename(filename, ".txt") + "." + image_type)
39
+ end
data/bin/rpiet CHANGED
@@ -2,29 +2,86 @@
2
2
 
3
3
  require 'optparse'
4
4
  require 'rpiet'
5
- require 'rpiet/image/url_image'
6
5
 
6
+ run = :chowder
7
+ time_execution = false
7
8
  codel_size = 1
8
9
 
9
- log = RPiet::Logger::NoOutput
10
+ dump = nil
11
+
12
+ log = nil
10
13
  opts = OptionParser.new do |opts|
11
14
  opts.banner = "Usage: #{__FILE__} -d image_file"
12
15
 
16
+ opts.on("-t", "--time", "time execution") { time_execution = true }
17
+ opts.on("-r", "--runtime runtime") do |runtime|
18
+ run = case runtime
19
+ when 'native' then :native
20
+ when 'graph' then :graph
21
+ when 'ir' then :ir
22
+ when 'cfg' then :cfg
23
+ when 'chowder' then :chowder
24
+ else raise ArgumentError.new("invalid runtime specified: #{runtime}")
25
+ end
26
+
27
+ end
28
+ opts.on("-I", "--dump-ir", "Dump image as IR instructions") { dump = :ir }
13
29
  opts.on("-d", "--debug style", "turn on debugging") do |style|
14
30
  log = case style
15
- when 'simple'
16
- RPiet::Logger::SimpleAsciiOutput
17
- when 'complex'
18
- RPiet::Logger::ComplexAsciiOutput
19
- end
31
+ when 'simple' then RPiet::Logger::SimpleAsciiOutput.new
32
+ when 'complex' then RPiet::Logger::ComplexAsciiOutput.new
33
+ when 'graphical' then RPiet::Logger::Graphical.new
34
+ else raise ArgumentError.new("invalid logger specified: #{style}")
35
+ end
20
36
  end
21
37
  opts.on("-c", "--codel-size w", "size of codel") { |w| codel_size = w.to_i }
22
38
  end
23
39
 
24
40
  opts.parse!(ARGV)
25
- opts.usage("You need to supply a source image filename.") unless ARGV.first
41
+ unless ARGV.first
42
+ puts "You need to supply a source image filename."
43
+ puts opts
44
+ exit -1
45
+ end
26
46
 
27
47
  filename = ARGV.first
28
- filename = 'file:' + ARGV.first if File.exist? filename
29
- image = RPiet::Image::URLImage.new(filename, codel_size)
30
- RPiet::Interpreter.new(image, log.new).run
48
+
49
+ if filename =~ /.txt/
50
+ require 'rpiet/image/ascii_image'
51
+ image = RPiet::Image::AsciiImage.new(File.read(filename), codel_size)
52
+ elsif filename == '-'
53
+ require 'rpiet/image/ascii_image'
54
+ image = RPiet::Image::AsciiImage.new($stdin.gets(nil), codel_size)
55
+ else
56
+ filename = 'file:' + filename if File.exist? filename
57
+ require 'rpiet/image/url_image'
58
+ image = RPiet::Image::URLImage.new(filename, codel_size)
59
+ end
60
+
61
+
62
+ runner = case run
63
+ when :graph
64
+ require 'rpiet/asg/graph_interpreter'
65
+ RPiet::ASG::GraphInterpreter.new(image, log)
66
+ when :ir
67
+ require 'rpiet/ir/ir_interpreter'
68
+ RPiet::IR::IRInterpreter.new(image, log)
69
+ when :cfg
70
+ require 'rpiet/ir/ir_cfg_interpreter'
71
+ RPiet::IR::IRCFGInterpreter.new(image, log)
72
+ when :native
73
+ require 'rpiet/ir/ir_native_interpreter'
74
+ RPiet::IR::IRNativeInterpreter.new(image, log)
75
+ else
76
+ RPiet::Interpreter.new(image, log)
77
+ end
78
+
79
+ if dump && (run == :ir || run == :cfg)
80
+ puts runner.disasm
81
+ else
82
+ start = Time.now if time_execution
83
+ runner.run
84
+ puts "time: #{Time.now - start}s" if time_execution
85
+ end
86
+
87
+ exit 0
@@ -0,0 +1,7 @@
1
+ lr db .. nc dc dc dc dc dc
2
+ ++ ++ ++ ++ ++ nc ++ ++ lb
3
+ ++ ny ++ ++ ++ .. ++ ++ lb
4
+ ny ny ny ++ ++ .. ++ ++ lg
5
+ ++ ny ++ ++ ++ .. ++ ++ ng
6
+ ++ ny .. .. .. nc lr lc dg
7
+
@@ -0,0 +1,39 @@
1
+ require_relative '../asg'
2
+ require_relative 'parser'
3
+
4
+ module RPiet
5
+ module ASG
6
+ class GraphInterpreter
7
+ include LiveMachineState
8
+
9
+ def initialize(image, event_handler = nil)
10
+ Node.handle_event_handler(@event_handler = event_handler)
11
+ # FIXME: need a way to debug parser conditionally from command line
12
+ @graph = Parser.new(image).run
13
+ end
14
+
15
+ def reset
16
+ reset_machine()
17
+ @node = @graph
18
+ end
19
+
20
+ def next_step
21
+ while @node && @node.hidden?
22
+ @node = @node.exec self
23
+ end
24
+ @node = @node.exec self if @node
25
+ end
26
+
27
+ def running?
28
+ @node
29
+ end
30
+
31
+ def run
32
+ reset
33
+ while running? do
34
+ next_step
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,156 @@
1
+ require_relative '../color'
2
+ require_relative '../machine'
3
+ require_relative '../source'
4
+ require_relative '../event_handler'
5
+ require_relative '../asg'
6
+
7
+ module RPiet
8
+ module ASG
9
+ class Parser
10
+ ##
11
+ # Represents the transition to the next possible group from
12
+ # the current group. The direction is directed by the cc
13
+ # and dp values. The node passed in is the previously created
14
+ # node. The result of this translation will get pointed to by
15
+ # the supplied node.
16
+ class Transition
17
+ attr_reader :group, :cc_ordinal, :dp_ordinal
18
+ attr_accessor :node
19
+
20
+ def initialize(group, cc_ordinal, dp_ordinal, node)
21
+ @group, @cc_ordinal, @dp_ordinal, @node = group, cc_ordinal, dp_ordinal, node
22
+ end
23
+
24
+ def eql?(other)
25
+ other.group == @group && other.cc_ordinal == @cc_ordinal && other.dp_ordinal == @dp_ordinal
26
+ end
27
+
28
+ def hash
29
+ @cc_ordinal ^ @group.hash ^ @dp_ordinal
30
+ end
31
+
32
+ def inspect
33
+ "#{group}: #{@cc_ordinal}=#{@dp_ordinal}"
34
+ end
35
+
36
+ alias :to_s :inspect
37
+ end
38
+
39
+ attr_reader :pvm, :source, :pixels, :x, :y, :step
40
+
41
+ def initialize(image, event_handler = RPiet::Logger::NoOutput.new)
42
+ @source, @event_handler = RPiet::Source.new(image), event_handler
43
+ @x, @y, @pvm, @step = 0, 0, RPiet::Machine.new, 1
44
+ @work_list = []
45
+ @already_visited = {} # state => node
46
+ @graph = Node.create(@step, @x, @y, :noop)
47
+ @nodes = {}
48
+ end
49
+
50
+ def run
51
+ @work_list.push Transition.new(@source.group_at(@x, @y), @pvm.cc.ordinal, @pvm.dp.ordinal, @graph)
52
+ while !@work_list.empty?
53
+ restore_state(@work_list.pop)
54
+ next_step
55
+ end
56
+ @graph
57
+ end
58
+
59
+ ##
60
+ # Add state to graph. If we have seen it we link to it otherwise we add to worklist.
61
+ def add_state(state)
62
+ if @already_visited[state]
63
+ state.node.add_path @already_visited[state], state.cc_ordinal, state.dp_ordinal
64
+ elsif !@work_list.include?(state)
65
+ @work_list.push state
66
+ end
67
+ end
68
+
69
+ def restore_state(state)
70
+ @x, @y = state.group.rl
71
+ @pvm.cc.from_ordinal! state.cc_ordinal
72
+ @pvm.dp.from_ordinal! state.dp_ordinal
73
+ @current_state = state
74
+ end
75
+
76
+ def create_node(step, x, y, operation, block_value)
77
+ node = Node.create(step, x, y, operation, block_value)
78
+ return @nodes[node] if @nodes[node]
79
+ @nodes[node] = node
80
+ node
81
+ end
82
+
83
+ def next_step
84
+ @pvm.block_value = @source.group_at(@x, @y).size
85
+ attempt = 1
86
+ seen_white = false
87
+ ex, ey = @source.group_at(@x, @y).point_for(@pvm) # Exit point from group
88
+ @event_handler.step_begin(self, ex, ey)
89
+ while attempt <= 8 do
90
+ nx, ny = @pvm.next_possible(ex, ey) # where we enter Next group
91
+ valid = @source.valid?(nx, ny)
92
+ @event_handler.next_possible(self, ex, ey, nx, ny, valid)
93
+
94
+ if !valid
95
+ @pvm.orient_elsewhere(attempt)
96
+ attempt += 1
97
+
98
+ ex, ey = @source.group_at(@x, @y).point_for(@pvm) if !seen_white
99
+ @event_handler.trying_again(self, ex, ey)
100
+ elsif @source.pixels[nx][ny] == RPiet::Color::WHITE
101
+ if !seen_white
102
+ seen_white = true
103
+ attempt = 1
104
+ @event_handler.seen_white(self)
105
+ end
106
+ ex, ey = nx, ny
107
+ else
108
+ if !seen_white
109
+ operation = @pvm.calculate_operation @source.pixels[nx][ny], @source.pixels[@x][@y]
110
+ else
111
+ operation = :noop
112
+ end
113
+
114
+ @step += 1
115
+
116
+ if @current_state.dp_ordinal != @pvm.dp.ordinal
117
+ node = Node.create(@step, nx, ny, :dp, @pvm.cc.ordinal, @pvm.dp.ordinal)
118
+ @current_state.node.add_path(node, @current_state.cc_ordinal, @current_state.dp_ordinal)
119
+ @current_state.node = node
120
+ @already_visited[@current_state] = node
121
+ end
122
+ if @current_state.cc_ordinal != @pvm.cc.ordinal
123
+ node = Node.create(@step, nx, ny, :cc, @pvm.cc.ordinal, @pvm.dp.ordinal)
124
+ @current_state.node.add_path(node, @current_state.cc_ordinal, @current_state.dp_ordinal)
125
+ @current_state.node = node
126
+ @already_visited[@current_state] = node unless @already_visited[@current_state]
127
+ end
128
+
129
+ node = create_node(@step, nx, ny, operation, @pvm.block_value)
130
+ @current_state.node.add_path(node, @current_state.cc_ordinal, @current_state.dp_ordinal)
131
+ @already_visited[@current_state] = node unless @already_visited[@current_state]
132
+
133
+ @x, @y = nx, ny
134
+ group = @source.group_at(@x, @y)
135
+
136
+ case operation
137
+ when :swch
138
+ add_state Transition.new(group, -1, @pvm.dp.ordinal, node)
139
+ add_state Transition.new(group, 1, @pvm.dp.ordinal, node)
140
+ when :pntr
141
+ add_state Transition.new(group, @pvm.cc.ordinal, 0, node)
142
+ add_state Transition.new(group, @pvm.cc.ordinal, 1, node)
143
+ add_state Transition.new(group, @pvm.cc.ordinal, 2, node)
144
+ add_state Transition.new(group, @pvm.cc.ordinal, 3, node)
145
+ else
146
+ add_state Transition.new(group, @pvm.cc.ordinal, @pvm.dp.ordinal, node)
147
+ end
148
+ return
149
+ end
150
+ end
151
+ @current_state.node.add_path(ExitNode.new(step, x, y))
152
+ @event_handler.execution_completed(self)
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,66 @@
1
+ require 'set'
2
+
3
+ module RPiet
4
+ # Generic visitor for visiting all nodes in the graph once but
5
+ # then also visiting all nodes which complete a cycle a second time.
6
+ class Visitor
7
+ def initialize
8
+ @visited = Set.new
9
+ end
10
+
11
+ def run(root)
12
+ worklist = [root]
13
+
14
+ visit worklist
15
+ end
16
+
17
+ def visit(worklist)
18
+ while !worklist.empty?
19
+ node = worklist.pop
20
+ next unless node
21
+ already_visited = @visited.include? node
22
+
23
+ if already_visited
24
+ visit_again node
25
+ else
26
+ @visited.add node
27
+ if node # child of nil means exit
28
+ if node.operation == :pntr
29
+ work_items = visit_first_pntr node, worklist
30
+ elsif node.operation == :swch
31
+ work_items = visit_first_swch node, worklist
32
+ else
33
+ work_items = visit_first node
34
+ end
35
+ worklist.concat work_items if work_items && !work_items.empty?
36
+ end
37
+ end
38
+ break if worklist.empty?
39
+ end
40
+ end
41
+
42
+ def visit_children(node)
43
+ node.paths.each do |next_node|
44
+ next_node.visit self if next_node
45
+ end
46
+ end
47
+
48
+ def visit_first(node)
49
+ node.paths
50
+ end
51
+
52
+ # First time we encounter an individual swch node
53
+ def visit_first_swch(node, worklist=nil)
54
+ node.paths
55
+ end
56
+
57
+ # First time we encounter an individual pntr node
58
+ def visit_first_pntr(node, worklist=nil)
59
+ node.paths
60
+ end
61
+
62
+ # Second time we encounter any ndoe
63
+ def visit_again(node)
64
+ end
65
+ end
66
+ end