seafoam 0.8 → 0.12

Sign up to get free protection for your applications and to get access to all the features.
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