///|
/// A toy s-expression AST, to show what building a `Doc` from a real tree looks
/// like. Run with `moon run cmd/main`.
enum Sexpr {
  Atom(String)
  List(Array[Sexpr])
}

///|
/// Turn an s-expression into a document describing every way it may be laid
/// out. Nothing here mentions widths or indentation levels: the combinators
/// take care of both.
fn Sexpr::to_doc(self : Sexpr) -> @pp.Doc {
  match self {
    Atom(name) => @pp.txt(name)
    List([]) => @pp.txt("()")
    // `(lambda args body)` gets a layout of its own...
    List([Atom("lambda"), defn, body]) =>
      @pp.lambda_like_sexpr(@pp.txt("lambda"), defn.to_doc(), body.to_doc())
    // ...`(begin ...)` always goes vertical...
    List([Atom("begin"), .. bodies]) =>
      @pp.begin_like_sexpr(@pp.txt("begin"), bodies.map(body => body.to_doc()))
    // ...and everything else is a plain call.
    List([func, .. args]) =>
      @pp.standard_sexpr(func.to_doc(), args.map(arg => arg.to_doc()))
  }
}

///|
fn example() -> Sexpr {
  List([
    Atom("begin"),
    List([
      Atom("define"),
      Atom("greet"),
      List([
        Atom("lambda"),
        Atom("(name)"),
        List([
          Atom("string-append"),
          Atom("\"Welcome back, \""),
          Atom("name"),
          Atom("\"! Good to see you again.\""),
        ]),
      ]),
    ]),
    List([Atom("display"), List([Atom("greet"), Atom("\"Ada\"")])]),
  ])
}

///|
fn main {
  let doc = example().to_doc()
  for width in [80, 46, 30] {
    println("--- the same document at width \{width} ---")
    println(doc.display_string(width~))
    println("")
  }
  let words = "the quick brown fox jumps over the lazy dog"
    .split(" ")
    .map(word => @pp.txt(word.to_owned()))
    .collect()
  println("--- wrap at width 20 ---")
  println(@pp.wrap(words).display_string(width=20))
}