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