Add canonicalisation pass

This commit is contained in:
cdo committed 2026-09-26 13:32:41 +01:00
1 parent f22e916e0c
commit c4eca9defe
3 files changed
+146 -1

No files matched your search

+4 -1
View File
@@ -6,7 +6,10 @@ fn main() -> std::io::Result<()> {
eval::Normaliser::new(), eval::Normaliser::new(),
eval::Differentiator::new('x'), eval::Differentiator::new('x'),
eval::ConstFolder::new(), eval::ConstFolder::new(),
eval::Simplifier::new()); eval::Simplifier::new(),
eval::ConstFolder::new(),
eval::Canonicaliser::new()
);
let mut repl = Repl::new(std::io::stdin(), std::io::stdout(), eval); let mut repl = Repl::new(std::io::stdin(), std::io::stdout(), eval);
repl.run() repl.run()
} }
+2
View File
@@ -3,12 +3,14 @@ pub mod const_fold;
pub mod compose; pub mod compose;
pub mod differentiate; pub mod differentiate;
pub mod simplify; pub mod simplify;
pub mod canonicalise;
pub use normalise::*; pub use normalise::*;
pub use const_fold::*; pub use const_fold::*;
pub use compose::*; pub use compose::*;
pub use differentiate::*; pub use differentiate::*;
pub use simplify::*; pub use simplify::*;
pub use canonicalise::*;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Error { pub enum Error {
+140
View File
@@ -0,0 +1,140 @@
use crate::expression::Expression;
use crate::eval::{Error, Evaluator};
pub struct Canonicaliser;
impl Canonicaliser {
pub fn new() -> Self {
Self {}
}
}
impl Evaluator for Canonicaliser {
type Expr = Expression;
type Error = Error;
fn eval(&mut self, input: Expression) -> Result<Expression, Error> {
Ok(canonicalise(input))
}
}
fn canonicalise(expr: Expression) -> Expression {
match expr {
Expression::Sum(x, y) => recurse(Expression::Sum, x, y),
Expression::Product(x, y) => recurse(product, x, y),
Expression::Exponent(x, y) => recurse(Expression::Exponent, x, y),
_ => expr
}
}
fn product(
x: std::boxed::Box<Expression>,
y: std::boxed::Box<Expression>)
-> Expression
{
if let Some(_) = y.integer() {
Expression::Product(y, x)
} else {
Expression::Product(x, y)
}
}
fn recurse(
f: impl Fn(
std::boxed::Box<Expression>,
std::boxed::Box<Expression>) -> Expression,
x: std::boxed::Box<Expression>,
y: std::boxed::Box<Expression>)
-> Expression
{
f(boxed(x), boxed(y))
}
fn boxed(
mut expr: std::boxed::Box<Expression>) -> std::boxed::Box<Expression>
{
*expr = canonicalise(*expr);
expr
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn product_integer_symbol_is_unchanged() {
let input = Expression::product(
Expression::Integer(1),
Expression::Symbol('x'));
let result = Canonicaliser::new().eval(input).unwrap();
assert_eq!(
result,
Expression::product(
Expression::Integer(1),
Expression::Symbol('x')));
}
#[test]
fn product_symbol_integer_becomes_product_integer_symbol() {
let input = Expression::product(
Expression::Symbol('x'),
Expression::Integer(1));
let result = Canonicaliser::new().eval(input).unwrap();
assert_eq!(
result,
Expression::product(
Expression::Integer(1),
Expression::Symbol('x')));
}
#[test]
fn product_symbol_integer_nested_in_sum_is_reordered() {
let input = Expression::sum(
Expression::Integer(1),
Expression::product(
Expression::Symbol('x'),
Expression::Integer(1)));
let result = Canonicaliser::new().eval(input).unwrap();
assert_eq!(
result,
Expression::sum(
Expression::Integer(1),
Expression::product(
Expression::Integer(1),
Expression::Symbol('x'))));
}
#[test]
fn product_symbol_integer_nested_in_product_is_reordered() {
let input = Expression::product(
Expression::Integer(1),
Expression::product(
Expression::Symbol('x'),
Expression::Integer(1)));
let result = Canonicaliser::new().eval(input).unwrap();
assert_eq!(
result,
Expression::product(
Expression::Integer(1),
Expression::product(
Expression::Integer(1),
Expression::Symbol('x'))));
}
#[test]
fn exponent_symbol_integer_nested_in_exponent_is_reordered() {
let input = Expression::exponent(
Expression::Integer(1),
Expression::product(
Expression::Symbol('x'),
Expression::Integer(1)));
let result = Canonicaliser::new().eval(input).unwrap();
assert_eq!(
result,
Expression::exponent(
Expression::Integer(1),
Expression::product(
Expression::Integer(1),
Expression::Symbol('x'))));
}
}