snix_eval/vm/
macros.rs

1/// This module provides macros which are used in the implementation
2/// of the VM for the implementation of repetitive operations.
3///
4/// This macro simplifies the implementation of arithmetic operations,
5/// correctly handling the behaviour on different pairings of number
6/// types.
7#[macro_export]
8macro_rules! arithmetic_op {
9    ( $self:ident, $op:tt ) => {{ // TODO: remove
10        let b = $self.pop();
11        let a = $self.pop();
12        let result = fallible!($self, arithmetic_op!(&a, &b, $op));
13        $self.push(result);
14    }};
15
16    ( $a:expr, $b:expr, $op:tt ) => {{
17        match ($a, $b) {
18            (Value::Integer(i1), Value::Integer(i2)) => Ok(Value::Integer(i1 $op i2)),
19            (Value::Float(f1), Value::Float(f2)) => Ok(Value::Float(f1 $op f2)),
20            (Value::Integer(i1), Value::Float(f2)) => Ok(Value::Float(*i1 as f64 $op f2)),
21            (Value::Float(f1), Value::Integer(i2)) => Ok(Value::Float(f1 $op *i2 as f64)),
22
23            (v1, v2) => Err(ErrorKind::TypeError {
24                expected: "number (either int or float)",
25                actual: if v1.is_number() {
26                    v2.type_of()
27                } else {
28                    v1.type_of()
29                },
30            }),
31        }
32    }};
33}
34
35/// This macro simplifies the implementation of comparison operations.
36#[macro_export]
37macro_rules! cmp_op {
38    ( $vm:ident, $frame:ident, $span:ident, $op:tt ) => {{
39        lifted_pop! {
40            $vm(b, a) => {
41                async fn compare(a: Value, b: Value, co: GenCo) -> Result<Value, ErrorKind> {
42                    let a = generators::request_force(&co, a).await;
43                    let b = generators::request_force(&co, b).await;
44                    let span = generators::request_span(&co).await;
45                    let ordering = a.nix_cmp_ordering(b, co, span).await?;
46                    match ordering {
47                        Err(cek) => Ok(Value::from(cek)),
48                        Ok(ordering) => Ok(Value::Bool(cmp_op!(@order $op ordering))),
49                    }
50                }
51
52                let gen_span = $frame.current_span();
53                $vm.push_call_frame($span, $frame);
54                $vm.enqueue_generator("compare", gen_span, |co| compare(a, b, co));
55                return Ok(false);
56            }
57        }
58    }};
59
60    (@order < $ordering:expr) => {
61        $ordering == Ordering::Less
62    };
63
64    (@order > $ordering:expr) => {
65        $ordering == Ordering::Greater
66    };
67
68    (@order <= $ordering:expr) => {
69        matches!($ordering, Ordering::Equal | Ordering::Less)
70    };
71
72    (@order >= $ordering:expr) => {
73        matches!($ordering, Ordering::Equal | Ordering::Greater)
74    };
75}
76
77#[macro_export]
78macro_rules! lifted_pop {
79    ($vm:ident ($($bind:ident),+) => $body:expr) => {
80        {
81            $(
82                let $bind = $vm.stack_pop();
83            )+
84            $(
85                if $bind.is_catchable() {
86                    $vm.stack.push($bind);
87                    continue;
88                }
89            )+
90            $body
91        }
92    }
93}