Implement evaluator composition
This commit is contained in:
1 parent
48f58d87fc
commit
cdaf2e7013
2 files changed
+149
No files matched your search
@@ -1,5 +1,6 @@
|
||||
pub mod normalise;
|
||||
pub mod const_fold;
|
||||
pub mod compose;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user