ast_to_json.rb 1.71 KB
#!/usr/bin/env ruby

# Takes an clang-generated AST in stdin
# Outputs a D3-compliant JSON file

require 'json'

class CodeStruct
  attr_accessor :name, :starts_at, :size

  def initialize(parent, name, starts_at)
    @parent = parent
    @name = name
    @starts_at = starts_at
    @size = 0
    @children = []
    @parent.add_child(self) unless @parent.nil?
  end

  def ends_at(offset)
    @size = offset-@starts_at
  end

  def add_child(s)
    @children.push(s)
  end

  def as_d3_dict
    res = {name: name}
    if @children.empty?
      res[:value] = size
    else
      res[:children] = @children.map{|c| c.as_d3_dict}
    end
    res
  end

  def log(space = 0)
    puts (" "*space) + "#{name} (SIZE=#{size})"
    @children.each do |child|
      child.log(space+2)
    end
  end
end

def handleNewLine(line, index, stack)
  # Example : "    213736 |     class Timer (primary base)"
  offset, depth, name = line.match(/\s*([0-9]+)\s\|(\s+)(.*)/).captures
  offset = offset.to_i
  depth = (depth.size-1)/2

  parent = stack[depth-1]
  stack[depth..-1].each do |finished|
    finished.ends_at(offset)
  end
  stack = stack[0...depth]
  struct = CodeStruct.new(parent, name, offset)
  stack.push(struct)

  return stack
end

class_name = ARGV[0]

stack = []
ignore_line = true
STDIN.each_line.with_index do |l,i|
  if l == "         0 | class #{class_name}\n"
    ignore_line = false
  end
  if l.start_with?("           | ") && ignore_line == false
    ignore_line = true
    end_offset = l.match(/sizeof=([0-9]+)/).captures.first
    end_offset = end_offset.to_i
    stack[-1].ends_at(end_offset)
    stack[0].ends_at(end_offset)
  end
  unless ignore_line
    stack = handleNewLine(l, i, stack)
  end
end

puts JSON.generate(stack[0].as_d3_dict)