Last active
January 25, 2020 18:58
-
-
Save davidad/402c0e09a723e0f53e94da69ff83ca15 to your computer and use it in GitHub Desktop.
"Closed" AST implementation vs "extensible" implementation using dynamic dispatch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(test)] | |
mod expr_adt { | |
pub enum Expr { | |
Var(String), | |
Const(f64), | |
Plus(Box<Expr>, Box<Expr>), | |
Times(Box<Expr>, Box<Expr>), | |
} | |
pub fn gen_evaluator<'a, F : 'a + Fn(&str) -> f64> (env: F) -> Box<dyn 'a + Fn(&Expr) -> f64> { | |
fn rec<F : Fn(&str) -> f64>(env: &F, e: &Expr) -> f64 { | |
match e { | |
Expr::Var(v) => env(v), | |
Expr::Const(x) => *x, | |
Expr::Plus(e1, e2) => rec(env,e1) + rec(env,e2), | |
Expr::Times(e1, e2) => rec(env,e1) * rec(env,e2), | |
} | |
} | |
Box::new(move |e| rec(&env,e)) | |
} | |
pub fn x_evaluator(x: f64) -> Box<dyn Fn(&Expr) -> f64> { | |
gen_evaluator(move |v: &str| match v { "x" => x, _ => panic!("No such variable {}", v) }) | |
} | |
pub use self::Expr::*; | |
} | |
mod expr_trait { | |
pub trait Expr { | |
fn eval(&self, env: &dyn Fn(&str) -> f64) -> f64; | |
} | |
pub struct Var(pub String); | |
pub struct Const(pub f64); | |
pub struct Plus(pub Box<dyn Expr>, pub Box<dyn Expr>); | |
pub struct Times(pub Box<dyn Expr>, pub Box<dyn Expr>); | |
impl Expr for Var { | |
fn eval(&self, env: &dyn Fn(&str) -> f64) -> f64 { | |
match self { Var(v) => env(v) } | |
} | |
} | |
impl Expr for Const { | |
fn eval(&self, _: &dyn Fn(&str) -> f64) -> f64 { | |
match self { Const(c) => *c } | |
} | |
} | |
impl Expr for Plus { | |
fn eval(&self, env: &dyn Fn(&str) -> f64) -> f64 { | |
match self { Plus(e1, e2) => e1.eval(env) + e2.eval(env) } | |
} | |
} | |
impl Expr for Times { | |
fn eval(&self, env: &dyn Fn(&str) -> f64) -> f64 { | |
match self { Times(e1, e2) => e1.eval(env) * e2.eval(env) } | |
} | |
} | |
pub fn gen_evaluator<'a, F : 'a + Fn(&str) -> f64> (env: F) -> Box<dyn 'a + Fn(&dyn Expr) -> f64> { | |
Box::new(move |e| e.eval(&env)) | |
} | |
pub fn x_evaluator(x: f64) -> Box<dyn Fn(&dyn Expr) -> f64> { | |
gen_evaluator(move |v: &str| match v { "x" => x, _ => panic!("No such variable {}", v) }) | |
} | |
} | |
pub fn main() { | |
let example_expr_adt = expr_adt::Plus(Box::new(expr_adt::Times(Box::new(expr_adt::Times(Box::new(expr_adt::Const(2.0)),Box::new(expr_adt::Var("x".to_string())))),Box::new(expr_adt::Var("x".to_string())))),Box::new(expr_adt::Const(4.0))); | |
let x_evaluator_2_adt = expr_adt::x_evaluator(2.0); | |
println!("{}", x_evaluator_2_adt(&example_expr_adt)); | |
let example_expr_trait = expr_trait::Plus(Box::new(expr_trait::Times(Box::new(expr_trait::Times(Box::new(expr_trait::Const(2.0)),Box::new(expr_trait::Var("x".to_string())))),Box::new(expr_trait::Var("x".to_string())))),Box::new(expr_trait::Const(4.0))); | |
let x_evaluator_2_trait = expr_trait::x_evaluator(2.0); | |
println!("{}", x_evaluator_2_trait(&example_expr_trait)); | |
struct Div(Box<dyn expr_trait::Expr>, Box<dyn expr_trait::Expr>); | |
impl expr_trait::Expr for Div { | |
fn eval(&self, env: &dyn Fn(&str) -> f64) -> f64 { | |
match self { Div(e1, e2) => e1.eval(env) / e2.eval(env) } | |
} | |
} | |
let div_example = expr_trait::Plus(Box::new(expr_trait::Const(1.0)), Box::new(Div(Box::new(expr_trait::Plus(Box::new(expr_trait::Times(Box::new(expr_trait::Times(Box::new(expr_trait::Const(2.0)),Box::new(expr_trait::Var("x".to_string())))),Box::new(expr_trait::Var("x".to_string())))),Box::new(expr_trait::Const(4.0)))),Box::new(expr_trait::Const(3.0))))); | |
println!("{}", x_evaluator_2_trait(&div_example)); | |
} | |
extern crate test; | |
#[cfg(test)] | |
mod tests { | |
use test::Bencher; | |
use super::*; | |
#[bench] | |
fn bench_adt_eval(b: &mut Bencher) { | |
let example_expr_adt = expr_adt::Plus(Box::new(expr_adt::Times(Box::new(expr_adt::Times(Box::new(expr_adt::Const(2.0)),Box::new(expr_adt::Var("x".to_string())))),Box::new(expr_adt::Var("x".to_string())))),Box::new(expr_adt::Const(4.0))); | |
let x_evaluator_2_adt = expr_adt::x_evaluator(2.0); | |
b.iter(|| { | |
let adt = test::black_box(&example_expr_adt); | |
x_evaluator_2_adt(adt) | |
}); | |
} | |
#[bench] | |
fn bench_trait_eval(b: &mut Bencher) { | |
let example_expr_trait = expr_trait::Plus(Box::new(expr_trait::Times(Box::new(expr_trait::Times(Box::new(expr_trait::Const(2.0)),Box::new(expr_trait::Var("x".to_string())))),Box::new(expr_trait::Var("x".to_string())))),Box::new(expr_trait::Const(4.0))); | |
let x_evaluator_2_trait = expr_trait::x_evaluator(2.0); | |
b.iter(|| { | |
let adt = test::black_box(&example_expr_trait); | |
x_evaluator_2_trait(adt) | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "AST_eval" | |
authors = ["davidad"] | |
edition = "2018" | |
version = "0.0.2" | |
[[bin]] | |
name = "AST_eval" | |
path = "AST_eval.rs" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment