// dump_mcode.cm — pretty-print mcode IR (before streamlining)
//
// Usage: ./cell --core . dump_mcode.cm <file.ce|file.cm>

var fd = use("fd")
var json = use("json")
var tokenize = use("tokenize")
var parse = use("parse")
var fold = use("fold")
var mcode = use("mcode")

if (length(args) < 1) {
  print("usage: cell --core . dump_mcode.cm <file>")
  return
}

var filename = args[0]
var src = text(fd.slurp(filename))
var tok = tokenize(src, filename)
var ast = parse(tok.tokens, src, filename, tokenize)
var folded = fold(ast)
var compiled = mcode(folded)

var pad_right = function(s, w) {
  var r = s
  while (length(r) < w) {
    r = r + " "
  }
  return r
}

var fmt_val = function(v) {
  if (is_null(v)) {
    return "null"
  }
  if (is_number(v)) {
    return text(v)
  }
  if (is_text(v)) {
    return `"${v}"`
  }
  if (is_object(v)) {
    return json.encode(v)
  }
  if (is_logical(v)) {
    return v ? "true" : "false"
  }
  return text(v)
}

var dump_function = function(func, name) {
  var nr_args = func.nr_args != null ? func.nr_args : 0
  var nr_slots = func.nr_slots != null ? func.nr_slots : 0
  var nr_close = func.nr_close_slots != null ? func.nr_close_slots : 0
  var instrs = func.instructions
  var i = 0
  var pc = 0
  var instr = null
  var op = null
  var n = 0
  var parts = null
  var j = 0
  var operands = null
  var pc_str = null
  var op_str = null
  print(`\n=== ${name} (args=${text(nr_args)}, slots=${text(nr_slots)}, closures=${text(nr_close)}) ===`)
  if (instrs == null || length(instrs) == 0) {
    print("  (empty)")
    return null
  }
  while (i < length(instrs)) {
    instr = instrs[i]
    if (is_text(instr)) {
      if (!starts_with(instr, "_nop_")) {
        print(`${instr}:`)
      }
    } else if (is_array(instr)) {
      op = instr[0]
      n = length(instr)
      parts = []
      j = 1
      while (j < n - 2) {
        push(parts, fmt_val(instr[j]))
        j = j + 1
      }
      operands = text(parts, ", ")
      pc_str = pad_right(text(pc), 5)
      op_str = pad_right(op, 14)
      print(`  ${pc_str} ${op_str} ${operands}`)
      pc = pc + 1
    }
    i = i + 1
  }
  return null
}

var main_name = null
var fi = 0
var func = null
var fname = null

// Dump main
if (compiled.main != null) {
  main_name = compiled.name != null ? compiled.name : "<main>"
  dump_function(compiled.main, main_name)
}

// Dump sub-functions
if (compiled.functions != null) {
  fi = 0
  while (fi < length(compiled.functions)) {
    func = compiled.functions[fi]
    fname = func.name != null ? func.name : `<func_${text(fi)}>`
    dump_function(func, `[${text(fi)}] ${fname}`)
    fi = fi + 1
  }
}
