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
mod blocks;

/// Primitives for the **Array** class.
pub mod array;
/// Primitives for the **Class** class.
pub mod class;
/// Primitives for the **Double** class.
pub mod double;
/// Primitives for the **Integer** class.
pub mod integer;
/// Primitives for the **Method** class and the **Primitive** class.
pub mod method;
/// Primitives for the **Object** class.
pub mod object;
/// Primitives for the **String** class.
pub mod string;
/// Primitives for the **Symbol** class.
pub mod symbol;
/// Primitives for the **System** class.
pub mod system;

pub use self::blocks::{block1, block2, block3};

use crate::invokable::Return;
use crate::universe::Universe;
use crate::value::Value;

/// A interpreter primitive (just a bare function pointer).
pub type PrimitiveFn = fn(universe: &mut Universe, args: Vec<Value>) -> Return;

/// Macro for checking and destructuring arguments passed to primitives.
#[macro_export]
macro_rules! expect_args {
    ($signature:expr, $args:expr, [ $( $ptrn:pat $( => $name:ident )? ),* $(,)? ]) => {
        #[allow(unused_mut)]
        let ($($(mut $name,)?)*) = {
            #[allow(unused_variables, unused_mut)]
            let mut iter = $args.into_iter();
            $(#[allow(unreachable_patterns)]
            $(let $name =)? match iter.next() {
                Some($ptrn) => {$($name)?},
                Some(_) => return Return::Exception(format!("'{}': wrong type", $signature)),
                None => return Return::Exception(format!("'{}': missing argument", $signature)),
            };)*
            ($($($name,)?)*)
        };
    };
}

pub fn get_class_primitives(
    class_name: &str,
) -> Option<&'static [(&'static str, PrimitiveFn, bool)]> {
    match class_name {
        "Array" => Some(self::array::CLASS_PRIMITIVES),
        "Block1" => Some(self::block1::CLASS_PRIMITIVES),
        "Block2" => Some(self::block2::CLASS_PRIMITIVES),
        "Block3" => Some(self::block3::CLASS_PRIMITIVES),
        "Class" => Some(self::class::CLASS_PRIMITIVES),
        "Double" => Some(self::double::CLASS_PRIMITIVES),
        "Integer" => Some(self::integer::CLASS_PRIMITIVES),
        "Method" => Some(self::method::CLASS_PRIMITIVES),
        "Primitive" => Some(self::method::CLASS_PRIMITIVES),
        "Object" => Some(self::object::CLASS_PRIMITIVES),
        "String" => Some(self::string::CLASS_PRIMITIVES),
        "Symbol" => Some(self::symbol::CLASS_PRIMITIVES),
        "System" => Some(self::system::CLASS_PRIMITIVES),
        _ => None,
    }
}

pub fn get_instance_primitives(
    class_name: &str,
) -> Option<&'static [(&'static str, PrimitiveFn, bool)]> {
    match class_name {
        "Array" => Some(self::array::INSTANCE_PRIMITIVES),
        "Block1" => Some(self::block1::INSTANCE_PRIMITIVES),
        "Block2" => Some(self::block2::INSTANCE_PRIMITIVES),
        "Block3" => Some(self::block3::INSTANCE_PRIMITIVES),
        "Class" => Some(self::class::INSTANCE_PRIMITIVES),
        "Double" => Some(self::double::INSTANCE_PRIMITIVES),
        "Integer" => Some(self::integer::INSTANCE_PRIMITIVES),
        "Method" => Some(self::method::INSTANCE_PRIMITIVES),
        "Primitive" => Some(self::method::INSTANCE_PRIMITIVES),
        "Object" => Some(self::object::INSTANCE_PRIMITIVES),
        "String" => Some(self::string::INSTANCE_PRIMITIVES),
        "Symbol" => Some(self::symbol::INSTANCE_PRIMITIVES),
        "System" => Some(self::system::INSTANCE_PRIMITIVES),
        _ => None,
    }
}