rust_lisp/
utils.rs

1use std::{any::Any, rc::Rc};
2
3use crate::model::{FloatType, HashMapRc, IntType, List, RuntimeError, Symbol, Value};
4
5/// Given a `Value` assumed to be a `Value::List()`, grab the item at `index`
6/// and err if there isn't one.
7pub fn require_arg<'a>(
8    func_or_form_name: &str,
9    args: &'a [Value],
10    index: usize,
11) -> Result<&'a Value, RuntimeError> {
12    args.get(index).ok_or_else(|| RuntimeError {
13        msg: format!(
14            "\"{}\" requires an argument {}",
15            func_or_form_name,
16            index + 1
17        ),
18    })
19}
20
21/// Given a `Value` assumed to be a `Value::List()`, and some type T, grab the
22/// item at `index` in the list and try converting it to type T. RuntimeError if
23/// the argument doesn't exist, or if it is the wrong type.
24pub fn require_typed_arg<'a, T>(
25    func_or_form_name: &str,
26    args: &'a [Value],
27    index: usize,
28) -> Result<T, RuntimeError>
29where
30    T: TryFrom<&'a Value> + TypeName,
31{
32    require_arg(func_or_form_name, args, index)?
33        .try_into()
34        .map_err(|_| RuntimeError {
35            msg: format!(
36                "\"{}\" requires argument {} to be a {}; got {}",
37                func_or_form_name,
38                index + 1,
39                T::get_name(),
40                args.get(index).unwrap_or(&Value::NIL)
41            ),
42        })
43}
44
45pub trait TypeName {
46    fn get_name() -> &'static str;
47}
48
49impl TypeName for IntType {
50    fn get_name() -> &'static str {
51        "int"
52    }
53}
54
55impl TypeName for FloatType {
56    fn get_name() -> &'static str {
57        "float"
58    }
59}
60
61impl TypeName for &String {
62    fn get_name() -> &'static str {
63        "string"
64    }
65}
66
67impl TypeName for &Symbol {
68    fn get_name() -> &'static str {
69        "symbol"
70    }
71}
72
73impl TypeName for &List {
74    fn get_name() -> &'static str {
75        "list"
76    }
77}
78
79impl TypeName for &HashMapRc {
80    fn get_name() -> &'static str {
81        "hash map"
82    }
83}
84
85impl TypeName for &Rc<dyn Any> {
86    fn get_name() -> &'static str {
87        "foreign value"
88    }
89}