monkey_rs/eval/
builtin.rs

1//! Built-in functions to Monkey
2
3use std::{fmt, rc::Rc};
4
5use super::error;
6use super::object;
7
8/// Built-in function provided by Monkey.
9#[derive(Debug, PartialEq, Eq, Clone)]
10pub enum Builtin {
11    /// Return the length of an iterable Monkey object.
12    Len,
13    /// Return the first element of a given array.
14    First,
15    /// Return the last element of a given array.
16    Last,
17    /// Return a new array containing all the elements of the array passed as
18    /// argument, except for the first one
19    Rest,
20    /// Allocates a new array with the same elements as the array passed as
21    /// argument with the addition of the new, pushed element.
22    Push,
23    /// Prints the given arguments to STDOUT
24    Puts,
25}
26
27impl fmt::Display for Builtin {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Builtin::Len => write!(f, "len"),
31            Builtin::First => write!(f, "first"),
32            Builtin::Last => write!(f, "last"),
33            Builtin::Rest => write!(f, "rest"),
34            Builtin::Push => write!(f, "push"),
35            Builtin::Puts => write!(f, "puts"),
36        }
37    }
38}
39
40impl Builtin {
41    /// Lookup and retrieve a builtin function object by name/ identifier, if it
42    /// exists.
43    pub fn lookup(name: &str) -> Option<object::Object> {
44        match name {
45            "len" => Some(object::Object::Builtin(Builtin::Len)),
46            "first" => Some(object::Object::Builtin(Builtin::First)),
47            "last" => Some(object::Object::Builtin(Builtin::Last)),
48            "rest" => Some(object::Object::Builtin(Builtin::Rest)),
49            "push" => Some(object::Object::Builtin(Builtin::Push)),
50            "puts" => Some(object::Object::Builtin(Builtin::Puts)),
51            _ => None,
52        }
53    }
54
55    /// Apply the builtin function on the passed arguments slice.
56    pub fn apply(
57        &self,
58        args: &[Rc<object::Object>],
59    ) -> Result<Rc<object::Object>, error::EvaluationError> {
60        match self {
61            Builtin::Len => {
62                check_args_count(1, args.len())?;
63
64                match &*args[0] {
65                    object::Object::String(str) => {
66                        Ok(Rc::new(object::Object::Integer(str.len() as i64)))
67                    }
68                    object::Object::Array(arr) => {
69                        Ok(Rc::new(object::Object::Integer(arr.len() as i64)))
70                    }
71                    other => Err(error::EvaluationError::new(format!(
72                        "argument to `len` not supported, got {}",
73                        other
74                    ))),
75                }
76            }
77            Builtin::First => {
78                check_args_count(1, args.len())?;
79
80                match &*args[0] {
81                    object::Object::Array(arr) => match arr.first() {
82                        Some(element) => Ok(Rc::clone(element)),
83                        None => Ok(Rc::new(object::Object::Null)),
84                    },
85                    other => Err(error::EvaluationError::new(format!(
86                        "argument to `first` must be ARRAY, got {}",
87                        other
88                    ))),
89                }
90            }
91            Builtin::Last => {
92                check_args_count(1, args.len())?;
93
94                match &*args[0] {
95                    object::Object::Array(arr) => match arr.last() {
96                        Some(element) => Ok(Rc::clone(element)),
97                        None => Ok(Rc::new(object::Object::Null)),
98                    },
99                    other => Err(error::EvaluationError::new(format!(
100                        "argument to `last` must be ARRAY, got {}",
101                        other
102                    ))),
103                }
104            }
105            Builtin::Rest => {
106                check_args_count(1, args.len())?;
107
108                match &*args[0] {
109                    object::Object::Array(arr) => {
110                        let length = arr.len();
111                        if length > 0 {
112                            let new_elements = arr[1..].to_vec();
113                            Ok(Rc::new(object::Object::Array(new_elements)))
114                        } else {
115                            Ok(Rc::new(object::Object::Null))
116                        }
117                    }
118                    other => Err(error::EvaluationError::new(format!(
119                        "argument to `rest` must be ARRAY, got {}",
120                        other
121                    ))),
122                }
123            }
124            Builtin::Push => {
125                check_args_count(2, args.len())?;
126
127                match &*args[0] {
128                    object::Object::Array(arr) => {
129                        let mut new_elements = arr.clone();
130                        new_elements.push(Rc::clone(&args[1]));
131                        Ok(Rc::new(object::Object::Array(new_elements)))
132                    }
133                    other => Err(error::EvaluationError::new(format!(
134                        "argument to `push` must be ARRAY, got {}",
135                        other
136                    ))),
137                }
138            }
139            Builtin::Puts => {
140                args.iter().for_each(|obj| println!("{}", obj));
141
142                // Puts returns a null value
143                Ok(Rc::new(object::Object::Null))
144            }
145        }
146    }
147}
148
149/// Verify that the number of arguments passed matches expected count.
150fn check_args_count(expected: usize, actual: usize) -> Result<(), error::EvaluationError> {
151    match expected == actual {
152        true => Ok(()),
153        false => Err(error::EvaluationError::new(format!(
154            "wrong number of arguments: expected={}, got={}",
155            expected, actual
156        ))),
157    }
158}