seafoam 0.8 → 0.12

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bgv2isabelle +1 -5
  3. data/bin/bgv2json +1 -5
  4. data/bin/seafoam +1 -5
  5. data/lib/seafoam/bgv/bgv_parser.rb +1 -2
  6. data/lib/seafoam/commands.rb +135 -101
  7. data/lib/seafoam/formatters/base.rb +88 -0
  8. data/lib/seafoam/formatters/formatters.rb +8 -0
  9. data/lib/seafoam/formatters/json.rb +82 -0
  10. data/lib/seafoam/formatters/text.rb +70 -0
  11. data/lib/seafoam/graal/graph_description.rb +22 -0
  12. data/lib/seafoam/graal/pi.rb +18 -0
  13. data/lib/seafoam/graal/source.rb +5 -9
  14. data/lib/seafoam/graph.rb +9 -1
  15. data/lib/seafoam/graphviz_writer.rb +3 -0
  16. data/lib/seafoam/json_writer.rb +1 -3
  17. data/lib/seafoam/{annotators → passes}/fallback.rb +4 -4
  18. data/lib/seafoam/{annotators → passes}/graal.rb +43 -15
  19. data/lib/seafoam/passes/truffle.rb +58 -0
  20. data/lib/seafoam/passes.rb +61 -0
  21. data/lib/seafoam/version.rb +1 -1
  22. data/lib/seafoam.rb +7 -6
  23. metadata +17 -81
  24. data/.github/probots.yml +0 -2
  25. data/.github/workflows/workflows.yml +0 -40
  26. data/.gitignore +0 -6
  27. data/.rubocop.yml +0 -37
  28. data/.ruby-version +0 -1
  29. data/.seafoam/config +0 -1
  30. data/CODE_OF_CONDUCT.md +0 -128
  31. data/CONTRIBUTING.md +0 -5
  32. data/Gemfile +0 -2
  33. data/Gemfile.lock +0 -59
  34. data/LICENSE.md +0 -7
  35. data/README.md +0 -378
  36. data/bin/cfg2asm +0 -24
  37. data/demos/box-unbox-stats +0 -65
  38. data/docs/annotators.md +0 -43
  39. data/docs/bgv.md +0 -294
  40. data/docs/getting-graphs.md +0 -63
  41. data/docs/images/igv.png +0 -0
  42. data/docs/images/seafoam.png +0 -0
  43. data/docs/images/spotlight-igv.png +0 -0
  44. data/docs/images/spotlight-seafoam.png +0 -0
  45. data/docs/json.md +0 -35
  46. data/examples/Fib.java +0 -24
  47. data/examples/MatMult.java +0 -39
  48. data/examples/fib-java.bgv.gz +0 -0
  49. data/examples/fib.js +0 -15
  50. data/examples/fib.rb +0 -15
  51. data/examples/identity.rb +0 -13
  52. data/examples/java/Irreducible.class +0 -0
  53. data/examples/java/Irreducible.j +0 -35
  54. data/examples/java/IrreducibleDecompiled.java +0 -21
  55. data/examples/java/JavaExamples.java +0 -418
  56. data/examples/matmult.rb +0 -29
  57. data/examples/overflow.rb +0 -13
  58. data/examples/ruby/clamps.rb +0 -20
  59. data/examples/ruby/graal.patch +0 -15
  60. data/examples/ruby/ruby_examples.rb +0 -278
  61. data/lib/seafoam/annotators.rb +0 -54
  62. data/lib/seafoam/cfg/cfg_parser.rb +0 -93
  63. data/lib/seafoam/cfg/disassembler.rb +0 -70
  64. data/lib/seafoam/config.rb +0 -34
  65. data/seafoam.gemspec +0 -22
  66. data/spec/seafoam/annotators/fallback_spec.rb +0 -69
  67. data/spec/seafoam/annotators/graal_spec.rb +0 -96
  68. data/spec/seafoam/annotators_spec.rb +0 -61
  69. data/spec/seafoam/bgv/bgv_parser_spec.rb +0 -167
  70. data/spec/seafoam/binary/io_binary_reader_spec.rb +0 -176
  71. data/spec/seafoam/cfg/cfg_parser_spec.rb +0 -21
  72. data/spec/seafoam/cfg/disassembler_spec.rb +0 -32
  73. data/spec/seafoam/command_spec.rb +0 -314
  74. data/spec/seafoam/graph_spec.rb +0 -172
  75. data/spec/seafoam/graphviz_writer_spec.rb +0 -63
  76. data/spec/seafoam/json_writer_spec.rb +0 -14
  77. data/spec/seafoam/spec_helpers.rb +0 -34
  78. data/spec/seafoam/spotlight_spec.rb +0 -38
  79. data/tools/render-all +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f5cdd6b29d9c25bc7db525b2f49d6937fb996bc108a7bf18ddf0364575e6999
4
- data.tar.gz: 510608b5d784ef4c58768aafb42372bdd33aad226a6123e9ad72ea77845a8050
3
+ metadata.gz: 5365ea61783b2be678609c214262d4df073fa4e66df60e9370b6fd33dd73825a
4
+ data.tar.gz: 322fe5170082c57d51120c3fa43910ca2e79a7839737167955ad1f330084af83
5
5
  SHA512:
6
- metadata.gz: 125d9c14699c1a9a72bc1311b2673cf24d725af3c4da38eac1c2c89ebce4d6157c3e08ad7cc7b8769f8c755227cd04da4b9a6f53ae30f0f3ce38e7a1cf12d4b2
7
- data.tar.gz: 2430c0b5f04378767c5bbbb51274900deae22291fd905831f4a5d5847edd5e7497e3df94a434ab803689ee4f9b3f6cd5d6740bdc28dbf96d7da329c8610ac6e5
6
+ metadata.gz: 33cdf9e0f73e9a41d11ee2c19f60b015f8fe5168ddd92eaee1a254c13c6296e8696514858e0fc0c77c583657c3d01b2292919077f9131768198801bd9610dbc9
7
+ data.tar.gz: 2354dd4f1c5d87c83d265577a0e64b5130cd9dc7de46e6222629b985bdac038b6977f3fe10ffc2624e45b2cc56dbd67ea243734a528a17eab780a80c00996621
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/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
@@ -162,8 +162,7 @@ module Seafoam
162
162
  count += 1
163
163
  arg
164
164
  end
165
- components = groups_names + [name]
166
- components.join('/')
165
+ groups_names + [name]
167
166
  end
168
167
 
169
168
  private
@@ -1,39 +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 ' --draw-blocks'
34
- @out.puts ' --option key value'
35
- @out.puts ' --help'
36
- @out.puts ' --version'
26
+ help(*args)
37
27
  when 'version', '-v', '-version', '--version'
38
28
  version(*args)
39
29
  else
@@ -43,21 +33,23 @@ module Seafoam
43
33
  when nil
44
34
  help(*args)
45
35
  when 'info'
46
- info name, *args
36
+ info name, formatter_module, *args
47
37
  when 'list'
48
- list name, *args
38
+ list name, formatter_module, *args
49
39
  when 'search'
50
40
  search name, *args
51
41
  when 'edges'
52
- edges name, *args
42
+ edges name, formatter_module, *args
53
43
  when 'props'
54
44
  props name, *args
55
45
  when 'source'
56
- source name, *args
46
+ source name, formatter_module, *args
57
47
  when 'render'
58
48
  render name, *args
59
49
  when 'debug'
60
50
  debug name, *args
51
+ when 'describe'
52
+ describe name, formatter_module, *args
61
53
  else
62
54
  raise ArgumentError, "unknown command #{command}"
63
55
  end
@@ -155,82 +147,42 @@ module Seafoam
155
147
  end
156
148
  end
157
149
 
158
- def cfg2asm(*args)
159
- case args.first
160
- when nil, 'help', '-h', '--help', '-help'
161
- args = args.drop(1)
162
- raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
163
-
164
- @out.puts 'cfg2asm file.bgv...'
165
- @out.puts ' --no-comments'
166
- @out.puts ' --help'
167
- @out.puts ' --version'
168
- when 'version', '-v', '-version', '--version'
169
- args = args.drop(1)
170
- version(*args)
171
- else
172
- comments = true
173
- files = []
174
-
175
- until args.empty?
176
- arg = args.shift
177
- if arg.start_with?('-')
178
- case arg
179
- when '--no-comments'
180
- comments = false
181
- else
182
- raise ArgumentError, "unknown option #{arg}"
183
- end
184
- else
185
- files.push arg
186
- end
187
- end
188
-
189
- files.each_with_index do |file, n|
190
- parser = Seafoam::CFG::CFGParser.new(@out, file)
191
- parser.skip_over_cfg 'After code installation'
192
- nmethod = parser.read_nmethod
193
-
194
- disassembler = Seafoam::CFG::Disassembler.new(@out)
195
- @out.puts if n.positive?
196
- @out.puts "[#{file}]"
197
- disassembler.disassemble(nmethod, comments)
198
- end
199
- end
200
- end
201
-
202
150
  private
203
151
 
204
152
  # seafoam file.bgv info
205
- def info(name, *args)
153
+ def info(name, formatter_module, *args)
206
154
  file, *rest = parse_name(name)
207
155
  raise ArgumentError, 'info only works with a file' unless rest == [nil, nil, nil]
208
-
209
156
  raise ArgumentError, 'info does not take arguments' unless args.empty?
210
157
 
211
158
  parser = BGV::BGVParser.new(file)
212
159
  major, minor = parser.read_file_header(version_check: false)
213
- @out.puts "BGV #{major}.#{minor}"
160
+ formatter = formatter_module::InfoFormatter.new(major, minor)
161
+
162
+ @out.puts formatter.format
214
163
  end
215
164
 
216
165
  # seafoam file.bgv list
217
- def list(name, *args)
166
+ def list(name, formatter_module, *args)
218
167
  file, *rest = parse_name(name)
219
168
  raise ArgumentError, 'list only works with a file' unless rest == [nil, nil, nil]
220
-
221
169
  raise ArgumentError, 'list does not take arguments' unless args.empty?
222
170
 
223
171
  parser = BGV::BGVParser.new(file)
224
172
  parser.read_file_header
225
173
  parser.skip_document_props
174
+ entries = []
226
175
  loop do
227
176
  index, = parser.read_graph_preheader
228
177
  break unless index
229
178
 
230
179
  graph_header = parser.read_graph_header
231
- @out.puts "#{file}:#{index} #{parser.graph_name(graph_header)}"
180
+ entries << formatter_module::ListFormatter::Entry.new(file, parser.graph_name(graph_header), index)
232
181
  parser.skip_graph
233
182
  end
183
+
184
+ formatter = formatter_module::ListFormatter.new(entries)
185
+ @out.puts formatter.format
234
186
  end
235
187
 
236
188
  # seafoam file.bgv:n... search term...
@@ -263,7 +215,7 @@ module Seafoam
263
215
  end
264
216
 
265
217
  def search_object(tag, object, terms)
266
- full_text = JSON.generate(object)
218
+ full_text = JSON.generate(JSONWriter.prepare_json(object))
267
219
  full_text_down = full_text.downcase
268
220
  start = 0
269
221
  terms.each do |t|
@@ -289,17 +241,18 @@ module Seafoam
289
241
  end
290
242
 
291
243
  # seafoam file.bgv:n... edges
292
- def edges(name, *args)
244
+ def edges(name, formatter_module, *args)
293
245
  file, graph_index, node_id, edge_id = parse_name(name)
294
246
  raise ArgumentError, 'edges needs at least a graph' unless graph_index
295
-
296
247
  raise ArgumentError, 'edges does not take arguments' unless args.empty?
297
248
 
249
+ entry = nil
250
+
298
251
  with_graph(file, graph_index) do |parser|
299
252
  parser.read_graph_header
300
253
  graph = parser.read_graph
301
254
  if node_id
302
- Annotators.apply graph
255
+ Passes.apply graph
303
256
  node = graph.nodes[node_id]
304
257
  raise ArgumentError, 'node not found' unless node
305
258
 
@@ -310,24 +263,18 @@ module Seafoam
310
263
  edges = node.outputs.select { |edge| edge.to == to }
311
264
  raise ArgumentError, 'edge not found' if edges.empty?
312
265
 
313
- edges.each do |edge|
314
- @out.puts "#{edge.from.id_and_label} ->(#{edge.props[:label]}) #{edge.to.id_and_label}"
315
- end
266
+ entry = formatter_module::EdgesFormatter::EdgesEntry.new(edges)
316
267
  else
317
- @out.puts 'Input:'
318
- node.inputs.each do |input|
319
- @out.puts " #{node.id_and_label} <-(#{input.props[:label]}) #{input.from.id_and_label}"
320
- end
321
- @out.puts 'Output:'
322
- node.outputs.each do |output|
323
- @out.puts " #{node.id_and_label} ->(#{output.props[:label]}) #{output.to.id_and_label}"
324
- end
268
+ entry = formatter_module::EdgesFormatter::NodeEntry.new(node)
325
269
  end
326
270
  break
327
271
  else
328
- @out.puts "#{graph.nodes.count} nodes, #{graph.edges.count} edges"
272
+ entry = formatter_module::EdgesFormatter::SummaryEntry.new(graph.nodes.count, graph.edges.count)
329
273
  end
330
274
  end
275
+
276
+ formatter = formatter_module::EdgesFormatter.new(entry)
277
+ @out.puts formatter.format
331
278
  end
332
279
 
333
280
  # seafoam file.bgv... props
@@ -376,7 +323,7 @@ module Seafoam
376
323
  end
377
324
 
378
325
  # seafoam file.bgv:n:n source
379
- def source(name, *args)
326
+ def source(name, formatter_module, *args)
380
327
  file, graph_index, node_id, edge_id = parse_name(name)
381
328
  raise ArgumentError, 'source needs a node' unless node_id
382
329
  raise ArgumentError, 'source only works with a node' if edge_id
@@ -388,7 +335,57 @@ module Seafoam
388
335
  node = graph.nodes[node_id]
389
336
  raise ArgumentError, 'node not found' unless node
390
337
 
391
- @out.puts Graal::Source.render(node.props['nodeSourcePosition'])
338
+ formatter = formatter_module::SourceFormatter.new(node.props['nodeSourcePosition'])
339
+ @out.puts formatter.format
340
+ end
341
+ end
342
+
343
+ # seafoam file.bgv:n describe
344
+ def describe(name, formatter_module, *args)
345
+ file, graph_index, *rest = parse_name(name)
346
+
347
+ if graph_index.nil? || !rest.all?(&:nil?)
348
+ raise ArgumentError, 'describe only works with a graph'
349
+ end
350
+ raise ArgumentError, 'describe does not take arguments' unless args.empty?
351
+
352
+ parser = BGV::BGVParser.new(file)
353
+ parser.read_file_header
354
+ parser.skip_document_props
355
+
356
+ loop do
357
+ index, = parser.read_graph_preheader
358
+ break unless index
359
+
360
+ parser.skip_graph_header
361
+
362
+ if index != graph_index
363
+ parser.skip_graph
364
+ next
365
+ end
366
+
367
+ graph = parser.read_graph
368
+ description = Seafoam::Graal::GraphDescription.new
369
+
370
+ graph.nodes.each_value do |node|
371
+ node_class = node.props.dig(:node_class, :node_class)
372
+ case node_class
373
+ when 'org.graalvm.compiler.nodes.IfNode'
374
+ description.branches = true
375
+ when 'org.graalvm.compiler.nodes.LoopBeginNode'
376
+ description.loops = true
377
+ when 'org.graalvm.compiler.nodes.InvokeNode', 'org.graalvm.compiler.nodes.InvokeWithExceptionNode'
378
+ description.calls = true
379
+ end
380
+ end
381
+
382
+ description.deopts = graph.nodes[0].outputs.map(&:to)
383
+ .all? { |t| t.props.dig(:node_class, :node_class) == 'org.graalvm.compiler.nodes.DeoptimizeNode' }
384
+
385
+ formatter = formatter_module::DescribeFormatter.new(graph, description)
386
+ @out.puts formatter.format
387
+
388
+ break
392
389
  end
393
390
  end
394
391
 
@@ -398,8 +395,10 @@ module Seafoam
398
395
  raise ArgumentError, 'render needs at least a graph' unless graph_index
399
396
  raise ArgumentError, 'render only works with a graph' unless rest == [nil, nil]
400
397
 
401
- annotator_options = {
398
+ pass_options = {
399
+ simplify_truffle_args: true,
402
400
  hide_frame_state: true,
401
+ hide_pi: true,
403
402
  hide_floating: false,
404
403
  reduce_edges: true
405
404
  }
@@ -420,12 +419,16 @@ module Seafoam
420
419
  raise ArgumentError, 'no list for --spotlight' unless spotlight_arg
421
420
 
422
421
  spotlight_nodes = spotlight_arg.split(',').map { |n| Integer(n) }
422
+ when '--full-truffle-args'
423
+ pass_options[:simplify_truffle_args] = false
423
424
  when '--show-frame-state'
424
- annotator_options[:hide_frame_state] = false
425
+ pass_options[:hide_frame_state] = false
426
+ when '--show-pi'
427
+ pass_options[:hide_pi] = false
425
428
  when '--hide-floating'
426
- annotator_options[:hide_floating] = true
429
+ pass_options[:hide_floating] = true
427
430
  when '--no-reduce-edges'
428
- annotator_options[:reduce_edges] = false
431
+ pass_options[:reduce_edges] = false
429
432
  when '--draw-blocks'
430
433
  draw_blocks = true
431
434
  when '--option'
@@ -436,7 +439,7 @@ module Seafoam
436
439
  raise ArgumentError, "no value for --option #{key}" unless out_file
437
440
 
438
441
  value = { 'true' => true, 'false' => 'false' }.fetch(key, value)
439
- annotator_options[key.to_sym] = value
442
+ pass_options[key.to_sym] = value
440
443
  else
441
444
  raise ArgumentError, "unexpected option #{arg}"
442
445
  end
@@ -459,7 +462,7 @@ module Seafoam
459
462
  with_graph(file, graph_index) do |parser|
460
463
  parser.skip_graph_header
461
464
  graph = parser.read_graph
462
- Annotators.apply graph, annotator_options
465
+ Passes.apply graph, pass_options
463
466
  if spotlight_nodes
464
467
  spotlight = Spotlight.new(graph)
465
468
  spotlight_nodes.each do |node_id|
@@ -569,6 +572,32 @@ module Seafoam
569
572
  raise ArgumentError, 'graph not found' unless graph_found
570
573
  end
571
574
 
575
+ # Prints help.
576
+ def help(*_args)
577
+ @out.puts 'seafoam file.bgv info'
578
+ @out.puts ' file.bgv list'
579
+ @out.puts ' file.bgv[:graph][:node[-edge]] search term...'
580
+ @out.puts ' file.bgv[:graph][:node[-edge]] edges'
581
+ @out.puts ' file.bgv[:graph][:node[-edge]] props'
582
+ @out.puts ' file.bgv:graph:node source'
583
+ @out.puts ' file.bgv:graph describe'
584
+ @out.puts ' file.bgv:graph render'
585
+ @out.puts ' --spotlight n,n,n...'
586
+ @out.puts ' --out graph.pdf'
587
+ @out.puts ' graph.svg'
588
+ @out.puts ' graph.png'
589
+ @out.puts ' graph.dot'
590
+ @out.puts ' --full-truffle-args'
591
+ @out.puts ' --show-frame-state'
592
+ @out.puts ' --show-pi'
593
+ @out.puts ' --hide-floating'
594
+ @out.puts ' --no-reduce-edges'
595
+ @out.puts ' --draw-blocks'
596
+ @out.puts ' --option key value'
597
+ @out.puts ' --help'
598
+ @out.puts ' --version'
599
+ end
600
+
572
601
  # Prints the version.
573
602
  def version(*args)
574
603
  raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
@@ -602,10 +631,15 @@ module Seafoam
602
631
 
603
632
  # Open a file for the user if possible.
604
633
  def autoopen(file)
605
- if RUBY_PLATFORM.include?('darwin') && @out.tty?
634
+ return unless @out.tty?
635
+
636
+ case RUBY_PLATFORM
637
+ when /darwin/
606
638
  system 'open', file
607
- # Don't worry if it fails.
639
+ when /linux/
640
+ system 'xdg-open', file
608
641
  end
642
+ # Don't worry if it fails.
609
643
  end
610
644
  end
611
645
  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