Fix formatting of binary operations

The precedence rules of binary operations are a bit trickier than
expected. The fact that a parent and a child term have the same
precedence level doesn’t automatically mean that parentheses can be
omitted. This is the case, for example, with

    a - (b + c)

While addition and subtraction have the same precedence level, the
parenthesis cannot be omitted. In general, this happens on the right-
hand side of the subtraction, division, and modulo operators if the
right-hand side has the same precedence level.

This patch fixes the output of binary operations accordingly.
This commit is contained in:
Patrick Lühne 2020-03-30 05:57:56 +02:00
parent 549f127729
commit 551c35ed75
Signed by: patrick
GPG Key ID: 05F3611E97A70ABF
1 changed files with 71 additions and 27 deletions

View File

@ -160,7 +160,6 @@ impl<'term> std::fmt::Debug for TermDisplay<'term>
Some(parent_precedence) => precedence > parent_precedence, Some(parent_precedence) => precedence > parent_precedence,
None => false, None => false,
}; };
let precedence = Some(precedence);
if requires_parentheses if requires_parentheses
{ {
@ -201,36 +200,81 @@ impl<'term> std::fmt::Debug for TermDisplay<'term>
Ok(()) Ok(())
}, },
crate::Term::BinaryOperation( crate::Term::BinaryOperation(binary_operation) =>
crate::BinaryOperation{operator: crate::BinaryOperator::Add, left, right}) {
=> write!(format, "{:?} + {:?}", display_term(left, precedence), let operator_string = match binary_operation.operator
display_term(right, precedence)), {
crate::Term::BinaryOperation( crate::BinaryOperator::Add => "+",
crate::BinaryOperation{operator: crate::BinaryOperator::Subtract, left, right}) crate::BinaryOperator::Subtract => "-",
=> write!(format, "{:?} - {:?}", display_term(left, precedence), crate::BinaryOperator::Multiply => "*",
display_term(right, precedence)), crate::BinaryOperator::Divide => "/",
crate::Term::BinaryOperation( crate::BinaryOperator::Modulo => "%",
crate::BinaryOperation{operator: crate::BinaryOperator::Multiply, left, right}) crate::BinaryOperator::Exponentiate => "**",
=> write!(format, "{:?} * {:?}", display_term(left, precedence), };
display_term(right, precedence)),
crate::Term::BinaryOperation( let left_requires_parentheses = binary_operation.left.precedence() == precedence
crate::BinaryOperation{operator: crate::BinaryOperator::Divide, left, right}) // Exponentiation is right-associative and thus requires parentheses when
=> write!(format, "{:?} / {:?}", display_term(left, precedence), // nested on the left side
display_term(right, precedence)), && binary_operation.operator == crate::BinaryOperator::Exponentiate;
crate::Term::BinaryOperation(
crate::BinaryOperation{operator: crate::BinaryOperator::Modulo, left, right}) // The subtraction, division, and modulo operators require parentheses around the
=> write!(format, "{:?} % {:?}", display_term(left, precedence), // right argument even if it has the same precedence
display_term(right, precedence)), let operator_requires_right_priority = match binary_operation.operator
crate::Term::BinaryOperation( {
crate::BinaryOperation{operator: crate::BinaryOperator::Exponentiate, left, right}) crate::BinaryOperator::Subtract
=> write!(format, "{:?} ** {:?}", display_term(left, precedence), | crate::BinaryOperator::Divide
display_term(right, precedence)), | crate::BinaryOperator::Modulo
=> true,
_ => false,
};
// Additionally, modulo operations nested to the right of another multiplicative
// operation always require parentheses
let right_requires_priority = match *binary_operation.right
{
crate::Term::BinaryOperation(
crate::BinaryOperation{operator: crate::BinaryOperator::Modulo, ..})
=> true,
_ => false,
};
let right_requires_parentheses = binary_operation.right.precedence() == precedence
&& (operator_requires_right_priority || right_requires_priority);
if left_requires_parentheses
{
write!(format, "(")?;
}
write!(format, "{:?}", display_term(&binary_operation.left, Some(precedence)))?;
if left_requires_parentheses
{
write!(format, ")")?;
}
write!(format, " {} ", operator_string)?;
if right_requires_parentheses
{
write!(format, "(")?;
}
write!(format, "{:?}", display_term(&binary_operation.right, Some(precedence)))?;
if right_requires_parentheses
{
write!(format, ")")?;
}
Ok(())
},
crate::Term::UnaryOperation( crate::Term::UnaryOperation(
crate::UnaryOperation{operator: crate::UnaryOperator::Negative, argument}) crate::UnaryOperation{operator: crate::UnaryOperator::Negative, argument})
=> write!(format, "-{:?}", display_term(argument, precedence)), => write!(format, "-{:?}", display_term(argument, Some(precedence))),
crate::Term::UnaryOperation( crate::Term::UnaryOperation(
crate::UnaryOperation{operator: crate::UnaryOperator::AbsoluteValue, argument}) crate::UnaryOperation{operator: crate::UnaryOperator::AbsoluteValue, argument})
=> write!(format, "|{:?}|", display_term(argument, precedence)), => write!(format, "|{:?}|", display_term(argument, None)),
}?; }?;
if requires_parentheses if requires_parentheses