///|
/// A sequence of `|` alternatives.
fn build_alts(
  blocks : Array[@ast.Node],
  target : Target,
  head~ : Bool,
) -> Built {
  let armor = target is ArmorDoc
  let prefer_multi = target is MultiDoc
  let mut singles : Array[@doc.Doc]? = Some([])
  let multis : Array[@doc.Doc] = []
  let mut any_quotes = false
  for block in blocks {
    let groups = block.children()
    let mut sub_singles : Array[@doc.Doc]? = Some([])
    let sub_multis : Array[@doc.Doc] = []
    let mut all_atoms = true
    let mut one_atom = false
    for i in 0.. acc.push(s)
        _ => sub_singles = None
      }
      sub_multis.push(r.multi)
    }
    if groups.length() == 0 {
      // An alternative with nothing in it can only be written with `«»`.
      match singles {
        Some(acc) => acc.push(Str("|«»"))
        None => ()
      }
      multis.push(Str("|«»"))
      continue
    }
    // Single line: `| a; b`, or `|« a; b »` when a piece is not self-delimiting
    // and could otherwise run into what follows.
    let single = match sub_singles {
      Some(parts) if !prefer_multi || (parts.length() == 1 && one_atom) => {
        let line = @doc.join(parts, Str("; "))
        Some(
          if all_atoms {
            @doc.Doc::Seq([Str("| "), line])
          } else {
            Seq([Str("|« "), line, Str(" »")])
          },
        )
      }
      _ => None
    }
    match (singles, single) {
      (Some(acc), Some(s)) => acc.push(s)
      _ => singles = None
    }
    if prefer_multi {
      let stacked : @doc.Doc = Seq([Str("| "), Align(@doc.join(sub_multis, Nl))])
      multis.push(
        match (sub_singles, sub_multis.length()) {
          (Some(parts), 1) if one_atom =>
            Or(Seq([Str("| "), parts[0]]), stacked)
          _ => stacked
        },
      )
    } else {
      // The `|` itself is added by the caller, which is what lets the armoured
      // and plain forms differ only in their separator.
      multis.push(
        Nest(
          2,
          Seq([
            Nl,
            @doc.join(sub_multis, if armor { Seq([Str(";"), Nl]) } else { Nl }),
          ]),
        ),
      )
    }
  }
  let single : @doc.Doc? = if prefer_multi {
    None
  } else {
    match singles {
      Some(parts) => Some(@doc.join(parts, Str(" ")))
      None => None
    }
  }
  let multi : @doc.Doc = if prefer_multi {
    Seq([if head { @doc.empty } else { Nl }, Align(@doc.join(multis, Nl))])
  } else {
    Seq([
      Nl,
      Align(
        if armor {
          let out : Array[@doc.Doc] = [Str("|«")]
          for i in 0.. 0 {
              out.push(Seq([Nl, Str("»"), Nl, Str("|«")]))
            }
            out.push(multis[i])
          }
          out.push(Nl)
          out.push(Str("»"))
          Seq(out)
        } else {
          let out : Array[@doc.Doc] = [Str("|")]
          for i in 0.. 0 {
              out.push(Seq([Nl, Nl, Str("|")]))
            }
            out.push(multis[i])
          }
          Seq(out)
        },
      ),
    ])
  }
  { single, multi, quotes: any_quotes, is_atom: false, }
}

///|
/// A whole document: a sequence of groups.
fn build_multi(groups : Array[@ast.Node], target : Target) -> Built {
  let armor = target is ArmorDoc
  let prefer_multi = target is MultiDoc
  let mut singles : Array[@doc.Doc]? = Some([])
  let multis : Array[@doc.Doc] = []
  let mut any_quotes = false
  for i in 0.. acc.push(s)
      _ => singles = None
    }
    multis.push(r.multi)
  }
  let single : @doc.Doc? = match singles {
    Some(parts) =>
      if !prefer_multi || parts.length() <= 1 {
        Some(@doc.join(parts, Str("; ")))
      } else {
        None
      }
    None => None
  }
  let multi : @doc.Doc = if armor {
    Seq([
      Str(";«"),
      Nest(2, Seq([Nl, @doc.join(multis, Seq([Str(";"), Nl]))])),
      Nl,
      Str("»"),
    ])
  } else {
    @doc.join(multis, Nl)
  }
  { single, multi, quotes: any_quotes, is_atom: false, }
}