class SyntaxTree::YARV::SeaOfNodes

flow.
to the graph. The edges of the graph represent either control flow or data
instruction in the instruction sequence or a synthesized node that we add
allows us to have the vertices of the graph represent either an
represent both control and data flow in the same graph. The way we use it
A sea of nodes is an intermediate representation used by a compiler to

def self.compile(dfg)

def self.compile(dfg)
  Compiler.new(dfg).compile
end

def initialize(dfg, nodes, local_graphs)

def initialize(dfg, nodes, local_graphs)
  @dfg = dfg
  @nodes = nodes
  @local_graphs = local_graphs
end

def to_mermaid

def to_mermaid
  Mermaid.flowchart do |flowchart|
    nodes.each do |node|
      flowchart.node("node_#{node.id}", node.label, shape: :rounded)
    end
    nodes.each do |producer|
      producer.outputs.each do |consumer_edge|
        label =
          if !consumer_edge.label
            # No label.
          elsif consumer_edge.to.is_a?(PhiNode)
            # Edges into phi nodes are labelled by the offset of the
            # instruction going into the merge.
            "%04d" % consumer_edge.label
          else
            consumer_edge.label.to_s
          end
        flowchart.link(
          flowchart.fetch("node_#{producer.id}"),
          flowchart.fetch("node_#{consumer_edge.to.id}"),
          label,
          type: consumer_edge.type == :info ? :dotted : :directed,
          color: { data: :green, control: :red }[consumer_edge.type]
        )
      end
    end
  end
end

def verify

def verify
  # Verify edge labels.
  nodes.each do |node|
    # Not talking about phi nodes right now.
    next if node.is_a?(PhiNode)
    if node.is_a?(InsnNode) && node.insn.branch_targets.any? &&
         !node.insn.is_a?(Leave)
      # A branching node must have at least one branch edge and
      # potentially a fallthrough edge coming out.
      labels = node.outputs.map(&:label).sort
      raise if labels[0] != :branch0
      raise if labels[1] != :fallthrough && labels.size > 2
    else
      labels = node.inputs.filter { |e| e.type == :data }.map(&:label)
      next if labels.empty?
      # No nil labels
      raise if labels.any?(&:nil?)
      # Labels should start at zero.
      raise unless labels.min.zero?
      # Labels should be contiguous.
      raise unless labels.sort == (labels.min..labels.max).to_a
    end
  end
end