///|
fn ir_escape(value : String) -> String {
  let builder = StringBuilder()
  for character in value {
    match character {
      '"' => builder.write_string("\\\"")
      '\\' => builder.write_string("\\\\")
      '\n' => builder.write_string("\\n")
      '\r' => builder.write_string("\\r")
      '\t' => builder.write_string("\\t")
      _ => builder.write_char(character)
    }
  }
  builder.to_string()
}

///|
/// Emit a deterministic, dependency-free JSON representation of the semantic index.
pub fn SemanticIndex::to_ir_json(self : SemanticIndex) -> String {
  let builder = StringBuilder()
  builder.write_string("{\"modules\":[")
  for index, mod in self.modules {
    if index > 0 {
      builder.write_char(',')
    }
    builder.write_string("{\"name\":\"")
    builder.write_string(ir_escape(mod.name))
    builder.write_string("\",\"ports\":[")
    for port_index, port in mod.ports {
      if port_index > 0 {
        builder.write_char(',')
      }
      builder.write_string("{\"name\":\"")
      builder.write_string(ir_escape(port.name))
      builder.write_string("\",\"direction\":\"")
      builder.write_string(
        match port.direction {
          @syntax.Input => "input"
          @syntax.Output => "output"
          @syntax.Inout => "inout"
          @syntax.Unspecified => "unspecified"
        },
      )
      builder.write_string("\"}")
    }
    builder.write_string("],\"signals\":[")
    for signal_index, signal in mod.signals {
      if signal_index > 0 {
        builder.write_char(',')
      }
      builder.write_string("{\"name\":\"")
      builder.write_string(ir_escape(signal.name))
      builder.write_string("\",\"width\":")
      match signal.width {
        Known(width) => builder.write_object(width)
        Unknown => builder.write_string("null")
      }
      builder.write_string(",\"drivers\":")
      builder.write_object(signal.drivers.length())
      builder.write_char('}')
    }
    builder.write_string("],\"instances\":[")
    for instance_index, instance in mod.instances {
      if instance_index > 0 {
        builder.write_char(',')
      }
      builder.write_string("{\"module\":\"")
      builder.write_string(ir_escape(instance.module_name))
      builder.write_string("\",\"name\":\"")
      builder.write_string(ir_escape(instance.instance_name))
      builder.write_string("\"}")
    }
    builder.write_string("]}")
  }
  builder.write_string("],\"edges\":[")
  for index, edge in self.graph.edges() {
    if index > 0 {
      builder.write_char(',')
    }
    let (parent, child) = edge
    builder.write_string("[\"")
    builder.write_string(ir_escape(parent))
    builder.write_string("\",\"")
    builder.write_string(ir_escape(child))
    builder.write_string("\"]")
  }
  builder.write_string("]}\n")
  builder.to_string()
}