///|
fn emsp(spec : Spec) -> Emix {
  {
    spec,
    mods: [
      file(path("astx.mbt"), east(spec)),
      file(path("pars.mbt"), eprs(spec)),
      file(path("vald.mbt"), evld(spec)),
      file(path("prnt.mbt"), eprt(spec)),
    ],
  }
}

///|
pub fn emit(value : Dslx) -> Emix {
  emsp(value.spec)
}

///|
pub fn Dslx::emit(self : Dslx) -> Emix {
  emsp(self.spec)
}

///|
pub fn file(path : String, text : String) -> File {
  { path, text }
}

///|
pub fn path(text : String) -> String {
  text
}

///|
pub fn mods(value : Emix) -> Array[File] {
  value.mods
}

///|
fn east(spec : Spec) -> String {
  let mut out = "// generated astx for " + spec.name + "\n"
  for node in spec.nods {
    out = out + "pub(all) enum " + node.name + " {\n"
    for item in node.casx {
      out = out + "  " + item.name + "\n"
    }
    out = out + "} derive(Debug, Eq)\n"
  }
  out
}

///|
fn eprs(spec : Spec) -> String {
  let mut out = "// generated pars for " + spec.name + "\n"
  out = out + "fn generated_spec() -> Dslx {\n"
  out = out + "  " + sspe(spec) + "\n"
  out = out + "}\n"
  for rule in spec.ruls {
    out = out +
      "pub fn " +
      rule.name +
      "(text : String) -> Pout { runx(generated_spec(), " +
      qstr(rule.name) +
      ", text) }\n"
  }
  out
}

///|
fn sspe(spec : Spec) -> String {
  let mut out = "dslx(" + qstr(spec.name) + ")"
  for node in spec.nods {
    out = out + ".node(" + snod(node) + ")"
  }
  for rule in spec.ruls {
    out = out +
      ".rule(rule(" +
      qstr(rule.name) +
      ").body(" +
      sexp(rule.expr) +
      "))"
  }
  out
}

///|
fn snod(node : Node) -> String {
  let mut out = "node(" + qstr(node.name) + ")"
  for item in node.casx {
    out = out + ".case(" + scas(item) + ")"
  }
  out
}

///|
fn scas(item : Case) -> String {
  let mut out = "case(" + qstr(item.name) + ")"
  for fld in item.flds {
    out = out + ".fldx(fldx(" + qstr(fld.name) + ", " + qstr(fld.typx) + "))"
  }
  out
}

///|
fn sexp(expr : Expr) -> String {
  match expr {
    Seqx(items) => "seqx([" + sexa(items) + "])"
    Altx(items) => "altx([" + sexa(items) + "])"
    Many(item) => "many(" + sexp(item) + ")"
    Optx(item) => "optx(" + sexp(item) + ")"
    Litx(text) => "litx(" + qstr(text) + ")"
    Tokn(name) => "tokn(" + qstr(name) + ")"
    Rgxx(patt) => "rgxx(" + qstr(patt) + ")"
    Refx(name) => "refx(" + qstr(name) + ")"
    Atom => "atom()"
  }
}

///|
fn sexa(items : Array[Expr]) -> String {
  let mut out = ""
  let mut i = 0
  for item in items {
    if i > 0 {
      out = out + ", "
    }
    out = out + sexp(item)
    i = i + 1
  }
  out
}

///|
fn qstr(text : String) -> String {
  let mut out = "\""
  let mut posx = 0
  while posx < text.length() {
    let ch = text[posx].to_int()
    if ch == 34 {
      out = out + "\\\""
    } else if ch == 92 {
      out = out + "\\\\"
    } else if ch == 10 {
      out = out + "\\n"
    } else if ch == 13 {
      out = out + "\\r"
    } else if ch == 9 {
      out = out + "\\t"
    } else {
      out = out + text.view(start_offset=posx, end_offset=posx + 1).to_owned()
    }
    posx = posx + 1
  }
  out + "\""
}

///|
fn evld(spec : Spec) -> String {
  "// generated vald for " + spec.name + "\n"
}

///|
fn eprt(spec : Spec) -> String {
  "// generated prnt for " + spec.name + "\n"
}