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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! Built-in functions to Monkey
use std::{fmt, rc::Rc};
use super::error;
use super::object;
/// Built-in function provided by Monkey.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Builtin {
/// Return the length of an iterable Monkey object.
Len,
/// Return the first element of a given array.
First,
/// Return the last element of a given array.
Last,
/// Return a new array containing all the elements of the array passed as
/// argument, except for the first one
Rest,
/// Allocates a new array with the same elements as the array passed as
/// argument with the addition of the new, pushed element.
Push,
/// Prints the given arguments to STDOUT
Puts,
}
impl fmt::Display for Builtin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Builtin::Len => write!(f, "len"),
Builtin::First => write!(f, "first"),
Builtin::Last => write!(f, "last"),
Builtin::Rest => write!(f, "rest"),
Builtin::Push => write!(f, "push"),
Builtin::Puts => write!(f, "puts"),
}
}
}
impl Builtin {
/// Lookup and retrieve a builtin function object by name/ identifier, if it
/// exists.
pub fn lookup(name: &str) -> Option<object::Object> {
match name {
"len" => Some(object::Object::Builtin(Builtin::Len)),
"first" => Some(object::Object::Builtin(Builtin::First)),
"last" => Some(object::Object::Builtin(Builtin::Last)),
"rest" => Some(object::Object::Builtin(Builtin::Rest)),
"push" => Some(object::Object::Builtin(Builtin::Push)),
"puts" => Some(object::Object::Builtin(Builtin::Puts)),
_ => None,
}
}
/// Apply the builtin function on the passed arguments slice.
pub fn apply(
&self,
args: &[Rc<object::Object>],
) -> Result<Rc<object::Object>, error::EvaluationError> {
match self {
Builtin::Len => {
check_args_count(1, args.len())?;
match &*args[0] {
object::Object::String(str) => {
Ok(Rc::new(object::Object::Integer(str.len() as i64)))
}
object::Object::Array(arr) => {
Ok(Rc::new(object::Object::Integer(arr.len() as i64)))
}
other => Err(error::EvaluationError::new(format!(
"argument to `len` not supported, got {}",
other
))),
}
}
Builtin::First => {
check_args_count(1, args.len())?;
match &*args[0] {
object::Object::Array(arr) => match arr.first() {
Some(element) => Ok(Rc::clone(element)),
None => Ok(Rc::new(object::Object::Null)),
},
other => Err(error::EvaluationError::new(format!(
"argument to `first` must be ARRAY, got {}",
other
))),
}
}
Builtin::Last => {
check_args_count(1, args.len())?;
match &*args[0] {
object::Object::Array(arr) => match arr.last() {
Some(element) => Ok(Rc::clone(element)),
None => Ok(Rc::new(object::Object::Null)),
},
other => Err(error::EvaluationError::new(format!(
"argument to `last` must be ARRAY, got {}",
other
))),
}
}
Builtin::Rest => {
check_args_count(1, args.len())?;
match &*args[0] {
object::Object::Array(arr) => {
let length = arr.len();
if length > 0 {
let new_elements = arr[1..].to_vec();
Ok(Rc::new(object::Object::Array(new_elements)))
} else {
Ok(Rc::new(object::Object::Null))
}
}
other => Err(error::EvaluationError::new(format!(
"argument to `rest` must be ARRAY, got {}",
other
))),
}
}
Builtin::Push => {
check_args_count(2, args.len())?;
match &*args[0] {
object::Object::Array(arr) => {
let mut new_elements = arr.clone();
new_elements.push(Rc::clone(&args[1]));
Ok(Rc::new(object::Object::Array(new_elements)))
}
other => Err(error::EvaluationError::new(format!(
"argument to `push` must be ARRAY, got {}",
other
))),
}
}
Builtin::Puts => {
args.iter().for_each(|obj| println!("{}", obj));
// Puts returns a null value
Ok(Rc::new(object::Object::Null))
}
}
}
}
/// Verify that the number of arguments passed matches expected count.
fn check_args_count(expected: usize, actual: usize) -> Result<(), error::EvaluationError> {
match expected == actual {
true => Ok(()),
false => Err(error::EvaluationError::new(format!(
"wrong number of arguments: expected={}, got={}",
expected, actual
))),
}
}