Skip to content

Instantly share code, notes, and snippets.

@Pagliacii
Last active October 24, 2021 03:41
Show Gist options
  • Save Pagliacii/79d299d9adde88b33d5a94bb309d272b to your computer and use it in GitHub Desktop.
Save Pagliacii/79d299d9adde88b33d5a94bb309d272b to your computer and use it in GitHub Desktop.
Performing procedures dynamically
use std::collections::HashMap;
use std::fmt;
use std::ops::Add;
use std::sync::Arc;
struct Procedure {
name: String,
inner_proc: Arc<dyn Fn(Vec<Value>) -> Value>,
}
impl Procedure {
fn new<F, S, R>(name: S, f: F) -> Self
where
F: 'static + Fn(Vec<Value>) -> R,
S: Into<String>,
R: ToValue,
{
Self {
name: name.into(),
inner_proc: Arc::new(move |args: Vec<Value>| f(args).to_value()),
}
}
fn execute(&self, args: Vec<Value>) -> Value {
(self.inner_proc)(args)
}
}
impl Clone for Procedure {
fn clone(&self) -> Self {
Self {
name: self.name.to_string(),
inner_proc: self.inner_proc.clone(),
}
}
}
impl fmt::Debug for Procedure {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<Procedure {}>", self.name)
}
}
#[derive(Clone, Debug)]
enum Value {
Num(f64),
Procedure(Procedure),
Null,
}
impl Value {
fn new<T: ToValue>(item: T) -> Self {
item.to_value()
}
fn perform(&self, args: Vec<Value>) -> Value {
if let Self::Procedure(func) = self {
func.execute(args)
} else {
panic!("Expected a procedure to be performed, got {}", self)
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Num(v) => write!(f, "{}", v),
_ => write!(f, "()"),
}
}
}
impl Add for Value {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
match (self, other) {
(Value::Num(l), Value::Num(r)) => Value::Num(l + r),
(left, right) => panic!("Unable to add {} to {}", left, right),
}
}
}
trait ToValue {
fn to_value(self) -> Value;
}
impl ToValue for Value {
fn to_value(self) -> Value {
self
}
}
impl ToValue for f64 {
fn to_value(self) -> Value {
Value::Num(self)
}
}
impl ToValue for () {
fn to_value(self) -> Value {
Value::Null
}
}
impl ToValue for Procedure {
fn to_value(self) -> Value {
Value::Procedure(self)
}
}
fn add_value(args: Vec<Value>) -> Value {
if args.is_empty() {
Value::Num(0.0)
} else {
args[0].clone() + add_value(args[1..].to_vec())
}
}
fn main() {
let mut operations: HashMap<String, Value> = HashMap::new();
operations.insert("test".into(), Procedure::new("test", |_: Vec<Value>| {}).to_value());
operations.insert("add".into(), Procedure::new("add", add_value).to_value());
let test = operations.get("test").unwrap();
println!("{}", test.perform(vec![]));
let add = operations.get("add").unwrap();
println!("{}", add.perform(vec![Value::Num(1.0), Value::Num(2.0), Value::Num(3.0)]));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment