mdl_monkey/
object.rs

1//! Objects produced when evaluating the Monkey programming language from
2//! <https://interpreterbook.com/>.
3
4use crate::ast;
5
6use std::collections::HashMap;
7use std::error;
8use std::fmt;
9use std::result;
10
11/// Objects produced when evaluating Monkey source code, along with their
12/// associated data if applicable.
13#[derive(Clone, Debug, PartialEq)]
14pub enum Object {
15    Null,
16    Integer(i64),
17    Float(f64),
18    Boolean(bool),
19    String(String),
20    ReturnValue(Box<Object>),
21    Function(Function),
22    Builtin(Builtin),
23    Array(Array),
24    Hash(Hash),
25}
26
27impl fmt::Display for Object {
28    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29        match self {
30            Object::Null => write!(f, "null"),
31            Object::Integer(i) => i.fmt(f),
32            Object::Float(fl) => fl.fmt(f),
33            Object::Boolean(b) => b.fmt(f),
34            Object::String(s) => s.fmt(f),
35            Object::ReturnValue(r) => write!(f, "return({})", r),
36            Object::Function(func) => func.fmt(f),
37            Object::Builtin(b) => b.fmt(f),
38            Object::Array(a) => a.fmt(f),
39            Object::Hash(h) => h.fmt(f),
40        }
41    }
42}
43
44/// An execution environment used when evaluating Monkey source code.
45#[derive(Clone, Debug, Default, PartialEq)]
46pub struct Environment {
47    store: HashMap<String, Object>,
48    outer: Option<Box<Environment>>,
49}
50
51impl Environment {
52    /// Creates a new `Environment`.
53    pub fn new() -> Self {
54        Environment {
55            store: HashMap::new(),
56            outer: None,
57        }
58    }
59
60    /// Creates an enclosed `Environment` for use within a function call.
61    pub fn new_enclosed(outer: Self) -> Self {
62        let mut env = Self::new();
63        env.outer = Some(Box::new(outer));
64        env
65    }
66
67    /// Retrieves the object associated with an identifier name, or returns
68    /// `None` if no object is associated with `name`.
69    pub fn get(&self, name: &str) -> Option<&Object> {
70        match (self.store.get(name), &self.outer) {
71            // We found a binding in this environment; no need to consult the
72            // outer environment.
73            (Some(obj), _) => Some(obj),
74            // We did not find a binding; try the outer environment.
75            (None, Some(outer)) => outer.get(name),
76            // We found no binding and there is no outer environment.
77            (None, _) => None,
78        }
79    }
80
81    /// Binds an object in the environment with the identifier `name`.
82    pub fn set(&mut self, name: String, obj: &Object) -> Object {
83        self.store.insert(name, obj.clone());
84        obj.clone()
85    }
86}
87
88/// The object representation of a Monkey function.
89#[derive(Clone, Debug, PartialEq)]
90pub struct Function {
91    pub parameters: Vec<String>,
92    pub body: ast::BlockStatement,
93    pub env: Environment,
94}
95
96impl fmt::Display for Function {
97    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98        let params = self.parameters.join(", ");
99
100        write!(f, "fn({}) {{\n{}\n}}", params, self.body)
101    }
102}
103
104/// The object representation of a built-in Monkey function.
105#[derive(Clone, Debug, PartialEq)]
106pub enum Builtin {
107    First,
108    Last,
109    Len,
110    Push,
111    Puts,
112    Rest,
113}
114
115impl Builtin {
116    /// Constructs a built-in using its name.
117    pub fn lookup(name: &str) -> Option<Self> {
118        match name {
119            "first" => Some(Builtin::First),
120            "last" => Some(Builtin::Last),
121            "len" => Some(Builtin::Len),
122            "push" => Some(Builtin::Push),
123            "puts" => Some(Builtin::Puts),
124            "rest" => Some(Builtin::Rest),
125
126            _ => None,
127        }
128    }
129
130    /// Applies the appropriate built-in function on `args` to produce an
131    /// `Object`.
132    pub fn apply(&self, args: &[Object]) -> Result<Object> {
133        match self {
134            Builtin::First => builtin_first(&args),
135            Builtin::Last => builtin_last(&args),
136            Builtin::Len => builtin_len(&args),
137            Builtin::Push => builtin_push(&args),
138            Builtin::Puts => builtin_puts(&args),
139            Builtin::Rest => builtin_rest(&args),
140        }
141    }
142}
143
144impl fmt::Display for Builtin {
145    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146        match self {
147            Builtin::First => write!(f, "first"),
148            Builtin::Last => write!(f, "last"),
149            Builtin::Len => write!(f, "len"),
150            Builtin::Push => write!(f, "push"),
151            Builtin::Puts => write!(f, "puts"),
152            Builtin::Rest => write!(f, "rest"),
153        }
154    }
155}
156
157fn builtin_first(args: &[Object]) -> Result<Object> {
158    if args.len() != 1 {
159        return Err(Error::Builtin(
160            Builtin::First,
161            format!("expected 1 argument, but got {}", args.len()),
162        ));
163    }
164
165    if let Object::Array(a) = &args[0] {
166        if !a.elements.is_empty() {
167            Ok(a.elements.first().unwrap().clone())
168        } else {
169            Ok(Object::Null)
170        }
171    } else {
172        Err(Error::Builtin(
173            Builtin::First,
174            format!("argument {} is not an array", &args[0]),
175        ))
176    }
177}
178
179fn builtin_last(args: &[Object]) -> Result<Object> {
180    if args.len() != 1 {
181        return Err(Error::Builtin(
182            Builtin::Last,
183            format!("expected 1 argument, but got {}", args.len()),
184        ));
185    }
186
187    if let Object::Array(a) = &args[0] {
188        if !a.elements.is_empty() {
189            Ok(a.elements.last().unwrap().clone())
190        } else {
191            Ok(Object::Null)
192        }
193    } else {
194        Err(Error::Builtin(
195            Builtin::Last,
196            format!("argument {} is not an array", &args[0]),
197        ))
198    }
199}
200
201fn builtin_len(args: &[Object]) -> Result<Object> {
202    if args.len() != 1 {
203        return Err(Error::Builtin(
204            Builtin::Len,
205            format!("expected 1 argument, but got {}", args.len()),
206        ));
207    }
208
209    match &args[0] {
210        Object::Array(a) => Ok(Object::Integer(a.elements.len() as i64)),
211        Object::String(s) => Ok(Object::Integer(s.len() as i64)),
212        _ => Err(Error::Builtin(
213            Builtin::Len,
214            format!("argument {} cannot be used", &args[0]),
215        )),
216    }
217}
218
219fn builtin_push(args: &[Object]) -> Result<Object> {
220    if args.len() != 2 {
221        return Err(Error::Builtin(
222            Builtin::Push,
223            format!("expected 2 argument, but got {}", args.len()),
224        ));
225    }
226
227    let arr = if let Object::Array(a) = &args[0] {
228        a
229    } else {
230        return Err(Error::Builtin(
231            Builtin::Push,
232            format!("first argument {} is not an array", &args[0]),
233        ));
234    };
235
236    let mut out = arr.elements.clone();
237    out.push(args[1].clone());
238
239    Ok(Object::Array(Array { elements: out }))
240}
241
242fn builtin_puts(args: &[Object]) -> Result<Object> {
243    for a in args {
244        println!("{}", a);
245    }
246    Ok(Object::Null)
247}
248
249fn builtin_rest(args: &[Object]) -> Result<Object> {
250    if args.len() != 1 {
251        return Err(Error::Builtin(
252            Builtin::Rest,
253            format!("expected 1 argument, but got {}", args.len()),
254        ));
255    }
256
257    if let Object::Array(a) = &args[0] {
258        if !a.elements.is_empty() {
259            Ok(Object::Array(Array {
260                elements: a.elements.iter().skip(1).cloned().collect(),
261            }))
262        } else {
263            Ok(Object::Null)
264        }
265    } else {
266        Err(Error::Builtin(
267            Builtin::Rest,
268            format!("argument {} is not an array", &args[0]),
269        ))
270    }
271}
272
273/// The object representation of a Monkey array.
274#[derive(Clone, Debug, PartialEq)]
275pub struct Array {
276    pub elements: Vec<Object>,
277}
278
279impl fmt::Display for Array {
280    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
281        write!(
282            f,
283            "[{}]",
284            self.elements
285                .iter()
286                .map(|e| format!("{}", e))
287                .collect::<Vec<String>>()
288                .join(", ")
289        )
290    }
291}
292
293/// Objects which may be used as `Hash` keys.
294#[derive(Clone, Debug, Eq, Hash, PartialEq)]
295pub enum Hashable {
296    Integer(i64),
297    Boolean(bool),
298    String(String),
299}
300
301impl fmt::Display for Hashable {
302    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303        match self {
304            Hashable::Integer(i) => i.fmt(f),
305            Hashable::Boolean(b) => b.fmt(f),
306            Hashable::String(s) => s.fmt(f),
307        }
308    }
309}
310
311/// The object representation of a Monkey hash.
312#[derive(Clone, Debug, PartialEq)]
313pub struct Hash {
314    pub pairs: HashMap<Hashable, Object>,
315}
316
317impl fmt::Display for Hash {
318    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
319        let mut pairs = vec![];
320        for pair in &self.pairs {
321            pairs.push(format!(r#"{}: {}"#, pair.0, pair.1));
322        }
323
324        // Sort for deterministic output.
325        pairs.sort();
326        write!(f, "{{{}}}", pairs.join(", "))
327    }
328}
329
330/// A Result type specialized use with for an Error.
331pub type Result<T> = result::Result<T, Error>;
332
333/// Specifies the different classes of errors which may occur.
334#[derive(Debug, PartialEq)]
335pub enum Error {
336    Builtin(Builtin, String),
337}
338
339impl fmt::Display for Error {
340    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
341        match self {
342            Error::Builtin(b, err) => write!(f, "built-in {}: {}", b, err),
343        }
344    }
345}
346
347impl error::Error for Error {
348    fn cause(&self) -> Option<&dyn error::Error> {
349        None
350    }
351}