roan_engine/vm/
native_fn.rs

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
use crate::value::Value;
use anyhow::Result;
use std::{
    fmt,
    fmt::{Display, Formatter},
};
use tracing::debug;

#[derive(Debug, Clone)]
pub struct NativeFunctionParam {
    pub name: String,
    pub ty: String,
    pub is_rest: bool,
}

#[derive(Debug, Clone)]
pub struct NativeFunction {
    pub name: String,
    pub func: fn(args: Vec<Value>) -> Value,
    pub params: Vec<NativeFunctionParam>,
}

impl NativeFunction {
    pub fn new(
        name: impl Into<String>,
        params: Vec<NativeFunctionParam>,
        func: fn(args: Vec<Value>) -> Value,
    ) -> Self {
        Self {
            name: name.into(),
            func,
            params,
        }
    }

    pub fn call(&mut self, args: Vec<Value>) -> Result<Value> {
        debug!(
            "Executing native function: {} with {:?} args",
            self.name,
            args.len()
        );

        let mut params = vec![];
        for (param, val) in self.params.iter().zip(args.clone()) {
            if param.is_rest {
                let rest: Vec<Value> = args.iter().skip(self.params.len() - 1).cloned().collect();

                params.extend(rest);
            } else {
                params.push(val);
            }
        }

        Ok((self.func)(params))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{as_cast, native_function, value::Value};

    native_function!(fn __add_str(str1, str2) {
        let str1 = as_cast!(str1, String);
        let str2 = as_cast!(str2, String);

        Value::String(format!("{}{}", str1, str2))
    });

    #[test]
    fn test_native_function() {
        assert_eq!(
            __add_str()
                .call(vec![
                    Value::String("Hello".to_string()),
                    Value::String("World".to_string())
                ])
                .unwrap(),
            Value::String("HelloWorld".to_string())
        );
    }

    native_function!(fn __test1(a, b) {
        assert_eq!(a, Value::Int(1));
        assert_eq!(b, Value::Int(2));
        Value::Null
    });

    #[test]
    fn test_native_function_with_params() {
        assert_eq!(
            __test1().call(vec![Value::Int(1), Value::Int(2)]).unwrap(),
            Value::Null
        );
    }

    native_function!(fn __test(a, b, ...rest) {
        assert_eq!(a, Value::Int(1));
        assert_eq!(b, Value::Int(2));
        assert_eq!(rest[0], Value::Int(3));
        Value::Null
    });

    #[test]
    fn test_native_function_with_rest_param() {
        assert_eq!(
            __test()
                .call(vec![
                    Value::Int(1),
                    Value::Int(2),
                    Value::Int(3),
                    Value::Int(4)
                ])
                .unwrap(),
            Value::Null
        );
    }
}

impl Display for NativeFunction {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "<native fn {}>", self.name)
    }
}