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
//! Collection of helpful macros.

/// This macro creates a `Vec1` by checking at compile-time that its
/// invariant holds.
#[macro_export]
macro_rules! ne_vec {
    ($item:expr; 0) => {
        compile_error!("Cannot create an empty `Vec1`, it violates its invariant.")
    };

    () => {
        compile_error!("Cannot create an empty `Vec1`, it violates its invariant.")
    };

    ($item:expr; $length:expr) => {
        {
            fluence_it_types::ne_vec::NEVec::new(vec![$item; $length]).unwrap()
        }
    };

    ($($item:expr),+ $(,)?) => {
        {
            fluence_it_types::ne_vec::NEVec::new(vec![$($item),*]).unwrap()
        }
    };
}

/// This macro runs a parser, extracts the next input and the parser
/// output, and positions the next input on `$input`.
macro_rules! consume {
    (($input:ident, $parser_output:ident) = $parser_expression:expr) => {
        let (next_input, $parser_output) = $parser_expression;
        $input = next_input;
    };

    (($input:ident, mut $parser_output:ident) = $parser_expression:expr) => {
        let (next_input, mut $parser_output) = $parser_expression;
        $input = next_input;
    };
}

/// This macro creates an executable instruction for the interpreter.
///
/// # Example
///
/// The following example creates a `foo` executable instruction.clone(),
/// which takes 2 arguments (`x` and `y`), and does something
/// mysterious by using the `interpreter::Runtime` API.
///
/// ```rust,ignore
/// executable_instruction!(
///     foo(x: u64, y: u64, instruction_name: String) -> _ {
/// //                                                   ^ output type is purposely blank
/// //                      ^^^^^^^^^^^^^^^^ the instruction name, for debugging purposes
/// //              ^ the `y` argument
/// //      ^ the `x` argument
///
///     // an executable instruction is a closure that takes a `Runtime` instance
///     move |runtime| -> _ {
///         // Do something.
///
///         Ok(())
///     }
/// );
/// ```
///
/// Check the existing executable instruction to get more examples.
macro_rules! executable_instruction {
    ($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => {
        pub(crate) fn $name<Instance, Export, LocalImport, Memory, MemoryView, Store>(
            $($argument_name: $argument_type),*
        ) -> crate::interpreter::ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView, Store>
        where
            Export: crate::interpreter::wasm::structures::Export,
            LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
            Memory: crate::interpreter::wasm::structures::Memory<MemoryView, Store>,
            MemoryView: crate::interpreter::wasm::structures::MemoryView<Store>,
            Instance: crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
            Store: crate::interpreter::wasm::structures::Store,
        {
            #[allow(unused_imports)]
            use crate::interpreter::{stack::Stackable};

            Box::new($implementation)
        }
    };
}

#[cfg(test)]
macro_rules! test_executable_instruction {
    (
        $test_name:ident =
            instructions: [ $($instructions:expr),* $(,)* ],
            invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ],
            instance: $instance:expr,
            stack: [ $($stack:expr),* $(,)* ]
            $(,)*
    ) => {
        #[test]
        #[allow(non_snake_case, unused)]
        fn $test_name() {
            use crate::{
                interpreter::{
                    instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView},
                    stack::Stackable,
                    Instruction, Interpreter,
                },
                types::IType,
                values::IValue,
            };
            use std::{cell::Cell, collections::HashMap, convert::TryInto};

            let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> =
                (&vec![$($instructions),*]).try_into().unwrap();

            let invocation_inputs = vec![$($invocation_inputs),*];
            let mut instance = $instance;
            let run = interpreter.run(&invocation_inputs, &mut instance);

            let err = match &run {
                Ok(_) => "".to_string(),
                Err(e) => e.to_string(),
            };

            assert!(run.is_ok(), err);

            let stack = run.unwrap();

            assert_eq!(stack.as_slice(), &[$($stack),*]);
        }
    };

    (
        $test_name:ident =
            instructions: [ $($instructions:expr),* $(,)* ],
            invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ],
            instance: $instance:expr,
            error: $error:expr
            $(,)*
    ) => {
        #[test]
        #[allow(non_snake_case, unused)]
        fn $test_name() {
            use crate::{
                interpreter::{
                    instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView},
                    stack::Stackable,
                    Instruction, Interpreter,
                },
                types::IType,
                values::IValue,
            };
            use std::{cell::Cell, collections::HashMap, convert::TryInto};

            let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> =
                (&vec![$($instructions),*]).try_into().unwrap();

            let invocation_inputs = vec![$($invocation_inputs),*];
            let mut instance = $instance;
            let run = interpreter.run(&invocation_inputs, &mut instance);

            assert!(run.is_err());

            let error = run.unwrap_err().to_string();

            assert_eq!(error, String::from($error));
        }
    };
}