Implement evaluator composition

This commit is contained in:
cdo committed 2026-09-26 11:49:20 +01:00
1 parent 48f58d87fc
commit cdaf2e7013
2 files changed
+149

No files matched your search

+1
View File
@@ -1,5 +1,6 @@
pub mod normalise; pub mod normalise;
pub mod const_fold; pub mod const_fold;
pub mod compose;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
+148
View File
@@ -0,0 +1,148 @@
use crate::eval::Evaluator;
pub struct Composition<E1, E2> {
first: E1,
second: E2,
}
impl<E1, E2> Composition<E1, E2> {
pub fn new(first: E1, second: E2) -> Self {
Self { first, second }
}
}
impl<E1, E2, Expr, Error> Evaluator for Composition<E1, E2>
where
E1: Evaluator<Expr = Expr, Error = Error>,
E2: Evaluator<Expr = Expr, Error = Error>
{
type Expr = Expr;
type Error = Error;
fn eval(&mut self, input: Expr) -> Result<Expr, Error> {
self.second.eval(self.first.eval(input)?)
}
}
#[macro_export]
macro_rules! pipeline {
($last:expr) => {
$last
};
($first:expr, $($rest:expr),+) => {
$crate::eval::compose::Composition::new(
$first, $crate::pipeline!($($rest),+))
};
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
enum Expr {
Foo,
Bar,
Baz,
Quux,
}
#[derive(Debug, PartialEq)]
enum Err {
First,
Second,
Third,
}
struct First;
struct Second;
struct Third;
impl Evaluator for First {
type Expr = Expr;
type Error = Err;
fn eval(&mut self, input: Expr) -> Result<Expr, Err> {
match input {
Expr::Foo => Ok(Expr::Bar),
Expr::Baz => Ok(Expr::Foo),
Expr::Quux => Ok(Expr::Baz),
_ => Err(Err::First),
}
}
}
impl Evaluator for Second {
type Expr = Expr;
type Error = Err;
fn eval(&mut self, input: Expr) -> Result<Expr, Err> {
match input {
Expr::Bar => Ok(Expr::Baz),
Expr::Baz => Ok(Expr::Foo),
_ => Err(Err::Second),
}
}
}
impl Evaluator for Third {
type Expr = Expr;
type Error = Err;
fn eval(&mut self, input: Expr) -> Result<Expr, Err> {
match input {
Expr::Baz => Ok(Expr::Quux),
_ => Err(Err::Third),
}
}
}
#[test]
fn composition_chains_evaluators_in_order() {
let mut comp = Composition::new(First {}, Second {});
let result = comp.eval(Expr::Foo).unwrap();
assert_eq!(result, Expr::Baz);
}
#[test]
fn error_in_first_of_composition_is_returned() {
let mut comp = Composition::new(First {}, Second {});
let err = comp.eval(Expr::Bar).unwrap_err();
assert_eq!(err, Err::First);
}
#[test]
fn error_in_second_of_composition_is_returned() {
let mut comp = Composition::new(First {}, Second {});
let err = comp.eval(Expr::Baz).unwrap_err();
assert_eq!(err, Err::Second);
}
#[test]
fn pipeline_chains_evaluators_in_order() {
let mut pipe = pipeline!(First {}, Second {}, Third {});
let result = pipe.eval(Expr::Foo).unwrap();
assert_eq!(result, Expr::Quux);
}
#[test]
fn error_in_first_of_pipeline_is_returned() {
let mut pipe = pipeline!(First {}, Second {}, Third {});
let err = pipe.eval(Expr::Bar).unwrap_err();
assert_eq!(err, Err::First);
}
#[test]
fn error_in_second_of_pipeline_is_returned() {
let mut pipe = pipeline!(First {}, Second {}, Third {});
let err = pipe.eval(Expr::Baz).unwrap_err();
assert_eq!(err, Err::Second);
}
#[test]
fn error_in_third_of_pipeline_is_returned() {
let mut pipe = pipeline!(First {}, Second {}, Third {});
let err = pipe.eval(Expr::Quux).unwrap_err();
assert_eq!(err, Err::Third);
}
}