1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
use anyhow::Context;
use rust_lisp::default_env;
use rust_lisp::interpreter::eval_block;
use rust_lisp::model::{FloatType, RuntimeError, Symbol, Value};
use rust_lisp::parser::{parse, ParseError};
use std::num::ParseFloatError;
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
rc::Rc,
};
/// Evaluate the lisp expression
///
///
/// ```rust
/// # use std::error::Error;
/// # use string_template_plus::lisp::*;
/// # use std::collections::HashMap;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let mut vars: HashMap<String, String> = HashMap::new();
/// vars.insert("test".into(), "1".into());
/// assert_eq!(calculate(&vars, "(+ 1 1)")?, "2");
/// assert_eq!(calculate(&vars, "(st+var 'test)")?, "\"1\"");
/// assert_eq!(calculate(&vars, "(/ 2 (st+num 'test))")?, "2");
/// assert_eq!(calculate(&vars, "(st+has 'test)")?, "T");
/// # Ok(())
/// # }
pub fn calculate(variables: &HashMap<String, String>, expr: &str) -> anyhow::Result<String> {
let expr = parse(expr)
.collect::<Result<Vec<Value>, ParseError>>()
.ok()
.context("Parse Failed")?;
let env = Rc::new(RefCell::new(default_env()));
// can't figure out how to remove this unnecessary clone
let vars1 = variables.clone();
env.borrow_mut().define(
Symbol::from("st+var"),
Value::NativeClosure(Rc::new(RefCell::new(move |_, args: Vec<Value>| {
if args.len() == 1 {
let val = vars1.get(&args[0].to_string()).unwrap().to_string();
return Ok(Value::String(val));
}
return Ok(Value::NIL);
}))),
);
let vars2 = variables.clone();
env.borrow_mut().define(
Symbol::from("st+num"),
Value::NativeClosure(Rc::new(RefCell::new(move |_, args: Vec<Value>| {
if args.len() == 1 {
let val: FloatType = vars2
.get(&args[0].to_string())
.unwrap()
.parse()
.map_err(|e: ParseFloatError| RuntimeError { msg: e.to_string() })?;
return Ok(Value::Float(val));
}
return Ok(Value::NIL);
}))),
);
let vars3: HashSet<String> = variables.iter().map(|(k, _)| k.to_string()).collect();
env.borrow_mut().define(
Symbol::from("st+has"),
Value::NativeClosure(Rc::new(RefCell::new(move |_, args: Vec<Value>| {
if args.len() == 1 {
let has: bool = vars3.get(&args[0].to_string()).is_some();
return Ok(if has { Value::True } else { Value::False });
}
return Ok(Value::NIL);
}))),
);
// can't define functions it seems, hence the redefinition above
// env.borrow_mut().define(
// Symbol::from("stp-num"),
// lisp! {
// (lambda (x) ({ Value::Symbol("string-to-number".into())}
// ({Value::Symbol("stp-var".into())} x)))
// },
// );
let res = eval_block(env.clone(), expr.into_iter())?;
Ok(res.to_string())
}