///|
/// A description of ALL the ways a piece of output may be laid out.
///
/// Deliberately small: five constructors, and `Or` is ordered — the
/// single-line reading first, the multi-line one second. That ordering is what
/// the renderer relies on, and it is why this is not a general pretty-printing
/// document type.
///
/// The value is a DAG rather than a tree: the two branches of an `Or` share
/// their sub-documents, and a tree view of a nested document can be
/// exponentially larger than the graph. Nothing here copies, so building one
/// costs what it looks like it costs.
pub(all) enum Doc {
/// Printed literally.
Str(String)
/// A newline, then the current indentation as spaces.
Nl
/// Printed in order, at the same indentation.
Seq(Array[Doc])
/// Printed with the indentation increased by `n`.
Nest(Int, Doc)
/// Printed with the indentation set to the CURRENT column.
Align(Doc)
/// The single-line reading, then the multi-line one.
Or(Doc, Doc)
}
///|
pub let empty : Doc = Seq([])
///|
/// Everything in order.
pub fn seq(docs : Array[Doc]) -> Doc {
Seq(docs)
}
///|
/// `docs`, with `sep` between each pair.
pub fn join(docs : Array[Doc], sep : Doc) -> Doc {
if docs.length() <= 1 {
return Seq(docs)
}
let out = []
for i in 0.. 0 {
out.push(sep)
}
out.push(docs[i])
}
Seq(out)
}
///|
/// The document as an S-expression, in the shape the reference prints.
///
/// For comparing a document against the reference's own, which is the only way
/// to tell a layout disagreement (the renderer chose differently) from a
/// construction disagreement (the documents were never the same).
pub fn Doc::to_sexpr(self : Doc) -> String {
let buf = StringBuilder()
self.write_sexpr(buf)
buf.to_string()
}
///|
pub fn Doc::write_sexpr(self : Doc, buf : StringBuilder) -> Unit {
match self {
Str(s) => {
buf.write_char('"')
for c in s {
match c {
'"' => buf.write_string("\\\"")
'\\' => buf.write_string("\\\\")
'\n' => buf.write_string("\\n")
_ => buf.write_char(c)
}
}
buf.write_char('"')
}
Nl => buf.write_string("nl")
Seq(docs) => {
buf.write_string("(seq")
for d in docs {
buf.write_char(' ')
d.write_sexpr(buf)
}
buf.write_char(')')
}
Nest(n, inner) => {
buf.write_string("(nest \{n} ")
inner.write_sexpr(buf)
buf.write_char(')')
}
Align(inner) => {
buf.write_string("(align ")
inner.write_sexpr(buf)
buf.write_char(')')
}
Or(a, b) => {
buf.write_string("(or ")
a.write_sexpr(buf)
buf.write_char(' ')
b.write_sexpr(buf)
buf.write_char(')')
}
}
}