string_template_plus/
lisp.rs

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