use mathlex::{parse, parse_latex, BinaryOp, Expression, ToLatex, UnaryOp};
fn var(name: &str) -> Expression {
Expression::Variable(name.to_string())
}
fn int(n: i64) -> Expression {
Expression::Integer(n)
}
#[test]
fn test_unary_neg_of_sum() {
let ast = Expression::Unary {
op: UnaryOp::Neg,
operand: Box::new(Expression::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(var("b")),
}),
};
let s = ast.to_string();
assert!(s.contains("("), "Should have parens: {}", s);
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "-(a+b) should round-trip");
}
#[test]
fn test_unary_neg_of_product() {
let ast = Expression::Unary {
op: UnaryOp::Neg,
operand: Box::new(Expression::Binary {
op: BinaryOp::Mul,
left: Box::new(var("a")),
right: Box::new(var("b")),
}),
};
let s = ast.to_string();
assert!(s.contains("("), "Should have parens: {}", s);
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "-(a*b) should round-trip");
}
#[test]
fn test_power_left_associativity_needs_parens() {
let ast = Expression::Binary {
op: BinaryOp::Pow,
left: Box::new(Expression::Binary {
op: BinaryOp::Pow,
left: Box::new(var("a")),
right: Box::new(var("b")),
}),
right: Box::new(var("c")),
};
let s = ast.to_string();
assert!(s.contains("("), "Should have parens for (a^b)^c: {}", s);
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "(a^b)^c should round-trip");
}
#[test]
fn test_power_right_associativity_no_parens() {
let ast = Expression::Binary {
op: BinaryOp::Pow,
left: Box::new(var("a")),
right: Box::new(Expression::Binary {
op: BinaryOp::Pow,
left: Box::new(var("b")),
right: Box::new(var("c")),
}),
};
let s = ast.to_string();
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "a^(b^c) should round-trip");
}
#[test]
fn test_sum_in_product_needs_parens() {
let ast = Expression::Binary {
op: BinaryOp::Mul,
left: Box::new(Expression::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(var("b")),
}),
right: Box::new(var("c")),
};
let s = ast.to_string();
assert!(s.contains("("), "Should have parens: {}", s);
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "(a+b)*c should round-trip");
}
#[test]
fn test_product_in_sum_no_parens() {
let ast = Expression::Binary {
op: BinaryOp::Add,
left: Box::new(Expression::Binary {
op: BinaryOp::Mul,
left: Box::new(var("a")),
right: Box::new(var("b")),
}),
right: Box::new(var("c")),
};
let s = ast.to_string();
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "a*b+c should round-trip");
}
#[test]
fn test_subtraction_associativity() {
let left_assoc = Expression::Binary {
op: BinaryOp::Sub,
left: Box::new(Expression::Binary {
op: BinaryOp::Sub,
left: Box::new(var("a")),
right: Box::new(var("b")),
}),
right: Box::new(var("c")),
};
let s1 = left_assoc.to_string();
let parsed1 = parse(&s1).unwrap();
assert_eq!(left_assoc, parsed1, "(a-b)-c should round-trip");
let right_assoc = Expression::Binary {
op: BinaryOp::Sub,
left: Box::new(var("a")),
right: Box::new(Expression::Binary {
op: BinaryOp::Sub,
left: Box::new(var("b")),
right: Box::new(var("c")),
}),
};
let s2 = right_assoc.to_string();
assert!(s2.contains("("), "a-(b-c) should have parens: {}", s2);
let parsed2 = parse(&s2).unwrap();
assert_eq!(right_assoc, parsed2, "a-(b-c) should round-trip");
}
#[test]
fn test_division_associativity() {
let ast = Expression::Binary {
op: BinaryOp::Div,
left: Box::new(var("a")),
right: Box::new(Expression::Binary {
op: BinaryOp::Div,
left: Box::new(var("b")),
right: Box::new(var("c")),
}),
};
let s = ast.to_string();
assert!(s.contains("("), "a/(b/c) should have parens: {}", s);
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "a/(b/c) should round-trip");
}
#[test]
fn test_nested_unary() {
let ast = Expression::Unary {
op: UnaryOp::Neg,
operand: Box::new(Expression::Unary {
op: UnaryOp::Neg,
operand: Box::new(var("a")),
}),
};
let s = ast.to_string();
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "--a should round-trip");
}
#[test]
fn test_complex_precedence_chain() {
let ast = Expression::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(Expression::Binary {
op: BinaryOp::Mul,
left: Box::new(var("b")),
right: Box::new(Expression::Binary {
op: BinaryOp::Pow,
left: Box::new(var("c")),
right: Box::new(var("d")),
}),
}),
};
let s = ast.to_string();
let parsed = parse(&s).unwrap();
assert_eq!(ast, parsed, "a + b * c^d should round-trip");
}
#[test]
fn test_latex_unary_neg_of_sum() {
let ast = Expression::Unary {
op: UnaryOp::Neg,
operand: Box::new(Expression::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(var("b")),
}),
};
let s = ast.to_latex();
assert!(
s.contains("(") || s.contains("\\left"),
"Should have parens: {}",
s
);
let parsed = parse_latex(&s).unwrap();
assert_eq!(ast, parsed, "LaTeX -(a+b) should round-trip");
}
#[test]
fn test_latex_fraction_in_sum() {
let ast = Expression::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(Expression::Binary {
op: BinaryOp::Div,
left: Box::new(int(1)),
right: Box::new(int(2)),
}),
};
let s = ast.to_latex();
let parsed = parse_latex(&s).unwrap();
assert_eq!(ast, parsed, "LaTeX a + frac should round-trip");
}
#[test]
fn test_latex_power_precedence() {
let ast = Expression::Binary {
op: BinaryOp::Pow,
left: Box::new(Expression::Binary {
op: BinaryOp::Pow,
left: Box::new(var("a")),
right: Box::new(var("b")),
}),
right: Box::new(var("c")),
};
let s = ast.to_latex();
let parsed = parse_latex(&s).unwrap();
assert_eq!(ast, parsed, "LaTeX (a^b)^c should round-trip");
}
#[test]
fn test_latex_mul_precedence() {
let ast = Expression::Binary {
op: BinaryOp::Mul,
left: Box::new(Expression::Binary {
op: BinaryOp::Add,
left: Box::new(var("a")),
right: Box::new(var("b")),
}),
right: Box::new(var("c")),
};
let s = ast.to_latex();
let parsed = parse_latex(&s).unwrap();
assert_eq!(ast, parsed, "LaTeX (a+b)*c should round-trip");
}