spore_vm/
lib.rs

1use std::{collections::HashMap, sync::atomic::AtomicU16};
2
3use bumpalo::Bump;
4use gc::{is_garbage_collected, MemoryManager};
5use log::*;
6
7use compiler::Compiler;
8use error::{BacktraceError, VmError, VmResult};
9pub use settings::Settings;
10use stack_frame::StackFrame;
11use val::{
12    custom::CustomVal, ByteCode, CustomType, Instruction, NativeFunction, NativeFunctionContext,
13    ProtectedVal, Symbol, UnsafeVal, Val, ValId,
14};
15
16mod builtins;
17mod compiler;
18pub mod error;
19mod gc;
20pub mod parser;
21pub mod repl;
22mod settings;
23mod stack_frame;
24pub mod val;
25
26type BumpVec<'a, T> = bumpalo::collections::Vec<'a, T>;
27
28/// The GitHub issues page to file issues to.
29pub const ISSUE_LINK: &str = "https://github.com/wmedrano/spore/issues";
30
31/// The Spore virtual machine.
32///
33/// # Example
34/// ```rust
35/// let mut vm = spore_vm::Vm::default();
36/// vm.eval_str("(define foo 42)").unwrap();
37/// let foo = vm.val_by_name("foo").unwrap().try_int().unwrap(); // 42
38/// vm.eval_str("(define (bar x) (+ x foo))").unwrap();
39/// let bar_10 = vm
40///     .eval_function_by_name("bar", std::iter::once(10.into()))
41///     .unwrap()
42///     .try_int()
43///     .unwrap(); // 52
44/// ```
45#[derive(Debug)]
46pub struct Vm {
47    /// The data stack. This is used to store temporary values used for computation.
48    stack: Vec<UnsafeVal>,
49    /// Map from binding name to value. This is used to store global values.
50    values: HashMap<Symbol, UnsafeVal>,
51    /// The current stack frame. This contains what should be evaluated next and some extra context.
52    stack_frame: StackFrame,
53    /// The pending stack frames.
54    previous_stack_frames: Vec<StackFrame>,
55    /// Manages lifetime of all values, aside from simple atoms like bool/int/float.
56    pub(crate) objects: MemoryManager,
57    /// Contains bytecode compilation settings,
58    settings: Settings,
59    /// An arena for temporary computations for things like compilation and garbage collection.
60    tmp_arena: Option<Bump>,
61}
62
63impl Default for Vm {
64    /// Create a new virtual machine.
65    fn default() -> Vm {
66        Vm::new(Settings::default())
67    }
68}
69
70// A unique (enough) identifier for a VM. Used to identify if a value was generated from the VM or
71// not. The values start at 1 to ensure that the default (0) is not from a valid VM.
72static VM_ID: AtomicU16 = AtomicU16::new(1);
73
74impl Vm {
75    /// Create a new virtual machine.
76    pub fn new(settings: Settings) -> Vm {
77        let start_t = std::time::Instant::now();
78        let vm_id = VM_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
79        let mut vm = Vm {
80            // TODO: Determine optimal size for stack. Small values may perform, better, but
81            // exceeding the capacity may cause performance degregations.
82            stack: Vec::with_capacity(4096),
83            values: HashMap::new(),
84            // Allocate for a function call depth of 64. This is more than enough for most programs.
85            previous_stack_frames: Vec::with_capacity(64),
86            stack_frame: StackFrame::default(),
87            objects: MemoryManager::new(vm_id),
88            settings,
89            tmp_arena: Some(Bump::new()),
90        };
91        for (name, func) in builtins::BUILTINS {
92            vm = vm.with_native_function(name, *func);
93        }
94        info!(
95            "Initialized Spore VM in {elapsed:?} with {settings:?}",
96            elapsed = start_t.elapsed()
97        );
98        vm
99    }
100
101    /// Return the VM with the native function registered.
102    pub fn with_native_function(mut self, name: &str, func: NativeFunction) -> Self {
103        let func: UnsafeVal = func.into();
104        assert!(!is_garbage_collected(func));
105        // Unsafe OK: Native functions do not need to register with the vm.
106        unsafe { self.register_value(name, func) };
107        self
108    }
109
110    /// Return the VM with a custom value that is accessible globally.
111    ///
112    /// # Example
113    ///
114    /// ```rust
115    /// #[derive(Debug, Default)]
116    /// pub struct MyType(i64);
117    /// impl spore_vm::val::CustomType for MyType {}
118    /// impl std::fmt::Display for MyType {
119    ///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120    ///         write!(f, "my number is {}", self.0)
121    ///     }
122    /// }
123    ///
124    /// let mut vm = spore_vm::Vm::default()
125    ///     .with_custom_value("my_value", MyType(10));
126    /// let val = vm.val_by_name("my_value").unwrap();
127    /// let mut custom_val = val.try_custom_mut::<MyType>(&vm).unwrap();
128    /// custom_val.0 = 100;
129    /// ```
130    pub fn with_custom_value(mut self, name: &str, val: impl CustomType) -> Self {
131        let id = self.objects.insert_custom(CustomVal::new(val));
132        // Unsafe OK: Custom type is registered in the VM in the line above.
133        unsafe { self.register_value(name, id) };
134        self
135    }
136
137    /// Return the VM returned by calling `fn`.
138    pub fn with(self, f: impl Fn(Vm) -> Vm) -> Self {
139        f(self)
140    }
141
142    /// Register a value to the VM.
143    ///
144    /// # Safety
145    /// `val` must already be in the VM if it is a garbage collected type.
146    unsafe fn register_value(&mut self, name: &str, val: impl Into<UnsafeVal>) {
147        let val = val.into();
148        info!(
149            "Registering {name:?} to a(n) {tp} value.",
150            tp = val.type_name()
151        );
152        let interned_sym = self.get_or_create_symbol(name);
153        self.values.insert(interned_sym, val);
154    }
155}
156
157impl Vm {
158    /// Get the value with the given name or [None] if it does not exist.
159    pub fn val_by_name(&self, name: &str) -> Option<Val> {
160        let interned_name = self.get_symbol(name)?;
161        self.values
162            .get(&interned_name)
163            .copied()
164            // Unsafe OK: The value has not been garbage collected as its part of the values map.
165            .map(|v| unsafe { Val::from_unsafe_val(v) })
166    }
167
168    /// Evaluate a string in the virtual machine.
169    ///
170    /// ```rust
171    /// let mut vm = spore_vm::Vm::default();
172    /// let x = vm.eval_str("(+ 20 22)").unwrap().try_int().unwrap();
173    /// ```
174    pub fn eval_str(&mut self, source: &str) -> VmResult<ProtectedVal> {
175        let mut arena = self.tmp_arena.take().unwrap_or_else(|| {
176            warn!("Arena was unexpectedly unavailable. Please file an issue at {ISSUE_LINK} with proper context.");
177            Bump::new()
178        });
179        let bytecode = Compiler::compile(self, source, &arena)?;
180        arena.reset();
181        self.tmp_arena = Some(arena);
182        let bytecode_id = self.objects.insert_bytecode(bytecode);
183        self.eval_bytecode(bytecode_id, std::iter::empty())
184    }
185
186    /// Call a function with the given name.
187    ///
188    /// ```rust
189    /// let mut vm = spore_vm::Vm::default();
190    /// vm.eval_str("(define (fib n) (if (< n 2) n (+ (fib (+ n -1)) (fib (+ n -2)))))")
191    ///     .unwrap();
192    /// let ans = vm
193    ///     .eval_function_by_name("fib", std::iter::once(10.into()))
194    ///     .unwrap()
195    ///     .try_int()
196    ///     .unwrap();
197    /// ```
198    pub fn eval_function_by_name(
199        &mut self,
200        name: &str,
201        args: impl ExactSizeIterator<Item = Val<'static>>,
202    ) -> VmResult<ProtectedVal> {
203        let interned_name = self.get_or_create_symbol(name);
204        let function_val =
205            *self
206                .values
207                .get(&interned_name)
208                .ok_or_else(|| VmError::SymbolNotDefined {
209                    src: None,
210                    symbol: name.to_string(),
211                })?;
212        let bytecode_id = match function_val {
213            UnsafeVal::ByteCodeFunction(bc) => bc,
214            UnsafeVal::NativeFunction(f) => self
215                .objects
216                .insert_bytecode(ByteCode::new_native_function_call(name, f, args.len())),
217            v => {
218                return Err(VmError::TypeError {
219                    src: None,
220                    context: "eval-function-by-name",
221                    expected: UnsafeVal::FUNCTION_TYPE_NAME,
222                    actual: v.type_name(),
223                    value: v.formatted(self).to_string(),
224                })
225            }
226        };
227        // Unsafe Ack: These values should be inserted into VM stack ASAP.
228        let args = args.map(|arg| unsafe { arg.as_unsafe_val() });
229        self.eval_bytecode(bytecode_id, args)
230    }
231
232    /// Evaluate some bytecode in the virtual machine.
233    fn eval_bytecode(
234        &mut self,
235        bytecode_id: ValId<ByteCode>,
236        args: impl Iterator<Item = UnsafeVal>,
237    ) -> VmResult<ProtectedVal> {
238        let bytecode = self.objects.get_bytecode(bytecode_id).unwrap();
239        self.previous_stack_frames.clear();
240        self.stack.clear();
241        self.stack.extend(args);
242        self.stack
243            .extend(std::iter::repeat(UnsafeVal::Void).take(bytecode.local_bindings));
244        self.stack_frame = StackFrame::new(bytecode_id, bytecode, 0);
245        // Unsafe OK: The environment has just been set up.
246        unsafe { self.run_gc() };
247        loop {
248            if let Some(v) = self.run_next().map_err(|err| self.annotate_src(err))? {
249                // Unsafe OK: This is a new valid val and we are adding GC protection to it.
250                let v = unsafe { Val::from_unsafe_val(v) };
251                return Ok(ProtectedVal::new(self, v));
252            }
253        }
254    }
255
256    fn annotate_src(&self, error: VmError) -> VmError {
257        match self.stack_frame.previous_instruction_source(self) {
258            Some(src) => error.with_src(src),
259            None => error,
260        }
261    }
262
263    /// Run the next instruction in the virtual machine.
264    ///
265    /// If there are no more instructions to run, then `Some(return_value)` will be
266    /// returned. Otherwise, `None` will be returned.
267    fn run_next(&mut self) -> VmResult<Option<UnsafeVal>> {
268        let maybe_instruction = self
269            .stack_frame
270            .instructions
271            .as_ref()
272            .get(self.stack_frame.instruction_idx);
273        let instruction = maybe_instruction.unwrap_or(&Instruction::Return);
274        self.stack_frame.instruction_idx += 1;
275        match instruction {
276            Instruction::PushConst(c) => self.stack.push(*c),
277            Instruction::PushCurrentFunction => {
278                let f = UnsafeVal::ByteCodeFunction(self.stack_frame.bytecode_id);
279                self.stack.push(f);
280            }
281            Instruction::Pop(n) => {
282                let start = self.stack.len() - n;
283                self.stack.drain(start..);
284            }
285            Instruction::GetArg(n) => {
286                let val = self.stack[self.stack_frame.stack_start + *n];
287                self.stack.push(val);
288            }
289            Instruction::BindArg(n) => {
290                let val = self.stack.pop().unwrap();
291                self.stack[self.stack_frame.stack_start + *n] = val;
292            }
293            Instruction::Deref(symbol) => {
294                let v = match self.values.get(symbol) {
295                    Some(v) => *v,
296                    None => {
297                        return Err(VmError::SymbolNotDefined {
298                            src: None,
299                            symbol: self
300                                .symbol_to_str(*symbol)
301                                .unwrap_or("*symbol-not-registered*")
302                                .to_string(),
303                        });
304                    }
305                };
306                self.stack.push(v);
307            }
308            Instruction::Define(symbol) => {
309                let v = self.stack.pop().ok_or_else(BacktraceError::capture)?;
310                self.values.insert(*symbol, v);
311            }
312            Instruction::Eval(n) => {
313                self.execute_eval(*n)?;
314            }
315            Instruction::EvalNative { func, arg_count } => {
316                self.execute_eval_native(*func, *arg_count)?;
317            }
318            Instruction::JumpIf(n) => {
319                if self.stack.pop().unwrap().is_truthy() {
320                    self.stack_frame.instruction_idx += *n;
321                }
322            }
323            Instruction::Jump(n) => {
324                self.stack_frame.instruction_idx += *n;
325            }
326            Instruction::Return => return Ok(self.execute_return()),
327        }
328        Ok(None)
329    }
330
331    fn execute_eval_native(&mut self, func: NativeFunction, arg_count: usize) -> VmResult<()> {
332        let stack_start = self.stack.len() - arg_count;
333        let args = unsafe {
334            let slice = std::slice::from_raw_parts(self.stack.as_ptr().add(stack_start), arg_count);
335            Val::from_unsafe_val_slice(slice)
336        };
337        let builder = func(NativeFunctionContext::new(self), args)?;
338        // Unsafe OK: Value is inserted into VM immediately.
339        let v = unsafe { builder.build() };
340        match arg_count {
341            0 => {
342                self.stack.push(v);
343            }
344            _ => {
345                self.stack.truncate(stack_start + 1);
346                self.stack[stack_start] = v;
347            }
348        };
349        Ok(())
350    }
351
352    /// Execute the evaluation of the top n values in the stack.
353    ///
354    /// The deepest value should be a function with the rest of the values being the arguments.
355    fn execute_eval(&mut self, n: usize) -> VmResult<()> {
356        if n == 0 {
357            Err(BacktraceError::capture())?;
358        }
359        let function_idx = self
360            .stack
361            .len()
362            .checked_sub(n)
363            .ok_or_else(BacktraceError::capture)?;
364        let stack_start = function_idx + 1;
365        let func_val = self.stack[function_idx];
366        match func_val {
367            UnsafeVal::NativeFunction(func) => {
368                let args = unsafe {
369                    let slice =
370                        std::slice::from_raw_parts(self.stack.as_ptr().add(stack_start), n - 1);
371                    Val::from_unsafe_val_slice(slice)
372                };
373                let builder = func(NativeFunctionContext::new(self), args)?;
374                // Unsafe OK: Value is inserted into VM immediately.
375                let v = unsafe { builder.build() };
376                self.stack[function_idx] = v;
377                self.stack.truncate(stack_start);
378                Ok(())
379            }
380            UnsafeVal::ByteCodeFunction(bytecode_id) => {
381                let bytecode = {
382                    let bytecode = self.objects.get_bytecode(bytecode_id).unwrap();
383                    let arg_count = n - 1;
384                    if bytecode.arg_count != arg_count {
385                        return Err(VmError::ArityError {
386                            function: bytecode.name.clone(),
387                            expected: bytecode.arg_count,
388                            actual: arg_count,
389                        });
390                    }
391                    if self.previous_stack_frames.capacity() == self.previous_stack_frames.len() {
392                        return Err(self.execute_call_stack_limit_reached());
393                    }
394                    bytecode
395                };
396                self.stack
397                    .extend(std::iter::repeat(UnsafeVal::Void).take(bytecode.local_bindings));
398                let previous_stack_frame = std::mem::replace(
399                    &mut self.stack_frame,
400                    StackFrame::new(bytecode_id, bytecode, stack_start),
401                );
402                self.previous_stack_frames.push(previous_stack_frame);
403                Ok(())
404            }
405            _ => Err(VmError::TypeError {
406                src: None,
407                context: "function invocation",
408                expected: UnsafeVal::FUNCTION_TYPE_NAME,
409                actual: func_val.type_name(),
410                value: func_val.formatted(self).to_string(),
411            }),
412        }
413    }
414
415    fn execute_call_stack_limit_reached(&mut self) -> VmError {
416        let mut call_stack = Vec::with_capacity(1 + self.previous_stack_frames.len());
417        call_stack.push(self.stack_frame.bytecode(self).name.clone());
418        call_stack.extend(
419            self.previous_stack_frames
420                .iter()
421                .rev()
422                .map(|sf| sf.bytecode(self).name.clone()),
423        );
424        VmError::MaximumFunctionCallDepth {
425            call_stack,
426            max_depth: self.previous_stack_frames.len(),
427        }
428    }
429
430    /// Execute returning from the current stack frame.
431    fn execute_return(&mut self) -> Option<UnsafeVal> {
432        // 1. Return the current value to the top of the stack.
433        let ret_val: UnsafeVal = if self.stack_frame.stack_start < self.stack.len() {
434            // Unwrap OK: The above statement is never true when len == 0.
435            self.stack.pop().unwrap()
436        } else {
437            ().into()
438        };
439        // 2. Set up the next continuation.
440        match self.previous_stack_frames.pop() {
441            // 2a. Pop the stack frame and replace the top value in the stack with the return value.
442            Some(c) => {
443                self.stack.truncate(self.stack_frame.stack_start);
444                match self.stack.last_mut() {
445                    Some(v) => *v = ret_val,
446                    None => unreachable!(),
447                }
448                self.stack_frame = c;
449                None
450            }
451            // 2b. There is nothing to continue to so return the value.
452            None => {
453                std::mem::take(&mut self.stack_frame);
454                self.stack.clear();
455                Some(ret_val)
456            }
457        }
458    }
459}
460
461impl Vm {
462    /// Run the garbage collector.
463    ///
464    /// This does not need to be manually invoked as it is called automatically at the start of
465    /// evaluation through functions like [Self::eval_str] and [Self::eval_function_by_name].
466    ///
467    /// # Safety
468    ///
469    pub unsafe fn run_gc(&mut self) {
470        let is_gc = |v: &UnsafeVal| is_garbage_collected(*v);
471        let mut arena = self.tmp_arena.take().unwrap_or_else(|| {
472            warn!("Arena was unexpectedly unavailable. Please file an issue at {ISSUE_LINK} with proper context.");
473            Bump::new()
474        });
475        {
476            let mut bytecodes: BumpVec<(ValId<_>, ByteCode)> = BumpVec::new_in(&arena);
477            bytecodes.push((
478                self.stack_frame.bytecode_id,
479                self.stack_frame.bytecode(self).clone(),
480            ));
481            for previous_frame in self.previous_stack_frames.iter() {
482                bytecodes.push((
483                    previous_frame.bytecode_id,
484                    previous_frame.bytecode(self).clone(),
485                ));
486            }
487            let vals = self
488                .stack
489                .iter()
490                .copied()
491                .filter(is_gc)
492                .chain(self.values.values().copied().filter(is_gc))
493                .chain(bytecodes.iter().flat_map(|(id, bytecode)| {
494                    bytecode
495                        .values()
496                        .filter(is_gc)
497                        .chain(std::iter::once((*id).into()))
498                }));
499            self.objects.run_gc(&arena, vals);
500        }
501        arena.reset();
502        self.tmp_arena = Some(arena);
503    }
504}
505
506impl Vm {
507    /// Get the symbol for the given `s`, or `None` if it does not exist within the VM.
508    pub fn get_symbol(&self, s: &str) -> Option<Symbol> {
509        self.objects.get_symbol(s)
510    }
511
512    /// Get the given symbol within the VM or create it if it does not exist.
513    pub fn get_or_create_symbol(&mut self, s: &str) -> Symbol {
514        self.objects.get_or_create_symbol(s)
515    }
516
517    /// Get the `str` representation for a symbol.
518    pub fn symbol_to_str(&self, s: Symbol) -> Option<&str> {
519        self.objects.symbol_to_str(s)
520    }
521}
522
523impl Drop for Vm {
524    fn drop(&mut self) {
525        info!("Dropping Spore VM.");
526    }
527}
528
529#[cfg(test)]
530mod tests {
531    use error::CompileError;
532    use parser::span::Span;
533
534    use super::*;
535
536    #[test]
537    fn constant_expression_evaluates_to_constant() {
538        let mut vm = Vm::default();
539        let actual = vm.eval_str("42").unwrap();
540        assert_eq!(actual.try_int().unwrap(), 42);
541    }
542
543    #[test]
544    fn expression_can_evaluate() {
545        let mut vm = Vm::default();
546        let actual = vm.eval_str("(+ 1 2 3 4.0)").unwrap();
547        assert_eq!(actual.try_float().unwrap(), 10.0);
548    }
549
550    #[test]
551    fn list_function_returns_list() {
552        let mut vm = Vm::default();
553        let actual = vm.eval_str("(list 1 2.3 \"three\")").unwrap();
554        assert_eq!(actual.to_string(), "(1 2.3 \"three\")");
555    }
556
557    #[test]
558    fn vm_error_is_reported() {
559        let mut vm = Vm::default();
560        let src = "(+ true false)";
561        let actual = vm.eval_str(src).unwrap_err();
562        assert_eq!(
563            actual,
564            VmError::TypeError {
565                src: Some(Span::new(0, 14).with_src(src.into())),
566                context: "+",
567                expected: "int or float",
568                actual: UnsafeVal::BOOL_TYPE_NAME,
569                value: "true".to_string(),
570            }
571        );
572    }
573
574    #[test]
575    fn compile_error_is_reported() {
576        let mut vm = Vm::default();
577        let actual = vm.eval_str("((define x 12))").unwrap_err();
578        assert_eq!(
579            actual,
580            VmError::CompileError(CompileError::DefineNotAllowed)
581        );
582    }
583
584    #[test]
585    fn defined_variable_can_be_referenced() {
586        let mut vm = Vm::default();
587        assert_eq!(
588            vm.eval_str("(define x 12) (+ x x)")
589                .unwrap()
590                .try_int()
591                .unwrap(),
592            24
593        );
594        assert_eq!(vm.eval_str("(+ x 10)").unwrap().try_int().unwrap(), 22);
595    }
596
597    #[test]
598    fn if_statement_can_return_any_of() {
599        let mut vm = Vm::default();
600        assert_eq!(
601            vm.eval_str("(if true (+ 1 2))").unwrap().try_int().unwrap(),
602            3
603        );
604        assert_eq!(
605            vm.eval_str("(if true (+ 1 2) (+ 3 4))")
606                .unwrap()
607                .try_int()
608                .unwrap(),
609            3
610        );
611        assert_eq!(
612            vm.eval_str("(if false (+ 1 2) (+ 3 4))")
613                .unwrap()
614                .try_int()
615                .unwrap(),
616            7
617        );
618        let got = vm.eval_str("(if false (+ 1 2))").unwrap();
619        assert!(got.is_void(), "{got}");
620    }
621
622    #[test]
623    fn if_statement_with_truthy_predicate_true_branch() {
624        let mut vm = Vm::default();
625        assert_eq!(
626            vm.eval_str("(if 1 (+ 1 2) (+ 3 4))")
627                .unwrap()
628                .try_int()
629                .unwrap(),
630            3
631        );
632        assert_eq!(vm.eval_str("(if 1 (+ 1 2))").unwrap().try_int().unwrap(), 3);
633    }
634
635    #[test]
636    fn lambda_can_be_evaluated() {
637        let mut vm = Vm::default();
638        assert_eq!(
639            vm.eval_str("((lambda () 7))").unwrap().try_int().unwrap(),
640            7
641        );
642        assert_eq!(
643            vm.eval_str("((lambda () (+ 1 2 3)))")
644                .unwrap()
645                .try_int()
646                .unwrap(),
647            6
648        );
649    }
650
651    #[test]
652    fn lambda_with_args_can_be_evaluated() {
653        let mut vm = Vm::default();
654        assert_eq!(
655            vm.eval_str("((lambda (a b) 4) 1 2)")
656                .unwrap()
657                .try_int()
658                .unwrap(),
659            4,
660        );
661        assert_eq!(
662            vm.eval_str("((lambda (a b) (+ a b)) 1 2)")
663                .unwrap()
664                .try_int()
665                .unwrap(),
666            3
667        );
668    }
669
670    #[test]
671    fn function_called_with_wrong_number_of_args_produces_error() {
672        let mut vm = Vm::default();
673        assert_eq!(
674            vm.eval_str("((lambda () 10) 1)").unwrap_err(),
675            VmError::ArityError {
676                function: "".into(),
677                expected: 0,
678                actual: 1
679            },
680        );
681        assert_eq!(
682            vm.eval_str("((lambda (a) a))").unwrap_err(),
683            VmError::ArityError {
684                function: "".into(),
685                expected: 1,
686                actual: 0
687            },
688        );
689        let mut got = vm
690            .eval_str("(define (takes-two-args arg1 arg2) (+ arg1 arg2))")
691            .unwrap();
692        assert!(got.is_void(), "{got}");
693        let (vm, _) = got.split();
694        assert_eq!(
695            vm.eval_str("(takes-two-args 1)").unwrap_err(),
696            VmError::ArityError {
697                function: "takes-two-args".into(),
698                expected: 2,
699                actual: 1,
700            },
701        );
702    }
703
704    #[test]
705    fn can_get_val_by_name() {
706        let mut vm = Vm::default();
707        vm.eval_str("(define one 1) (define two 2)").unwrap();
708        assert_eq!(vm.val_by_name("one").unwrap().try_int().unwrap(), 1);
709        assert_eq!(vm.val_by_name("two").unwrap().try_int().unwrap(), 2);
710    }
711
712    #[test]
713    fn getting_val_that_does_not_exist_returns_err() {
714        let mut vm = Vm::default();
715        vm.eval_str("(define one 1) (define two 2)").unwrap();
716        assert!(vm.val_by_name("three").is_none());
717    }
718
719    #[test]
720    fn can_eval_by_function_with_native_function() {
721        let mut vm = Vm::default();
722        let ans = vm
723            .eval_function_by_name("+", [10.into(), 5.into()].into_iter())
724            .unwrap()
725            .try_int()
726            .unwrap();
727        assert_eq!(ans, 15);
728    }
729
730    #[test]
731    fn eval_function_that_does_not_exist_produces_error() {
732        let mut vm = Vm::default();
733        vm.eval_str("(define (foo) 1)").unwrap();
734        assert_eq!(
735            vm.eval_function_by_name("bar", std::iter::empty())
736                .unwrap_err(),
737            VmError::SymbolNotDefined {
738                src: None,
739                symbol: "bar".into()
740            },
741        );
742    }
743
744    #[test]
745    fn eval_function_that_is_not_function_produces_error() {
746        let mut vm = Vm::default();
747        vm.eval_str("(define foo 100)").unwrap();
748        assert_eq!(
749            vm.eval_function_by_name("foo", std::iter::empty())
750                .unwrap_err(),
751            VmError::TypeError {
752                src: None,
753                context: "eval-function-by-name",
754                expected: UnsafeVal::FUNCTION_TYPE_NAME,
755                actual: UnsafeVal::INT_TYPE_NAME,
756                value: "100".into(),
757            }
758        );
759    }
760
761    #[test]
762    fn can_call_function_recursively() {
763        let mut vm = Vm::default();
764        vm.eval_str("(define (fib n) (if (< n 2) n (+ (fib (+ n -1)) (fib (+ n -2)))))")
765            .unwrap();
766        let ans = vm
767            .eval_function_by_name("fib", std::iter::once(10.into()))
768            .unwrap()
769            .try_int()
770            .unwrap();
771        assert_eq!(ans, 55);
772    }
773
774    #[test]
775    fn infinite_recursion_halts() {
776        let mut vm = Vm::default();
777        assert!(vm
778            .eval_str("(define (recurse) (recurse))")
779            .unwrap()
780            .is_void());
781        assert_eq!(
782            vm.eval_str("(recurse)").unwrap_err(),
783            VmError::MaximumFunctionCallDepth {
784                max_depth: 64,
785                call_stack: std::iter::repeat("recurse")
786                    .take(64)
787                    .chain(std::iter::once(""))
788                    .map(Into::into)
789                    .collect(),
790            }
791        );
792    }
793
794    #[test]
795    fn aggressive_inline_produces_same_results_when_there_are_no_redefinitions() {
796        let mut aggressive_inline_vm = Vm::new(Settings {
797            enable_aggressive_inline: true,
798            enable_source_maps: false,
799        });
800        let mut default_vm = Vm::new(Settings {
801            enable_aggressive_inline: false,
802            enable_source_maps: true,
803        });
804        let srcs = ["(define x 12)", "x", "(+ x x)"];
805        for src in srcs {
806            assert_eq!(
807                aggressive_inline_vm.eval_str(src).unwrap().to_string(),
808                default_vm.eval_str(src).unwrap().to_string(),
809            )
810        }
811    }
812
813    #[test]
814    fn let_statement() {
815        let mut vm = Vm::default();
816        assert_eq!(
817            vm.eval_str("(let ([x 10] [y 20] [z (+ x y)]) (+ x y z))")
818                .unwrap()
819                .try_int()
820                .unwrap(),
821            60
822        );
823    }
824
825    #[test]
826    fn when_multiple_bindings_exist_last_one_is_used() {
827        let mut vm = Vm::default();
828        let src = r#"
829(let ([x 1])
830  (let ([x 2]
831        [x (+ x x)])
832    x))
833"#;
834        assert_eq!(vm.eval_str(src).unwrap().try_int().unwrap(), 4);
835    }
836
837    #[test]
838    fn multiple_bindings_dont_affect_previous_binding_when_out_of_scope() {
839        let mut vm = Vm::default();
840        let src = r#"
841(let ([x 1])
842  (let ([x 2]
843        [x (+ x x)])
844    x)
845x)
846"#;
847        assert_eq!(vm.eval_str(src).unwrap().try_int().unwrap(), 1);
848    }
849
850    #[test]
851    fn local_bindings_take_precedence_over_arguments() {
852        let mut vm = Vm::default();
853        let src = r#"
854(define (foo x)
855  (let ([old-x x]
856        [x     10])
857    (+ old-x x)))
858
859(foo 100)
860"#;
861        assert_eq!(vm.eval_str(src).unwrap().try_int().unwrap(), 110);
862    }
863
864    #[test]
865    fn empty_or_returns_false() {
866        let mut vm = Vm::default();
867        let src = "(or)";
868        assert!(!vm.eval_str(src).unwrap().try_bool().unwrap());
869    }
870
871    #[test]
872    fn or_with_true_returns_true() {
873        let mut vm = Vm::default();
874        let src = "(or false false true false)";
875        assert!(vm.eval_str(src).unwrap().try_bool().unwrap());
876    }
877
878    #[test]
879    fn or_with_truthy_values_returns_first_truthy_value() {
880        let mut vm = Vm::default();
881        let src = "(or false false 5 4 3 2)";
882        assert_eq!(vm.eval_str(src).unwrap().try_int().unwrap(), 5);
883    }
884
885    #[test]
886    fn or_with_all_false_or_void_returns_last_arg() {
887        let mut vm = Vm::default();
888        assert!(vm
889            .eval_str("(or void false void false void)")
890            .unwrap()
891            .is_void());
892        assert!(!vm
893            .eval_str("(or void false void false void false)")
894            .unwrap()
895            .try_bool()
896            .unwrap());
897    }
898
899    #[test]
900    fn and_with_no_args_returns_true() {
901        let mut vm = Vm::default();
902        let src = "(and)";
903        assert!(vm.eval_str(src).unwrap().try_bool().unwrap());
904    }
905
906    #[test]
907    fn and_with_all_truthy_args_returns_last_arg() {
908        let mut vm = Vm::default();
909        let src = "(and 1 2 3 4)";
910        assert_eq!(vm.eval_str(src).unwrap().try_int().unwrap(), 4);
911    }
912
913    #[test]
914    fn and_with_false_arg_returns_first_false_arg() {
915        let mut vm = Vm::default();
916        assert!(!vm
917            .eval_str("(and 1 2 false 3 4)")
918            .unwrap()
919            .try_bool()
920            .unwrap());
921        assert!(vm.eval_str("(and 1 2 void 3 4)").unwrap().is_void());
922    }
923}