///|
fn guard_bin_op_name(g : @ast.ClauseGuard) -> @ast.BinOp? {
match g {
BinaryOperator(operator~, ..) => Some(operator)
_ => None
}
}
///|
fn guard_precedence(g : @ast.ClauseGuard) -> Int32 {
match guard_bin_op_name(g) {
Some(op) => precedence(op)
None => max_precedence
}
}
///|
/// The compiler's `case`.
fn Formatter::case_(
self : Formatter,
subjects : ArrayView[@ast.Expr],
clauses : ArrayView[@ast.Clause],
location : @span.SrcSpan,
) -> @pretty.Doc raise FormatError {
let subject_docs = []
for subject in subjects {
subject_docs.push(group(self.expr(subject)))
}
let subjects_doc = group(
@pretty.next_break_fits(
nest(case_break + @pretty.join(subject_docs, comma_break)) +
breakable_space +
t("{"),
Disabled,
),
)
let clause_docs = []
for i, clause in clauses {
clause_docs.push(group(self.clause(clause, i)))
}
let clauses_doc = @pretty.concat(clause_docs)
let comments = self.pop_comments(location.end)
let closing = match printed_comments(comments[:], false) {
None => line_doc + t("}")
Some(c) => nest(line_doc + c) + line_doc + t("}")
}
@pretty.force_break(subjects_doc + nest(line_doc + clauses_doc) + closing)
}
///|
/// The compiler's `clause`.
fn Formatter::clause(
self : Formatter,
clause : @ast.Clause,
index : Int32,
) -> @pretty.Doc raise FormatError {
let space_before = self.pop_empty_lines(clause.location.start)
let comments = self.pop_comments(clause.location.start)
let clause_doc = match clause.guard_ {
None => self.alternative_patterns(clause)
Some(g) =>
self.alternative_patterns(clause) +
nest(breakable_space) +
if_space +
nest(group(self.clause_guard(g)))
}
let has_guard = clause.guard_ is Some(_)
let has_multiple_subjects = clause.pattern.length() > 1
let arrow_break = if has_guard || has_multiple_subjects {
breakable_space
} else {
t(" ")
}
let clause_doc = group(
group(clause_doc + arrow_break + t("->")) +
self.case_clause_value(clause.then),
)
let clause_doc = match printed_comments(comments[:], false) {
Some(c) => c + line_doc + clause_doc
None => clause_doc
}
if index == 0 {
clause_doc
} else if space_before {
two_lines_doc + clause_doc
} else {
line_doc + clause_doc
}
}
///|
/// The compiler's `alternative_patterns`.
fn Formatter::alternative_patterns(
self : Formatter,
clause : @ast.Clause,
) -> @pretty.Doc raise FormatError {
let has_guard = clause.guard_ is Some(_)
let has_multiple_subjects = clause.pattern.length() > 1
let alternatives_separator = if has_guard && !has_multiple_subjects {
nest(breakable_space) + t("| ")
} else {
breakable_space + t("| ")
}
let alternatives : Array[ArrayView[@ast.Pattern]] = [clause.pattern]
for a in clause.alternative_patterns {
alternatives.push(a)
}
let alternative_docs = []
for alternative_index, patterns in alternatives {
let pattern_docs = []
for pattern_index, pattern in patterns {
let is_first_pattern_of_clause = pattern_index == 0 &&
alternative_index == 0
let pattern_doc = self.pattern(pattern)
pattern_docs.push(
if is_first_pattern_of_clause {
pattern_doc
} else {
nest(pattern_doc)
},
)
}
alternative_docs.push(group(@pretty.join(pattern_docs, nest(comma_break))))
}
@pretty.join(alternative_docs, alternatives_separator)
}
///|
/// The compiler's `clause_guard_bin_op`, with the left spine walked in a
/// loop: the comments before each left side are popped top-down first (the
/// compiler pops them on the way down), then the leaf, then each right side
/// from the innermost link out.
fn Formatter::clause_guard_bin_op(
self : Formatter,
name : @ast.BinOp,
left : @ast.ClauseGuard,
right : @ast.ClauseGuard,
) -> @pretty.Doc raise FormatError {
let ops = [name]
let rights = [right]
let lefts = [left]
let mut leaf = left
while leaf is BinaryOperator(operator~, left=l, right=r, ..) {
ops.push(operator)
rights.push(r)
lefts.push(l)
leaf = l
}
let n = ops.length() - 1
let left_comments = []
for k = 0; k <= n; k = k + 1 {
left_comments.push(self.pop_comments(lefts[k].location().start))
}
let mut doc = self.clause_guard_bin_op_side(
ops[n],
leaf,
guard_precedence(leaf),
)
doc = commented(doc, left_comments[n][:])
for k = n; k >= 0; k = k - 1 {
let right_comments = self.pop_comments(rights[k].location().start)
let right_doc = self.clause_guard_bin_op_side(
ops[k],
rights[k],
guard_precedence(rights[k]) - 1,
)
let right_doc = commented(
binop(ops[k]) + t(" ") + right_doc,
right_comments[:],
)
doc = doc + breakable_space + right_doc
if k > 0 {
// Link k is the left side of link k - 1 (`clause_guard_bin_op_side`).
let side_precedence = precedence(ops[k])
doc = if can_be_grouped_with(ops[k], ops[k - 1]) {
operator_side(doc, precedence(ops[k - 1]), side_precedence)
} else {
operator_side(group(doc), precedence(ops[k - 1]), side_precedence)
}
doc = commented(doc, left_comments[k - 1][:])
}
}
doc
}
///|
/// The compiler's `clause_guard_bin_op_side`.
fn Formatter::clause_guard_bin_op_side(
self : Formatter,
name : @ast.BinOp,
side : @ast.ClauseGuard,
side_precedence : Int32,
) -> @pretty.Doc raise FormatError {
let side_doc = self.clause_guard(side)
match guard_bin_op_name(side) {
Some(side_name) if can_be_grouped_with(side_name, name) =>
operator_side(side_doc, precedence(name), side_precedence)
_ => operator_side(group(side_doc), precedence(name), side_precedence)
}
}
///|
/// The compiler's `clause_guard`. One unit of fuel per nesting level.
fn Formatter::clause_guard(
self : Formatter,
guard_ : @ast.ClauseGuard,
) -> @pretty.Doc raise FormatError {
self.nested(op_format, () => {
match guard_ {
BinaryOperator(operator~, left~, right~, ..) =>
self.clause_guard_bin_op(operator, left, right)
Var(name~, ..) => t(name)
TupleIndex(..) | FieldAccess(..) => self.guard_access_chain(guard_)
Constant(c) => self.const_expr(c)
Not(expression~, ..) => t("!") + self.clause_guard(expression)
Block(value~, ..) => group(wrap_block(self.clause_guard(value)))
}
})
}
///|
/// The compiler's `TupleIndex` and `FieldAccess` guard arms, for a whole
/// chain in a loop: the parser accepts a chain of any length.
fn Formatter::guard_access_chain(
self : Formatter,
guard_ : @ast.ClauseGuard,
) -> @pretty.Doc raise FormatError {
let suffixes : Array[String] = []
let mut base = guard_
let mut walking = true
while walking {
match base {
TupleIndex(tuple~, index~, ..) => {
suffixes.push(index.to_string())
base = tuple
}
FieldAccess(container~, label~, ..) => {
suffixes.push(label)
base = container
}
_ => walking = false
}
}
let mut doc = self.clause_guard(base)
for k = suffixes.length() - 1; k >= 0; k = k - 1 {
doc = doc + t(".") + t(suffixes[k])
}
doc
}