#![allow(clippy::result_large_err)]
use super::*;
impl LatexParser {
pub(super) fn expression_to_subscript_string(&self, expr: &Expression) -> ParseResult<String> {
match &expr.kind {
ExprKind::Integer(n) => Ok(n.to_string()),
ExprKind::Variable(s) => Ok(s.clone()),
ExprKind::Constant(c) => Ok(match c {
MathConstant::E => "e".to_string(),
MathConstant::I => "i".to_string(),
MathConstant::J => "j".to_string(),
MathConstant::K => "k".to_string(),
MathConstant::Pi => "pi".to_string(),
MathConstant::Infinity => "inf".to_string(),
MathConstant::NegInfinity => "neginf".to_string(),
MathConstant::NaN => "nan".to_string(),
}),
ExprKind::Binary { op, left, right } => {
let left_str = self.expression_to_subscript_string(left)?;
let right_str = self.expression_to_subscript_string(right)?;
let op_str = match op {
BinaryOp::Add => "plus",
BinaryOp::Sub => "minus",
BinaryOp::Mul => "times",
BinaryOp::Div => "div",
BinaryOp::Pow => "pow",
BinaryOp::Mod => "mod",
BinaryOp::PlusMinus => "pm",
BinaryOp::MinusPlus => "mp",
};
Ok(format!("{}{}{}", left_str, op_str, right_str))
}
ExprKind::Unary { op, operand } => {
let operand_str = self.expression_to_subscript_string(operand)?;
let op_str = match op {
crate::ast::UnaryOp::Neg => "neg",
crate::ast::UnaryOp::Pos => "pos",
crate::ast::UnaryOp::Factorial => "fact",
crate::ast::UnaryOp::Transpose => "T",
};
Ok(format!("{}{}", op_str, operand_str))
}
_ => Err(ParseError::invalid_subscript(
"subscript contains unsupported expression type",
Some(self.current_span()),
)),
}
}
pub(super) fn match_derivative_numerator(expr: &Expression) -> Option<(bool, u32)> {
match &expr.kind {
ExprKind::Variable(s) if s == "d" => Some((false, 1)),
ExprKind::Variable(s) if s == "partial" => Some((true, 1)),
ExprKind::Binary {
op: BinaryOp::Pow,
left,
right,
} => {
let is_partial = match &left.kind {
ExprKind::Variable(s) if s == "d" => false,
ExprKind::Variable(s) if s == "partial" => true,
_ => return None,
};
let order = match &right.kind {
ExprKind::Integer(n) if *n > 0 => *n as u32,
_ => return None,
};
Some((is_partial, order))
}
_ => None,
}
}
pub(super) fn match_derivative_denominator(expr: &Expression) -> Option<(bool, String, u32)> {
let ExprKind::Binary {
op: BinaryOp::Mul,
left,
right,
} = &expr.kind
else {
return None;
};
let is_partial = match &left.kind {
ExprKind::Variable(s) if s == "d" => false,
ExprKind::Variable(s) if s == "partial" => true,
_ => return None,
};
match &right.kind {
ExprKind::Variable(v) => Some((is_partial, v.clone(), 1)),
ExprKind::Binary {
op: BinaryOp::Pow,
left: var_expr,
right: order_expr,
} => {
let var = match &var_expr.kind {
ExprKind::Variable(v) => v.clone(),
_ => return None,
};
let order = match &order_expr.kind {
ExprKind::Integer(n) if *n > 0 => *n as u32,
_ => return None,
};
Some((is_partial, var, order))
}
_ => None,
}
}
pub(super) fn try_parse_derivative(
&mut self,
numerator: Expression,
denominator: Expression,
) -> ParseResult<Option<Expression>> {
let Some((is_partial, num_order)) = Self::match_derivative_numerator(&numerator) else {
return Ok(None);
};
let Some((denom_is_partial, var, denom_order)) =
Self::match_derivative_denominator(&denominator)
else {
return Ok(None);
};
if is_partial != denom_is_partial || num_order != denom_order {
return Ok(None);
}
let expr = self.parse_power()?;
let derivative: Expression = if is_partial {
ExprKind::PartialDerivative {
expr: Box::new(expr),
var,
order: num_order,
}
.into()
} else {
ExprKind::Derivative {
expr: Box::new(expr),
var,
order: num_order,
}
.into()
};
Ok(Some(derivative))
}
}