// examples/mini_compiler_pipeline — runnable demonstration of the
// `src/mini_compiler` direction: a mini-ML language driven end to end
// through lexing, parsing, Hindley-Milner inference, optimization, a
// bytecode VM (peephole + tail-call optimization) and JS code emission,
// with the interpreter used as a differential oracle.
//
// Run with:
//   moon run examples/mini_compiler_pipeline

///|
/// Run one source program through the full pipeline and print the evidence.
fn pipeline(title : String, src : String) -> Unit {
  println("=== \{title} ===")
  println("source: \{src}")
  match @mc.try_run_demo(src) {
    Ok(trace) => {
      println("principal type : \{trace.principal_type}")
      println("optimized form : \{trace.optimized_form}")
      let interp = show_val(trace.interp_result)
      let vm = show_val(trace.vm_result)
      println("interpreter    : \{interp}")
      println("bytecode VM    : \{vm}")
      println(
        if trace.interp_result == trace.vm_result {
          "cross-check    : interpreter == VM ✓"
        } else {
          "cross-check    : MISMATCH ✗"
        },
      )
    }
    Err(diag) => println(@mc.render_diagnostic(src, diag))
  }
  println("")
}

///|
fn show_val(v : @mc.Val) -> String {
  match v {
    VInt(n) => "VInt(\{n})"
    VBool(b) => "VBool(\{b})"
    VClosure(_, _, _) => ""
    VTuple(items) => {
      let parts = []
      for item in items {
        parts.push(show_val(item))
      }
      let sb = StringBuilder::new()
      sb.write_string("VTuple(")
      for i in 0.. 0 {
          sb.write_string(", ")
        }
        sb.write_string(parts[i])
      }
      sb.write_string(")")
      sb.to_string()
    }
  }
}

///|
/// Demonstrate deep tail recursion running in constant stack via TCO.
fn tco_demo() -> Unit {
  println("=== tail-call optimization ===")
  let src = "let rec sum = fun n -> fun acc -> if n == 0 then acc else sum (n - 1) (acc + n) in sum 100000 0"
  println("source: sum 100000 0 (tail-recursive accumulator)")
  match @mc.lex_ml(src) {
    Ok(tokens) =>
      match @mc.parse_ml(tokens) {
        Ok(expr) =>
          match @mc.infer(expr) {
            Ok((texpr, _)) => {
              let bc = @mc.tco_opt(@mc.peephole_opt(@mc.compile_ml(texpr)))
              let (v, depth) = @mc.run_tco_with_depth(bc, @mc.ml_env_empty())
              println(
                "result         : \{show_val(v)} (100000*100001/2 = 5000050000 wrapped to 32-bit Int)",
              )
              println("max frame depth: \{depth} (constant-stack tail calls)")
            }
            Err(diag) => println(@mc.render_diagnostic(src, diag))
          }
        Err(diag) => println(@mc.render_diagnostic(src, diag))
      }
    Err(diag) => println(@mc.render_diagnostic(src, diag))
  }
  println("")
}

///|
/// Emit JavaScript for a program to show the multi-backend code generator.
fn emit_demo() -> Unit {
  println("=== JS emission ===")
  let src = @mc.demo_fib_src
  match @mc.compile_ml_to_js(src) {
    Ok(js) => {
      println("source: \{src}")
      println("emitted JS:")
      println(js)
    }
    Err(diag) => println(@mc.render_diagnostic(src, diag))
  }
}

///|
fn main {
  pipeline("factorial", @mc.demo_factorial_src)
  pipeline("fibonacci", @mc.demo_fib_src)
  pipeline(
    "higher-order + let-polymorphism", "let id = fun x -> x in (id 1) + (if id true then 1 else 0)",
  )
  pipeline("type error (rendered diagnostic)", "1 + true")
  tco_demo()
  emit_demo()
}