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 normalise;
|
||||||
pub mod const_fold;
|
pub mod const_fold;
|
||||||
|
pub mod compose;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
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