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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;

use super::{Cont, Env, Ns, Primitive, Proc, Result, SExp};

mod base;
mod core;
mod math;
mod write;

/// Evaluation context for LISP expressions.
///
/// ## Note
/// `Context::default()` only provides *very* basic utilities. To obtain an
/// evaluation context with useful functions available, use
/// [`Context::base()`](#method.base).
///
/// ## Some implementation details
/// `Context` maintains separate environments for "core" (special forms, etc.),
/// "lang" (basic functions, vectors, and more), and "user" definitions. Most of
/// the provided methods operate on the "user" environment, as the intended use
/// case keeps the other environments immutable once they have been initialized.
pub struct Context {
    core: Ns,
    cont: Rc<RefCell<Cont>>,
    /// You can `insert` additional definitions here to make them available
    /// throughout the runtime. These definitions will not go out of scope
    /// automatically, but can be overridden (see [`get`](#method.get) for
    /// semantic details).
    pub lang: Ns,
    out: Option<String>,
}

impl Default for Context {
    fn default() -> Self {
        Self {
            core: Self::core(),
            cont: Cont::default().into_rc(),
            lang: Ns::new(),
            out: None,
        }
    }
}

impl Context {
    /// Add a new, nested scope.
    ///
    /// See [Context::pop](#method.pop) for a usage example.
    pub fn push(&mut self) {
        self.cont.borrow_mut().push();
    }

    /// Remove the most recently added scope.
    ///
    /// If the stack height is 1, all definitions will be cleared, and the
    /// global scope will be replaced with an empty one.
    ///
    /// # Example
    /// ```
    /// use parsley::prelude::*;
    /// let mut ctx = Context::default();
    ///
    /// assert_eq!(ctx.get("x"), None);
    /// ctx.push();
    /// ctx.define("x", SExp::Null);
    /// assert_eq!(ctx.get("x"), Some(SExp::Null));
    /// ctx.pop();
    /// assert_eq!(ctx.get("x"), None);
    /// ```
    pub fn pop(&mut self) {
        self.cont.borrow_mut().pop();
    }

    /// Create a new definition in the current scope.
    pub fn define(&mut self, key: &str, value: SExp) {
        self.cont.borrow().env().define(key, value)
    }

    /// Get the definition for a symbol in the execution environment.
    ///
    /// Returns `None` if no definition is found.
    ///
    /// # Override semantics
    /// This method searches for a definition in the following order:
    ///
    ///   1. The core language
    ///   2. User definitions, starting from the most recent scope and working
    ///      backward to the top-level
    ///   3. [Language-level definitions](#structfield.lang)
    ///
    /// What this means is that definitions populated in the `lang` field can be
    /// overridden inside the runtime (e.g. in a REPL), but special form keywords
    /// cannot. For example, we can `(define null "foo")`, but we cannot
    /// `(set! and or)`.
    ///
    /// # Examples
    /// ```
    /// let ctx = parsley::Context::default(); // only core definitions included
    /// assert!(ctx.get("potato").is_none());
    /// ```
    /// ```
    /// use parsley::prelude::*;
    /// let mut ctx = Context::default();
    ///
    /// ctx.define("x", SExp::from(3));
    /// assert_eq!(ctx.get("x"), Some(SExp::from(3)));
    /// ```
    pub fn get(&self, key: &str) -> Option<SExp> {
        // first check core (reserved keywords)
        if let Some(exp) = self.core.get(key) {
            return Some(exp.clone());
        }

        // then the environment stack
        if let Some(exp) = self.cont.borrow().env().get(key) {
            return Some(exp);
        }

        // then check the stdlib
        if let Some(exp) = self.lang.get(key) {
            return Some(exp.clone());
        }

        // otherwise fail
        None
    }

    /// Re-bind an existing definition to a new value.
    ///
    /// Returns `Ok` if an existing definition was found and updated. Returns
    /// `Err` if no definition exists.
    ///
    /// # Example
    /// ```
    /// use parsley::prelude::*;
    /// let mut ctx = Context::default();
    ///
    /// assert!(ctx.set("x", SExp::from(false)).is_err());    // Err, because x is not yet defined
    /// ctx.define("x", SExp::from(3));                       // define x
    /// assert_eq!(ctx.get("x"), Some(SExp::from(3)));        // check that its value is 3
    /// assert!(ctx.set("x", SExp::from("potato")).is_ok());  // Ok because x is now defined
    /// assert_eq!(ctx.get("x"), Some(SExp::from("potato"))); // check that its value is now "potato"
    /// ```
    pub fn set(&mut self, key: &str, value: SExp) -> Result {
        self.cont.borrow().env().set(key, value)
    }

    /// Push a new partial continuation with an existing environment.
    pub(super) fn use_env(&mut self, envt: Rc<Env>) {
        self.cont.borrow_mut().set_env(envt);
    }

    /// Push a new partial continuation onto the stack.
    pub(super) fn push_cont(&mut self) {
        self.cont = Cont::from(&self.cont).into_rc();
    }

    /// Pop the most recent partial continuation off of the stack.
    pub(super) fn pop_cont(&mut self) {
        let new = self.cont.borrow().parent().unwrap_or_default();
        self.cont = new;
    }

    fn eval_args(&mut self, args: SExp) -> Result {
        args.into_iter().map(|a| self.eval(a)).collect()
    }

    pub(super) fn eval_defer(&mut self, body: &SExp) -> Result {
        let mut result = Ok(SExp::Atom(Primitive::Undefined));

        let mut i = body.iter().peekable();

        while let Some(expr) = i.next() {
            if i.peek().is_some() {
                result = self.eval(expr.to_owned());
            } else {
                result = Ok(self.defer(expr.to_owned()))
            }

            if result.is_err() {
                break;
            }
        }
        result
    }

    /// Run a code snippet in an existing `Context`.
    ///
    /// # Example
    /// ```
    /// use parsley::prelude::*;
    /// let mut ctx = Context::base();
    ///
    /// assert!(ctx.run("x").is_err());
    /// assert!(ctx.run("(define x 6)").is_ok());
    /// assert_eq!(ctx.run("x").unwrap(), SExp::from(6));
    /// ```
    pub fn run(&mut self, expr: &str) -> Result {
        self.eval(expr.parse::<SExp>()?)
    }

    /// Evaluate an S-Expression in a context.
    ///
    /// The context will retain any definitions bound during evaluation
    /// (e.g. `define`, `set!`).
    ///
    /// # Examples
    /// ```
    /// use parsley::prelude::*;
    /// let result = Context::base().eval(
    ///     sexp![SExp::sym("eq?"), 0, 1]
    /// );
    /// assert_eq!(result.unwrap(), SExp::from(false));
    /// ```
    /// ```
    /// use parsley::prelude::*;
    /// let mut ctx = Context::base();
    ///
    /// let exp1 = sexp![SExp::sym("define"), SExp::sym("x"), 10];
    /// let exp2 = SExp::sym("x");
    ///
    /// ctx.eval(exp1);
    /// assert_eq!(ctx.eval(exp2).unwrap(), SExp::from(10));
    /// ```
    pub fn eval(&mut self, mut expr: SExp) -> Result {
        use super::Error::{NotAProcedure, NullList, UndefinedSymbol};
        use super::Func::Tail;
        use super::Primitive::{Procedure, Symbol, Undefined};
        use super::SExp::{Atom, Null, Pair};

        self.push_cont();

        let res = loop {
            expr = match expr {
                // cannot evaluate null
                Null => break Err(NullList),
                // check if symbol is defined
                Atom(Symbol(sym)) => match self.get(&sym) {
                    None | Some(Atom(Undefined)) => {
                        break Err(UndefinedSymbol { sym });
                    }
                    Some(exp) => exp,
                },
                // continue evaluation
                Atom(Procedure(Proc {
                    func: Tail { body, envt },
                    ..
                })) => {
                    self.cont.borrow_mut().set_env(envt);
                    expr = body.deref().to_owned();
                    continue;
                }
                // cannot reduce further
                Atom(_) => break Ok(expr),
                // it's an application
                Pair { head, tail } => {
                    // evaluate the first element
                    match self.eval(*head)? {
                        // if it is indeed a procedure
                        Atom(Procedure(p)) => {
                            let args = if p.defer_eval() {
                                *tail
                            } else {
                                self.eval_args(*tail)?
                            };
                            // then apply it
                            p.apply(args, self)?
                        }
                        // otherwise complain
                        proc => {
                            break Err(NotAProcedure {
                                exp: proc.to_string(),
                            });
                        }
                    }
                }
            };

            // see if we need to evaluate again
            match expr {
                Atom(Procedure(ref p)) if p.is_tail() => continue,
                _ => break Ok(expr),
            }
        };

        self.pop_cont();
        res
    }
}