seafoam 0.7 → 0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bgv2isabelle +1 -5
  3. data/bin/bgv2json +1 -5
  4. data/bin/cfg2asm +1 -5
  5. data/bin/seafoam +1 -5
  6. data/lib/seafoam/bgv/bgv_parser.rb +11 -4
  7. data/lib/seafoam/commands.rb +140 -58
  8. data/lib/seafoam/formatters/base.rb +88 -0
  9. data/lib/seafoam/formatters/formatters.rb +8 -0
  10. data/lib/seafoam/formatters/json.rb +82 -0
  11. data/lib/seafoam/formatters/text.rb +70 -0
  12. data/lib/seafoam/graal/graph_description.rb +22 -0
  13. data/lib/seafoam/graal/pi.rb +18 -0
  14. data/lib/seafoam/graal/source.rb +5 -9
  15. data/lib/seafoam/graph.rb +33 -1
  16. data/lib/seafoam/graphviz_writer.rb +24 -2
  17. data/lib/seafoam/json_writer.rb +1 -3
  18. data/lib/seafoam/{annotators → passes}/fallback.rb +4 -4
  19. data/lib/seafoam/{annotators → passes}/graal.rb +43 -15
  20. data/lib/seafoam/passes/truffle.rb +58 -0
  21. data/lib/seafoam/passes.rb +61 -0
  22. data/lib/seafoam/version.rb +1 -1
  23. data/lib/seafoam.rb +7 -4
  24. metadata +34 -63
  25. data/.github/probots.yml +0 -2
  26. data/.github/workflows/workflows.yml +0 -39
  27. data/.gitignore +0 -7
  28. data/.rubocop.yml +0 -37
  29. data/.ruby-version +0 -1
  30. data/.seafoam/config +0 -1
  31. data/CODE_OF_CONDUCT.md +0 -128
  32. data/CONTRIBUTING.md +0 -5
  33. data/Gemfile +0 -2
  34. data/LICENSE.md +0 -7
  35. data/README.md +0 -378
  36. data/demos/box-unbox-stats +0 -65
  37. data/docs/annotators.md +0 -43
  38. data/docs/bgv.md +0 -293
  39. data/docs/getting-graphs.md +0 -59
  40. data/docs/images/igv.png +0 -0
  41. data/docs/images/seafoam.png +0 -0
  42. data/docs/images/spotlight-igv.png +0 -0
  43. data/docs/images/spotlight-seafoam.png +0 -0
  44. data/docs/json.md +0 -35
  45. data/examples/Fib.java +0 -24
  46. data/examples/MatMult.java +0 -39
  47. data/examples/fib-java.bgv.gz +0 -0
  48. data/examples/fib.js +0 -15
  49. data/examples/fib.rb +0 -15
  50. data/examples/identity.rb +0 -13
  51. data/examples/java/Irreducible.class +0 -0
  52. data/examples/java/Irreducible.j +0 -35
  53. data/examples/java/IrreducibleDecompiled.java +0 -21
  54. data/examples/java/JavaExamples.java +0 -418
  55. data/examples/matmult.rb +0 -29
  56. data/examples/overflow.rb +0 -13
  57. data/examples/ruby/clamps.rb +0 -20
  58. data/examples/ruby/graal.patch +0 -15
  59. data/examples/ruby/ruby_examples.rb +0 -278
  60. data/lib/seafoam/annotators.rb +0 -54
  61. data/lib/seafoam/config.rb +0 -34
  62. data/seafoam.gemspec +0 -22
  63. data/spec/seafoam/annotators/fallback_spec.rb +0 -69
  64. data/spec/seafoam/annotators/graal_spec.rb +0 -96
  65. data/spec/seafoam/annotators_spec.rb +0 -61
  66. data/spec/seafoam/bgv/bgv_parser_spec.rb +0 -157
  67. data/spec/seafoam/binary/io_binary_reader_spec.rb +0 -176
  68. data/spec/seafoam/cfg/cfg_parser_spec.rb +0 -21
  69. data/spec/seafoam/cfg/disassembler_spec.rb +0 -32
  70. data/spec/seafoam/command_spec.rb +0 -316
  71. data/spec/seafoam/graph_spec.rb +0 -172
  72. data/spec/seafoam/graphviz_writer_spec.rb +0 -63
  73. data/spec/seafoam/json_writer_spec.rb +0 -14
  74. data/spec/seafoam/spec_helpers.rb +0 -34
  75. data/spec/seafoam/spotlight_spec.rb +0 -38
  76. data/tools/render-all +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a17a259b362af5eef7c3c874e6d796f647c6017d413cba708ac0a27e18043681
4
- data.tar.gz: 39eba465ba4ed92b3085a276d541072ccff48d62fdd00a317961c7401bcb0ee0
3
+ metadata.gz: bc92671f5ea437e5afccf98c5874434251e6df05fde08e325e0b8b4a60d5d2b0
4
+ data.tar.gz: 77a8f71d631be2c02674784598a97d7da4b83c4556f2fc21c5d66496678b3792
5
5
  SHA512:
6
- metadata.gz: e55cdfa1eea817f24bbaa22ef80916923441393529351df21c258987354cafe98d07ed4bff2fd3e6fc5ab5656ea7f85b938d7a906ee511cd9f06fa19c335431f
7
- data.tar.gz: '070468e7e9c1fd55b6429c07d8c226e1c0c0e4225a0f9a90bd577e7b57bb8320bcd0a59e1da223632986245f6eb5482a1320b11e6b786f41f456187766664b9b'
6
+ metadata.gz: 2c9d7b5ccea117d2b1b686d66fffbeebe4282dde52f5c9880125f57911f4da4ed2e7660f1957597931e5bf714258b0c1a08e6c9fc0bfbc822915b2ae22c08964
7
+ data.tar.gz: 2c991ed3d87e80ccdb92630dfc3d2283b01ed2125c70883eec7b5e0cd6031d55acbb8930feb7cf6ef6e029b0e58ff2b01b2d2c81c0a2a0bca266070a2c28ba2c
data/bin/bgv2isabelle CHANGED
@@ -5,12 +5,8 @@ require 'seafoam'
5
5
  # This is the 'bgv2isabelle' command line entry point.
6
6
 
7
7
  begin
8
- # Load configuraiton.
9
- config = Seafoam::Config.new
10
- config.load_config
11
-
12
8
  # Run the command line.
13
- commands = Seafoam::Commands.new($stdout, config)
9
+ commands = Seafoam::Commands.new($stdout)
14
10
  commands.bgv2isabelle(*ARGV)
15
11
  rescue StandardError => e
16
12
  if $DEBUG
data/bin/bgv2json CHANGED
@@ -5,12 +5,8 @@ require 'seafoam'
5
5
  # This is the 'bgv2json' command line entry point.
6
6
 
7
7
  begin
8
- # Load configuraiton.
9
- config = Seafoam::Config.new
10
- config.load_config
11
-
12
8
  # Run the command line.
13
- commands = Seafoam::Commands.new($stdout, config)
9
+ commands = Seafoam::Commands.new($stdout)
14
10
  commands.bgv2json(*ARGV)
15
11
  rescue StandardError => e
16
12
  if $DEBUG
data/bin/cfg2asm CHANGED
@@ -5,12 +5,8 @@ require 'seafoam'
5
5
  # This is the 'cfg2asm' command line entry point.
6
6
 
7
7
  begin
8
- # Load configuraiton.
9
- config = Seafoam::Config.new
10
- config.load_config
11
-
12
8
  # Run the command line.
13
- commands = Seafoam::Commands.new($stdout, config)
9
+ commands = Seafoam::Commands.new($stdout)
14
10
  commands.cfg2asm(*ARGV)
15
11
  rescue StandardError => e
16
12
  if $DEBUG
data/bin/seafoam CHANGED
@@ -5,12 +5,8 @@ require 'seafoam'
5
5
  # This is the 'seafoam' command line entry point.
6
6
 
7
7
  begin
8
- # Load configuraiton.
9
- config = Seafoam::Config.new
10
- config.load_config
11
-
12
8
  # Run the command line.
13
- commands = Seafoam::Commands.new($stdout, config)
9
+ commands = Seafoam::Commands.new($stdout)
14
10
  commands.seafoam(*ARGV)
15
11
  rescue StandardError => e
16
12
  if $DEBUG
@@ -127,13 +127,21 @@ module Seafoam
127
127
  end
128
128
  end
129
129
  end
130
- skip_blocks
130
+
131
+ # Read block information.
132
+ @reader.read_sint32.times do
133
+ block_id = @reader.read_sint32
134
+ block_nodes = @reader.read_sint32.times.map { @reader.read_sint32 }
135
+ # Followers aren't used but could be.
136
+ @reader.read_sint32.times.map { @reader.read_sint32 }
137
+ graph.create_block block_id, block_nodes
138
+ end
131
139
  graph
132
140
  end
133
141
 
134
142
  # Skip over a graph, having read or skipped its headers.
135
143
  def skip_graph
136
- # Already read BEGIN_GRAPH, id, format, args, and props
144
+ # Already read BEGIN_GRAPH, id, format, args, and props.
137
145
  @reader.read_sint32.times do
138
146
  @reader.skip_int32
139
147
  node_class = read_pool_object
@@ -154,8 +162,7 @@ module Seafoam
154
162
  count += 1
155
163
  arg
156
164
  end
157
- components = groups_names + [name]
158
- components.join('/')
165
+ groups_names + [name]
159
166
  end
160
167
 
161
168
  private
@@ -1,38 +1,29 @@
1
1
  require 'json'
2
+ require 'set'
2
3
 
3
4
  module Seafoam
4
5
  # Implementations of the command-line commands that you can run in Seafoam.
5
6
  class Commands
6
- def initialize(out, config)
7
+ def initialize(out)
7
8
  @out = out
8
- @config = config
9
9
  end
10
10
 
11
11
  # Run the general seafoam command.
12
12
  def seafoam(*args)
13
13
  first, *args = args
14
+
15
+ if first == '--json'
16
+ formatter_module = Seafoam::Formatters::Json
17
+ first, *args = args
18
+ else
19
+ formatter_module = Seafoam::Formatters::Text
20
+ end
21
+
14
22
  case first
15
23
  when nil, 'help', '-h', '--help', '-help'
16
24
  raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
17
25
 
18
- @out.puts 'seafoam file.bgv info'
19
- @out.puts ' file.bgv list'
20
- @out.puts ' file.bgv[:graph][:node[-edge]] search term...'
21
- @out.puts ' file.bgv[:graph][:node[-edge]] edges'
22
- @out.puts ' file.bgv[:graph][:node[-edge]] props'
23
- @out.puts ' file.bgv:graph:node source'
24
- @out.puts ' file.bgv:graph render'
25
- @out.puts ' --spotlight n,n,n...'
26
- @out.puts ' --out graph.pdf'
27
- @out.puts ' graph.svg'
28
- @out.puts ' graph.png'
29
- @out.puts ' graph.dot'
30
- @out.puts ' --show-frame-state'
31
- @out.puts ' --hide-floating'
32
- @out.puts ' --no-reduce-edges'
33
- @out.puts ' --option key value'
34
- @out.puts ' --help'
35
- @out.puts ' --version'
26
+ help(*args)
36
27
  when 'version', '-v', '-version', '--version'
37
28
  version(*args)
38
29
  else
@@ -42,21 +33,23 @@ module Seafoam
42
33
  when nil
43
34
  help(*args)
44
35
  when 'info'
45
- info name, *args
36
+ info name, formatter_module, *args
46
37
  when 'list'
47
- list name, *args
38
+ list name, formatter_module, *args
48
39
  when 'search'
49
40
  search name, *args
50
41
  when 'edges'
51
- edges name, *args
42
+ edges name, formatter_module, *args
52
43
  when 'props'
53
44
  props name, *args
54
45
  when 'source'
55
- source name, *args
46
+ source name, formatter_module, *args
56
47
  when 'render'
57
48
  render name, *args
58
49
  when 'debug'
59
50
  debug name, *args
51
+ when 'describe'
52
+ describe name, formatter_module, *args
60
53
  else
61
54
  raise ArgumentError, "unknown command #{command}"
62
55
  end
@@ -201,35 +194,39 @@ module Seafoam
201
194
  private
202
195
 
203
196
  # seafoam file.bgv info
204
- def info(name, *args)
197
+ def info(name, formatter_module, *args)
205
198
  file, *rest = parse_name(name)
206
199
  raise ArgumentError, 'info only works with a file' unless rest == [nil, nil, nil]
207
-
208
200
  raise ArgumentError, 'info does not take arguments' unless args.empty?
209
201
 
210
202
  parser = BGV::BGVParser.new(file)
211
203
  major, minor = parser.read_file_header(version_check: false)
212
- @out.puts "BGV #{major}.#{minor}"
204
+ formatter = formatter_module::InfoFormatter.new(major, minor)
205
+
206
+ @out.puts formatter.format
213
207
  end
214
208
 
215
209
  # seafoam file.bgv list
216
- def list(name, *args)
210
+ def list(name, formatter_module, *args)
217
211
  file, *rest = parse_name(name)
218
212
  raise ArgumentError, 'list only works with a file' unless rest == [nil, nil, nil]
219
-
220
213
  raise ArgumentError, 'list does not take arguments' unless args.empty?
221
214
 
222
215
  parser = BGV::BGVParser.new(file)
223
216
  parser.read_file_header
224
217
  parser.skip_document_props
218
+ entries = []
225
219
  loop do
226
220
  index, = parser.read_graph_preheader
227
221
  break unless index
228
222
 
229
223
  graph_header = parser.read_graph_header
230
- @out.puts "#{file}:#{index} #{parser.graph_name(graph_header)}"
224
+ entries << formatter_module::ListFormatter::Entry.new(file, parser.graph_name(graph_header), index)
231
225
  parser.skip_graph
232
226
  end
227
+
228
+ formatter = formatter_module::ListFormatter.new(entries)
229
+ @out.puts formatter.format
233
230
  end
234
231
 
235
232
  # seafoam file.bgv:n... search term...
@@ -262,7 +259,7 @@ module Seafoam
262
259
  end
263
260
 
264
261
  def search_object(tag, object, terms)
265
- full_text = JSON.generate(object)
262
+ full_text = JSON.generate(JSONWriter.prepare_json(object))
266
263
  full_text_down = full_text.downcase
267
264
  start = 0
268
265
  terms.each do |t|
@@ -288,17 +285,18 @@ module Seafoam
288
285
  end
289
286
 
290
287
  # seafoam file.bgv:n... edges
291
- def edges(name, *args)
288
+ def edges(name, formatter_module, *args)
292
289
  file, graph_index, node_id, edge_id = parse_name(name)
293
290
  raise ArgumentError, 'edges needs at least a graph' unless graph_index
294
-
295
291
  raise ArgumentError, 'edges does not take arguments' unless args.empty?
296
292
 
293
+ entry = nil
294
+
297
295
  with_graph(file, graph_index) do |parser|
298
296
  parser.read_graph_header
299
297
  graph = parser.read_graph
300
298
  if node_id
301
- Annotators.apply graph
299
+ Passes.apply graph
302
300
  node = graph.nodes[node_id]
303
301
  raise ArgumentError, 'node not found' unless node
304
302
 
@@ -309,24 +307,18 @@ module Seafoam
309
307
  edges = node.outputs.select { |edge| edge.to == to }
310
308
  raise ArgumentError, 'edge not found' if edges.empty?
311
309
 
312
- edges.each do |edge|
313
- @out.puts "#{edge.from.id_and_label} ->(#{edge.props[:label]}) #{edge.to.id_and_label}"
314
- end
310
+ entry = formatter_module::EdgesFormatter::EdgesEntry.new(edges)
315
311
  else
316
- @out.puts 'Input:'
317
- node.inputs.each do |input|
318
- @out.puts " #{node.id_and_label} <-(#{input.props[:label]}) #{input.from.id_and_label}"
319
- end
320
- @out.puts 'Output:'
321
- node.outputs.each do |output|
322
- @out.puts " #{node.id_and_label} ->(#{output.props[:label]}) #{output.to.id_and_label}"
323
- end
312
+ entry = formatter_module::EdgesFormatter::NodeEntry.new(node)
324
313
  end
325
314
  break
326
315
  else
327
- @out.puts "#{graph.nodes.count} nodes, #{graph.edges.count} edges"
316
+ entry = formatter_module::EdgesFormatter::SummaryEntry.new(graph.nodes.count, graph.edges.count)
328
317
  end
329
318
  end
319
+
320
+ formatter = formatter_module::EdgesFormatter.new(entry)
321
+ @out.puts formatter.format
330
322
  end
331
323
 
332
324
  # seafoam file.bgv... props
@@ -375,7 +367,7 @@ module Seafoam
375
367
  end
376
368
 
377
369
  # seafoam file.bgv:n:n source
378
- def source(name, *args)
370
+ def source(name, formatter_module, *args)
379
371
  file, graph_index, node_id, edge_id = parse_name(name)
380
372
  raise ArgumentError, 'source needs a node' unless node_id
381
373
  raise ArgumentError, 'source only works with a node' if edge_id
@@ -387,7 +379,57 @@ module Seafoam
387
379
  node = graph.nodes[node_id]
388
380
  raise ArgumentError, 'node not found' unless node
389
381
 
390
- @out.puts Graal::Source.render(node.props['nodeSourcePosition'])
382
+ formatter = formatter_module::SourceFormatter.new(node.props['nodeSourcePosition'])
383
+ @out.puts formatter.format
384
+ end
385
+ end
386
+
387
+ # seafoam file.bgv:n describe
388
+ def describe(name, formatter_module, *args)
389
+ file, graph_index, *rest = parse_name(name)
390
+
391
+ if graph_index.nil? || !rest.all?(&:nil?)
392
+ raise ArgumentError, 'describe only works with a graph'
393
+ end
394
+ raise ArgumentError, 'describe does not take arguments' unless args.empty?
395
+
396
+ parser = BGV::BGVParser.new(file)
397
+ parser.read_file_header
398
+ parser.skip_document_props
399
+
400
+ loop do
401
+ index, = parser.read_graph_preheader
402
+ break unless index
403
+
404
+ parser.skip_graph_header
405
+
406
+ if index != graph_index
407
+ parser.skip_graph
408
+ next
409
+ end
410
+
411
+ graph = parser.read_graph
412
+ description = Seafoam::Graal::GraphDescription.new
413
+
414
+ graph.nodes.each_value do |node|
415
+ node_class = node.props.dig(:node_class, :node_class)
416
+ case node_class
417
+ when 'org.graalvm.compiler.nodes.IfNode'
418
+ description.branches = true
419
+ when 'org.graalvm.compiler.nodes.LoopBeginNode'
420
+ description.loops = true
421
+ when 'org.graalvm.compiler.nodes.InvokeNode', 'org.graalvm.compiler.nodes.InvokeWithExceptionNode'
422
+ description.calls = true
423
+ end
424
+ end
425
+
426
+ description.deopts = graph.nodes[0].outputs.map(&:to)
427
+ .all? { |t| t.props.dig(:node_class, :node_class) == 'org.graalvm.compiler.nodes.DeoptimizeNode' }
428
+
429
+ formatter = formatter_module::DescribeFormatter.new(graph, description)
430
+ @out.puts formatter.format
431
+
432
+ break
391
433
  end
392
434
  end
393
435
 
@@ -397,8 +439,10 @@ module Seafoam
397
439
  raise ArgumentError, 'render needs at least a graph' unless graph_index
398
440
  raise ArgumentError, 'render only works with a graph' unless rest == [nil, nil]
399
441
 
400
- annotator_options = {
442
+ pass_options = {
443
+ simplify_truffle_args: true,
401
444
  hide_frame_state: true,
445
+ hide_pi: true,
402
446
  hide_floating: false,
403
447
  reduce_edges: true
404
448
  }
@@ -406,6 +450,7 @@ module Seafoam
406
450
  args = args.dup
407
451
  out_file = nil
408
452
  explicit_out_file = false
453
+ draw_blocks = false
409
454
  until args.empty?
410
455
  arg = args.shift
411
456
  case arg
@@ -418,12 +463,18 @@ module Seafoam
418
463
  raise ArgumentError, 'no list for --spotlight' unless spotlight_arg
419
464
 
420
465
  spotlight_nodes = spotlight_arg.split(',').map { |n| Integer(n) }
466
+ when '--full-truffle-args'
467
+ pass_options[:simplify_truffle_args] = false
421
468
  when '--show-frame-state'
422
- annotator_options[:hide_frame_state] = false
469
+ pass_options[:hide_frame_state] = false
470
+ when '--show-pi'
471
+ pass_options[:hide_pi] = false
423
472
  when '--hide-floating'
424
- annotator_options[:hide_floating] = true
473
+ pass_options[:hide_floating] = true
425
474
  when '--no-reduce-edges'
426
- annotator_options[:reduce_edges] = false
475
+ pass_options[:reduce_edges] = false
476
+ when '--draw-blocks'
477
+ draw_blocks = true
427
478
  when '--option'
428
479
  key = args.shift
429
480
  raise ArgumentError, 'no key for --option' unless key
@@ -432,7 +483,7 @@ module Seafoam
432
483
  raise ArgumentError, "no value for --option #{key}" unless out_file
433
484
 
434
485
  value = { 'true' => true, 'false' => 'false' }.fetch(key, value)
435
- annotator_options[key.to_sym] = value
486
+ pass_options[key.to_sym] = value
436
487
  else
437
488
  raise ArgumentError, "unexpected option #{arg}"
438
489
  end
@@ -455,7 +506,7 @@ module Seafoam
455
506
  with_graph(file, graph_index) do |parser|
456
507
  parser.skip_graph_header
457
508
  graph = parser.read_graph
458
- Annotators.apply graph, annotator_options
509
+ Passes.apply graph, pass_options
459
510
  if spotlight_nodes
460
511
  spotlight = Spotlight.new(graph)
461
512
  spotlight_nodes.each do |node_id|
@@ -469,14 +520,14 @@ module Seafoam
469
520
  if out_format == :dot
470
521
  File.open(out_file, 'w') do |stream|
471
522
  writer = GraphvizWriter.new(stream)
472
- writer.write_graph graph
523
+ writer.write_graph graph, false, draw_blocks
473
524
  end
474
525
  else
475
526
  begin
476
527
  IO.popen(['dot', "-T#{out_format}", '-o', out_file], 'w') do |stream|
477
528
  writer = GraphvizWriter.new(stream)
478
529
  hidpi = out_format == :png
479
- writer.write_graph graph, hidpi
530
+ writer.write_graph graph, hidpi, draw_blocks
480
531
  end
481
532
  rescue Errno::ENOENT
482
533
  raise 'Could not run Graphviz - is it installed?'
@@ -565,6 +616,32 @@ module Seafoam
565
616
  raise ArgumentError, 'graph not found' unless graph_found
566
617
  end
567
618
 
619
+ # Prints help.
620
+ def help(*_args)
621
+ @out.puts 'seafoam file.bgv info'
622
+ @out.puts ' file.bgv list'
623
+ @out.puts ' file.bgv[:graph][:node[-edge]] search term...'
624
+ @out.puts ' file.bgv[:graph][:node[-edge]] edges'
625
+ @out.puts ' file.bgv[:graph][:node[-edge]] props'
626
+ @out.puts ' file.bgv:graph:node source'
627
+ @out.puts ' file.bgv:graph describe'
628
+ @out.puts ' file.bgv:graph render'
629
+ @out.puts ' --spotlight n,n,n...'
630
+ @out.puts ' --out graph.pdf'
631
+ @out.puts ' graph.svg'
632
+ @out.puts ' graph.png'
633
+ @out.puts ' graph.dot'
634
+ @out.puts ' --full-truffle-args'
635
+ @out.puts ' --show-frame-state'
636
+ @out.puts ' --show-pi'
637
+ @out.puts ' --hide-floating'
638
+ @out.puts ' --no-reduce-edges'
639
+ @out.puts ' --draw-blocks'
640
+ @out.puts ' --option key value'
641
+ @out.puts ' --help'
642
+ @out.puts ' --version'
643
+ end
644
+
568
645
  # Prints the version.
569
646
  def version(*args)
570
647
  raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
@@ -598,10 +675,15 @@ module Seafoam
598
675
 
599
676
  # Open a file for the user if possible.
600
677
  def autoopen(file)
601
- if RUBY_PLATFORM.include?('darwin') && @out.tty?
678
+ return unless @out.tty?
679
+
680
+ case RUBY_PLATFORM
681
+ when /darwin/
602
682
  system 'open', file
603
- # Don't worry if it fails.
683
+ when /linux/
684
+ system 'xdg-open', file
604
685
  end
686
+ # Don't worry if it fails.
605
687
  end
606
688
  end
607
689
  end
@@ -0,0 +1,88 @@
1
+ module Seafoam
2
+ module Formatters
3
+ module Base
4
+ # Formats the output of the `describe` command.
5
+ class DescribeFormatter
6
+ attr_reader :graph, :description
7
+
8
+ def initialize(graph, description)
9
+ @graph = graph
10
+ @description = description
11
+ end
12
+ end
13
+
14
+ # Formats the output of the `edges` command.
15
+ class EdgesFormatter
16
+ EdgesEntry = Struct.new(:edges) do
17
+ def render(formatter)
18
+ formatter.render_edges_entry(edges)
19
+ end
20
+ end
21
+
22
+ NodeEntry = Struct.new(:node) do
23
+ def render(formatter)
24
+ formatter.render_node_entry(node)
25
+ end
26
+ end
27
+
28
+ SummaryEntry = Struct.new(:node_count, :edge_count) do
29
+ def render(formatter)
30
+ formatter.render_summary_entry(node_count, edge_count)
31
+ end
32
+ end
33
+
34
+ attr_reader :entry
35
+
36
+ def initialize(entry)
37
+ @entry = entry
38
+ end
39
+
40
+ def format
41
+ entry.render(self)
42
+ end
43
+
44
+ def render_edges_entry(edges)
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def render_node_entry(node)
49
+ raise NotImplementedError
50
+ end
51
+
52
+ def render_summary_entry(node_count, edge_count)
53
+ raise NotImplementedError
54
+ end
55
+ end
56
+
57
+ # Formats the output of the `info` command.
58
+ class InfoFormatter
59
+ attr_reader :major_version, :minor_version
60
+
61
+ def initialize(major_version, minor_version)
62
+ @major_version = major_version
63
+ @minor_version = minor_version
64
+ end
65
+ end
66
+
67
+ # Formats the output of the `list` command.
68
+ class ListFormatter
69
+ Entry = Struct.new(:file, :graph_name_components, :index)
70
+
71
+ attr_reader :entries
72
+
73
+ def initialize(entries)
74
+ @entries = entries
75
+ end
76
+ end
77
+
78
+ # Formats the output of the `source` command.
79
+ class SourceFormatter
80
+ attr_reader :source_position
81
+
82
+ def initialize(source_position)
83
+ @source_position = source_position
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,8 @@
1
+ module Seafoam
2
+ # Formatters are the mechanism by which `seafoam` command output is presented to the user.
3
+ module Formatters
4
+ autoload :Base, 'seafoam/formatters/base'
5
+ autoload :Json, 'seafoam/formatters/json'
6
+ autoload :Text, 'seafoam/formatters/text'
7
+ end
8
+ end
@@ -0,0 +1,82 @@
1
+ require 'json'
2
+
3
+ module Seafoam
4
+ module Formatters
5
+ module Json
6
+ # A JSON-based formatter for the `describe` command.
7
+ class DescribeFormatter < Seafoam::Formatters::Base::DescribeFormatter
8
+ def format
9
+ ret = Seafoam::Graal::GraphDescription::ATTRIBUTES.map { |attr| [attr, description.send(attr)] }.to_h
10
+ ret[:node_count] = graph.nodes.size
11
+
12
+ ret.to_json
13
+ end
14
+ end
15
+
16
+ # A JSON-based formatter for the `edges` command.
17
+ class EdgesFormatter < Seafoam::Formatters::Base::EdgesFormatter
18
+ def render_edges_entry(edges)
19
+ edges.map { |edge| build_edge(edge) }.to_json
20
+ end
21
+
22
+ def render_node_entry(node)
23
+ {
24
+ input: node.inputs.map { |input| build_edge(input) },
25
+ output: node.outputs.map { |output| build_edge(output) }
26
+ }.to_json
27
+ end
28
+
29
+ def render_summary_entry(node_count, edge_count)
30
+ { node_count: node_count, edge_count: edge_count }.to_json
31
+ end
32
+
33
+ def build_node(node)
34
+ { id: node.id.to_s, label: node.props[:label] }
35
+ end
36
+
37
+ def build_edge(edge)
38
+ { from: build_node(edge.from), to: build_node(edge.to), label: edge.props[:label] }
39
+ end
40
+ end
41
+
42
+ # A JSON-based formatter for the `info` command.
43
+ class InfoFormatter < Seafoam::Formatters::Base::InfoFormatter
44
+ def format
45
+ {
46
+ major_version: major_version,
47
+ minor_version: minor_version
48
+ }.to_json
49
+ end
50
+ end
51
+
52
+ # A JSON-based formatter for the `list` command.
53
+ class ListFormatter < Seafoam::Formatters::Base::ListFormatter
54
+ def format
55
+ entries.map do |entry|
56
+ {
57
+ graph_index: entry.index,
58
+ graph_file: entry.file,
59
+ graph_name_components: entry.graph_name_components
60
+ }
61
+ end.to_json
62
+ end
63
+ end
64
+
65
+ # A JSON-based formatter for the `source` command.
66
+ class SourceFormatter < Seafoam::Formatters::Base::SourceFormatter
67
+ def format
68
+ Seafoam::Graal::Source.walk(source_position, &method(:render_method)).to_json
69
+ end
70
+
71
+ def render_method(method)
72
+ declaring_class = method[:declaring_class]
73
+ name = method[:method_name]
74
+ {
75
+ class: declaring_class,
76
+ method: name
77
+ }
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end