Skip to main content

nanvm_lib/vm/any/
option_call.rs

1use crate::vm::{
2    Any, IVm,
3    lambda::{OptionLambda, Region},
4};
5
6impl<A: IVm> Any<A> {
7    /// The EDAG's `?.()` node, `['?.()', callee, args, k]`: `a?.(...args)`,
8    /// opening a short-circuit region. A nullish callee skips the arguments
9    /// and the rest of the chain, which is why they are a thunk; any other
10    /// callee has them evaluated first and is then called as `Any::call`
11    /// calls it — `1?.(...args)` is the `TypeError` `1(...args)` is, after
12    /// the arguments.
13    pub fn option_call(self, args: impl FnOnce() -> Result<Self, Self>) -> OptionLambda<A> {
14        OptionLambda(Region::Live(self).option_call_value(args))
15    }
16}
17
18#[cfg(test)]
19mod tests {
20    use crate::{
21        naive::Naive,
22        vm::{Any, IVm, Nullish, ToAny, ToArray, error},
23    };
24
25    type A = Naive;
26
27    fn args() -> Result<Any<A>, Any<A>> {
28        Ok([7.0.to_any()].to_array().to_any())
29    }
30
31    #[test]
32    fn calls_a_function() {
33        let identity: Any<A> =
34            A::static_function(|_, args| Ok(args.to_any()), 0, [].to_array(), None).to_any();
35        let a = args().unwrap();
36        assert_eq!(identity.option_call(|| Ok(a.clone())).end(), Ok(a));
37    }
38
39    /// A nullish callee skips the arguments and answers `undefined`.
40    #[test]
41    fn nullish_callee_skips() {
42        assert_eq!(
43            Nullish::Null
44                .to_any::<A>()
45                .option_call(|| Err("boom".into()))
46                .end(),
47            Ok(Any::undefined())
48        );
49    }
50
51    /// The guard is against a nullish callee, not a non-callable one: `1`
52    /// passes it, the arguments run, and then the call throws.
53    #[test]
54    fn non_callable_callee_throws_after_the_arguments() {
55        assert_eq!(
56            1.0.to_any::<A>().option_call(|| Err("boom".into())).end(),
57            Err("boom".into())
58        );
59        assert_eq!(
60            1.0.to_any::<A>().option_call(args).end(),
61            Err(error::unexpected_type())
62        );
63    }
64}