// An expression, written back as the notation reads it.
//
// For a READER — a diagnostic that quotes the rule that failed, a panel that
// shows what a component declares. Not a serialiser: nothing parses this back,
// and a shape it cannot spell is written as a description rather than refused,
// because a sentence with a gap in it is still a better answer than none.

///|
/// An expression as `.tutu` source.
pub fn Expr::show_source(self : Expr) -> String {
  match self {
    ELit(lit~, ..) => show_lit(lit)
    ETpl(parts~, ..) => {
      let out = StringBuilder()
      out.write_string("@str{")
      for p in parts {
        match p {
          TText(text~, ..) => out.write_string(text)
          TExpr(e) => out.write_string("@(" + e.show_source() + ")")
        }
      }
      out.write_string("}")
      out.to_string()
    }
    ERead(place~, ..) => show_place(place)
    ERef(place~, ..) => "&" + show_place(place)
    EMethod(name~, ..) => name + "()"
    EDyn(name~, ..) => "dyn." + name
    EMacroVar(name~, ..) => "^" + name
    EConfigVar(name~, ..) => "host." + name
    EName(name~, ..) => name
    ETypeName(name~, ..) => name
    EEventPath(segments~, ..) => "e." + segments.join(".")
    EApp(name~, args~, ..) => {
      let parts = []
      for a in args {
        parts.push(a.show_source())
      }
      name + "(" + parts.join(", ") + ")"
    }
    // The operands and the operators alternate: `a + b + c` is three operands
    // and two `+`, which is how one chain of one precedence is carried.
    EChain(ops~, operands~, ..) => {
      let out = StringBuilder()
      for i, e in operands {
        if i > 0 && i - 1 < ops.length() {
          out.write_string(" " + ops[i - 1] + " ")
        }
        out.write_string(e.show_source())
      }
      out.to_string()
    }
    EUnary(op~, operand~, ..) =>
      (if op is UNot { "!" } else { "-" }) + operand.show_source()
    EIf(cond~, then_~, else_~, ..) =>
      "if " +
      cond.show_source() +
      " | " +
      then_.show_source() +
      " | " +
      else_.show_source()
  }
}

///|
fn show_lit(l : Lit) -> String {
  match l {
    LNull => "none"
    LBool(b) => if b { "true" } else { "false" }
    LNum(n) =>
      if n == n.to_int().to_double() {
        n.to_int().to_string()
      } else {
        n.to_string()
      }
    LStr(s) => "\"" + s.replace_all(old="\"", new="\\\"") + "\""
  }
}

///|
fn show_place(p : Place) -> String {
  let out = StringBuilder()
  match p.root {
    PState(f) => out.write_string("it." + f)
    PBind(n) => out.write_string(n)
    PTarget => out.write_string(target_bind)
    PParam(n) => out.write_string(n)
  }
  for s in p.steps {
    match s {
      PField(f) => out.write_string("." + f)
      PIndex(e) => out.write_string("[" + e.show_source() + "]")
    }
  }
  out.to_string()
}