mod advanced;
pub(crate) use advanced::{fmt_calculus, fmt_linear_algebra, fmt_logic_sets, fmt_relations};
use crate::ast::{BinaryOp, Expression, UnaryOp};
use std::fmt;
pub(crate) fn precedence(op: BinaryOp) -> u8 {
match op {
BinaryOp::Add | BinaryOp::Sub | BinaryOp::PlusMinus | BinaryOp::MinusPlus => 1,
BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod => 2,
BinaryOp::Pow => 3,
}
}
pub(crate) fn needs_parens(child: &Expression, parent_op: BinaryOp, is_right: bool) -> bool {
match child {
Expression::Binary { op: child_op, .. } => {
let parent_prec = precedence(parent_op);
let child_prec = precedence(*child_op);
if child_prec < parent_prec {
return true;
}
if child_prec == parent_prec {
match (parent_op, *child_op) {
(BinaryOp::Pow, BinaryOp::Pow) => return true,
(BinaryOp::Sub, BinaryOp::Sub) | (BinaryOp::Div, BinaryOp::Div) => {
return is_right
}
_ => {}
}
}
false
}
_ => false,
}
}
pub(crate) fn fmt_literal(expr: &Expression, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match expr {
Expression::Integer(n) => write!(f, "{}", n),
Expression::Float(x) => write!(f, "{}", x),
Expression::Rational {
numerator,
denominator,
} => write!(f, "{}/{}", numerator, denominator),
Expression::Complex { real, imaginary } => write!(f, "{} + {}i", real, imaginary),
Expression::Quaternion { real, i, j, k } => {
write!(f, "{} + {}i + {}j + {}k", real, i, j, k)
}
Expression::Variable(name) => write!(f, "{}", name),
Expression::Constant(c) => write!(f, "{}", c),
_ => unreachable!("fmt_literal called on non-literal"),
}
}
pub(crate) fn fmt_binary(expr: &Expression, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Expression::Binary { op, left, right } = expr else {
unreachable!("fmt_binary called on non-binary");
};
if needs_parens(left, *op, false) {
write!(f, "({})", left)?;
} else {
write!(f, "{}", left)?;
}
write!(f, " {} ", op)?;
if needs_parens(right, *op, true) {
write!(f, "({})", right)
} else {
write!(f, "{}", right)
}
}
pub(crate) fn fmt_unary(expr: &Expression, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Expression::Unary { op, operand } = expr else {
unreachable!("fmt_unary called on non-unary");
};
let is_binary = matches!(**operand, Expression::Binary { .. });
match op {
UnaryOp::Factorial | UnaryOp::Transpose => {
if is_binary {
write!(f, "({}){}", operand, op)
} else {
write!(f, "{}{}", operand, op)
}
}
UnaryOp::Neg | UnaryOp::Pos => {
if is_binary {
write!(f, "{}({})", op, operand)
} else {
write!(f, "{}{}", op, operand)
}
}
}
}
pub(crate) fn fmt_function(expr: &Expression, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Expression::Function { name, args } = expr else {
unreachable!("fmt_function called on non-function");
};
write!(f, "{}(", name)?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
}
write!(f, ")")
}