rust_lisp/
interpreter.rs

1use crate::{
2    model::{Env, Lambda, List, RuntimeError, Symbol, Value},
3    utils::{require_arg, require_typed_arg},
4};
5use std::{cell::RefCell, rc::Rc};
6
7/// Evaluate a single Lisp expression in the context of a given environment.
8pub fn eval(env: Rc<RefCell<Env>>, expression: &Value) -> Result<Value, RuntimeError> {
9    eval_inner(env, expression, Context::new())
10}
11
12/// Evaluate a series of s-expressions. Each expression is evaluated in
13/// order and the final one's return value is returned.
14pub fn eval_block(
15    env: Rc<RefCell<Env>>,
16    clauses: impl Iterator<Item = Value>,
17) -> Result<Value, RuntimeError> {
18    eval_block_inner(env, clauses, Context::new())
19}
20
21fn eval_block_inner(
22    env: Rc<RefCell<Env>>,
23    clauses: impl Iterator<Item = Value>,
24    context: Context,
25) -> Result<Value, RuntimeError> {
26    let mut current_expr: Option<Value> = None;
27
28    for clause in clauses {
29        if let Some(expr) = current_expr {
30            match eval_inner(env.clone(), &expr, context.found_tail(true)) {
31                Ok(_) => (),
32                Err(e) => {
33                    return Err(e);
34                }
35            }
36        }
37
38        current_expr = Some(clause);
39    }
40
41    if let Some(expr) = &current_expr {
42        eval_inner(env, expr, context)
43    } else {
44        Err(RuntimeError {
45            msg: "Unrecognized expression".to_owned(),
46        })
47    }
48}
49
50/// `found_tail` and `in_func` are used when locating the tail position for
51/// tail-call optimization. Candidates are not eligible if a) we aren't already
52/// inside a function call, or b) we've already found the tail inside the current
53/// function call. `found_tail` is currently overloaded inside special forms to
54/// factor out function calls in, say, the conditional slot, which are not
55/// eligible to be the tail-call based on their position. A future refactor hopes
56/// to make things a little more semantic.
57fn eval_inner(
58    env: Rc<RefCell<Env>>,
59    expression: &Value,
60    context: Context,
61) -> Result<Value, RuntimeError> {
62    if context.quoting {
63        match expression {
64            Value::List(list) if *list != List::NIL => match &list.car()? {
65                Value::Symbol(Symbol(keyword)) if keyword == "comma" => {
66                    // do nothing, handle it down below
67                }
68                _ => {
69                    return list
70                        .into_iter()
71                        .map(|el| eval_inner(env.clone(), &el, context))
72                        .collect::<Result<List, RuntimeError>>()
73                        .map(Value::List);
74                }
75            },
76            _ => return Ok(expression.clone()),
77        }
78    }
79
80    match expression {
81        // look up symbol
82        Value::Symbol(symbol) => env.borrow().get(symbol).ok_or_else(|| RuntimeError {
83            msg: format!("\"{}\" is not defined", symbol),
84        }),
85
86        // s-expression
87        Value::List(list) if *list != List::NIL => {
88            match &list.car()? {
89                // special forms
90                Value::Symbol(Symbol(keyword)) if keyword == "comma" => {
91                    eval_inner(env, &list.cdr().car()?, context.quoting(false))
92                }
93
94                Value::Symbol(Symbol(keyword)) if keyword == "quote" => {
95                    eval_inner(env, &list.cdr().car()?, context.quoting(true))
96                }
97
98                Value::Symbol(Symbol(keyword)) if keyword == "define" || keyword == "set" => {
99                    let args = &list.cdr().into_iter().collect::<Vec<Value>>();
100
101                    let symbol = require_typed_arg::<&Symbol>(keyword, args, 0)?;
102                    let value_expr = require_arg(keyword, args, 1)?;
103
104                    let value = eval_inner(env.clone(), value_expr, context.found_tail(true))?;
105
106                    if keyword == "define" {
107                        env.borrow_mut().define(symbol.clone(), value.clone());
108                    } else {
109                        env.borrow_mut().set(symbol.clone(), value.clone())?;
110                    }
111
112                    Ok(value)
113                }
114
115                Value::Symbol(Symbol(keyword)) if keyword == "defmacro" => {
116                    let args = &list.cdr().into_iter().collect::<Vec<Value>>();
117
118                    let symbol = require_typed_arg::<&Symbol>(keyword, args, 0)?;
119                    let argnames_list = require_typed_arg::<&List>(keyword, args, 1)?;
120                    let argnames = value_to_argnames(argnames_list.clone())?;
121                    let body = Rc::new(Value::List(list.cdr().cdr().cdr()));
122
123                    let lambda = Value::Macro(Lambda {
124                        closure: env.clone(),
125                        argnames,
126                        body,
127                    });
128
129                    env.borrow_mut().define(symbol.clone(), lambda);
130
131                    Ok(Value::NIL)
132                }
133
134                Value::Symbol(Symbol(keyword)) if keyword == "defun" => {
135                    let args = &list.cdr().into_iter().collect::<Vec<Value>>();
136
137                    let symbol = require_typed_arg::<&Symbol>(keyword, args, 0)?;
138                    let argnames_list = require_typed_arg::<&List>(keyword, args, 1)?;
139                    let argnames = value_to_argnames(argnames_list.clone())?;
140                    let body = Rc::new(Value::List(list.cdr().cdr().cdr()));
141
142                    let lambda = Value::Lambda(Lambda {
143                        closure: env.clone(),
144                        argnames,
145                        body,
146                    });
147
148                    env.borrow_mut().define(symbol.clone(), lambda);
149
150                    Ok(Value::NIL)
151                }
152
153                Value::Symbol(Symbol(keyword)) if keyword == "lambda" => {
154                    let args = &list.cdr().into_iter().collect::<Vec<Value>>();
155
156                    let argnames_list = require_typed_arg::<&List>(keyword, args, 0)?;
157                    let argnames = value_to_argnames(argnames_list.clone())?;
158                    let body = Rc::new(Value::List(list.cdr().cdr()));
159
160                    Ok(Value::Lambda(Lambda {
161                        closure: env,
162                        argnames,
163                        body,
164                    }))
165                }
166
167                Value::Symbol(Symbol(keyword)) if keyword == "let" => {
168                    let let_env = Rc::new(RefCell::new(Env::extend(env)));
169
170                    let args = &list.cdr().into_iter().collect::<Vec<Value>>();
171
172                    let declarations = require_typed_arg::<&List>(keyword, args, 0)?;
173
174                    for decl in declarations.into_iter() {
175                        let decl = &decl;
176
177                        let decl_cons: &List = decl.try_into().map_err(|_| RuntimeError {
178                            msg: format!("Expected declaration clause, found {}", decl),
179                        })?;
180                        let symbol = &decl_cons.car()?;
181                        let symbol: &Symbol = symbol.try_into().map_err(|_| RuntimeError {
182                            msg: format!("Expected symbol for let declaration, found {}", symbol),
183                        })?;
184                        let expr = &decl_cons.cdr().car()?;
185
186                        let result = eval_inner(let_env.clone(), expr, context.found_tail(true))?;
187                        let_env.borrow_mut().define(symbol.clone(), result);
188                    }
189
190                    let body = &Value::List(list.cdr().cdr());
191                    let body: &List = body.try_into().map_err(|_| RuntimeError {
192                        msg: format!(
193                            "Expected expression(s) after let-declarations, found {}",
194                            body
195                        ),
196                    })?;
197
198                    eval_block_inner(let_env, body.into_iter(), context)
199                }
200
201                Value::Symbol(Symbol(keyword)) if keyword == "begin" => {
202                    eval_block_inner(env, list.cdr().into_iter(), context)
203                }
204
205                Value::Symbol(Symbol(keyword)) if keyword == "cond" => {
206                    let clauses = list.cdr();
207
208                    for clause in clauses.into_iter() {
209                        let clause = &clause;
210
211                        let clause: &List = clause.try_into().map_err(|_| RuntimeError {
212                            msg: format!("Expected conditional clause, found {}", clause),
213                        })?;
214
215                        let condition = &clause.car()?;
216                        let then = &clause.cdr().car()?;
217
218                        if eval_inner(env.clone(), condition, context.found_tail(true))?.into() {
219                            return eval_inner(env, then, context);
220                        }
221                    }
222
223                    Ok(Value::NIL)
224                }
225
226                Value::Symbol(Symbol(keyword)) if keyword == "if" => {
227                    let args = &list.cdr().into_iter().collect::<Vec<Value>>();
228
229                    let condition = require_arg(keyword, args, 0)?;
230                    let then_expr = require_arg(keyword, args, 1)?;
231                    let else_expr = require_arg(keyword, args, 2).ok();
232
233                    if eval_inner(env.clone(), condition, context.found_tail(true))?.into() {
234                        eval_inner(env, then_expr, context)
235                    } else {
236                        else_expr
237                            .map(|expr| eval_inner(env, &expr, context))
238                            .unwrap_or(Ok(Value::NIL))
239                    }
240                }
241
242                Value::Symbol(Symbol(keyword)) if keyword == "and" || keyword == "or" => {
243                    let args = &list.cdr().into_iter().collect::<Vec<Value>>();
244                    let is_or = keyword.as_str() == "or";
245
246                    let mut last_result: Option<Value> = None;
247                    for arg in args {
248                        let result = eval_inner(env.clone(), arg, context.found_tail(true))?;
249                        let truthy: bool = (&result).into();
250
251                        if is_or == truthy {
252                            return Ok(result);
253                        }
254
255                        last_result = Some(result);
256                    }
257
258                    Ok(if let Some(last_result) = last_result {
259                        last_result
260                    } else {
261                        // there were zero arguments
262                        (!is_or).into()
263                    })
264                }
265
266                // function call or macro expand
267                _ => {
268                    let mut func_or_macro =
269                        eval_inner(env.clone(), &list.car()?, context.found_tail(true))?;
270
271                    if matches!(func_or_macro, Value::Macro(_)) {
272                        let args = list.into_iter().skip(1).collect::<Vec<Value>>();
273
274                        let expanded =
275                            call_function_or_macro(env.clone(), &mut func_or_macro, args)?;
276
277                        eval_inner(env.clone(), &expanded, Context::new())
278                    } else {
279                        let args = list
280                            .into_iter()
281                            .skip(1)
282                            .map(|car| eval_inner(env.clone(), &car, context.found_tail(true)))
283                            .collect::<Result<Vec<Value>, RuntimeError>>()?;
284
285                        if !context.found_tail && context.in_func {
286                            Ok(Value::TailCall {
287                                func: Rc::new(func_or_macro),
288                                args,
289                            })
290                        } else {
291                            let mut res =
292                                call_function_or_macro(env.clone(), &mut func_or_macro, args);
293
294                            while let Ok(Value::TailCall { func, args }) = res {
295                                res = call_function_or_macro(env.clone(), func.as_ref(), args);
296                            }
297
298                            res
299                        }
300                    }
301                }
302            }
303        }
304
305        // plain value
306        _ => Ok(expression.clone()),
307    }
308}
309// 🦀 Boo! Did I scare ya? Haha!
310
311fn value_to_argnames(argnames: List) -> Result<Vec<Symbol>, RuntimeError> {
312    argnames
313        .into_iter()
314        .enumerate()
315        .map(|(index, arg)| match arg {
316            Value::Symbol(s) => Ok(s),
317            _ => Err(RuntimeError {
318                msg: format!(
319                    "Expected list of arg names, but arg {} is a {}",
320                    index,
321                    arg.type_name()
322                ),
323            }),
324        })
325        .collect()
326}
327
328/// Calling a function is separated from the main `eval_inner()` function
329/// so that tail calls can be evaluated without just returning themselves
330/// as-is as a tail-call.
331fn call_function_or_macro(
332    env: Rc<RefCell<Env>>,
333    func: &Value,
334    args: Vec<Value>,
335) -> Result<Value, RuntimeError> {
336    if let Value::NativeFunc(func) = func {
337        func(env, args)
338    } else if let Value::NativeClosure(closure) = func {
339        closure.borrow_mut()(env, args)
340    } else {
341        let lambda = match func {
342            Value::Lambda(lamb) => Some(lamb),
343            Value::Macro(lamb) => Some(lamb),
344            _ => None,
345        };
346
347        if let Some(lambda) = lambda {
348            // bind args
349            let mut arg_env = Env::extend(lambda.closure.clone());
350            for (index, arg_name) in lambda.argnames.iter().enumerate() {
351                if arg_name.0 == "..." {
352                    // rest parameters
353                    arg_env.define(
354                        Symbol::from("..."),
355                        Value::List(args.into_iter().skip(index).collect()),
356                    );
357                    break;
358                } else {
359                    arg_env.define(arg_name.clone(), args[index].clone());
360                }
361            }
362
363            // evaluate each line of body
364            let clauses: &List = lambda.body.as_ref().try_into()?;
365            eval_block_inner(
366                Rc::new(RefCell::new(arg_env)),
367                clauses.into_iter(),
368                Context {
369                    found_tail: false,
370                    in_func: true,
371                    quoting: false,
372                },
373            )
374        } else {
375            Err(RuntimeError {
376                msg: format!("{} is not callable", func),
377            })
378        }
379    }
380}
381
382#[derive(Debug, Clone, Copy)]
383struct Context {
384    pub found_tail: bool,
385    pub in_func: bool,
386    pub quoting: bool,
387}
388
389impl Context {
390    pub fn new() -> Self {
391        Self {
392            found_tail: false,
393            in_func: false,
394            quoting: false,
395        }
396    }
397
398    pub fn found_tail(self, found_tail: bool) -> Self {
399        Self {
400            found_tail,
401            in_func: self.in_func,
402            quoting: self.quoting,
403        }
404    }
405
406    // pub fn in_func(self, in_func: bool) -> Self {
407    //     Self {
408    //         found_tail: self.found_tail,
409    //         in_func,
410    //         quoting: self.quoting,
411    //     }
412    // }
413
414    pub fn quoting(self, quoting: bool) -> Self {
415        Self {
416            found_tail: self.found_tail,
417            in_func: self.in_func,
418            quoting,
419        }
420    }
421}