roan_engine/natives/
mod.rs

1use crate::{
2    module::StoredFunction,
3    natives::{
4        debug::{__eprint, __format, __print},
5        process::{__abort, __exit, __pid},
6    },
7    value::Value,
8    vm::native_fn::{NativeFunction, NativeFunctionParam},
9};
10use std::{panic, panic::panic_any};
11
12pub mod debug;
13mod process;
14
15#[macro_export]
16macro_rules! native_function {
17    (fn $name:ident($($arg:ident),* $(, ...$rest:ident)?) {$($body:tt)*}) => {
18        #[allow(unused_mut, unused_variables)]
19        pub fn $name() -> NativeFunction {
20            NativeFunction {
21                name: stringify!($name).to_string(),
22                func: |args| {
23                    let mut args_iter = args.into_iter();
24                    $(
25                        let $arg = match args_iter.next() {
26                            Some(value) => value,
27                            None => panic!("Expected argument but got None"),
28                        };
29                    )*
30
31                    $(
32                        let $rest = args_iter.collect::<Vec<Value>>();
33                    )?
34
35                    $($body)*
36                },
37                params: vec![
38                    $(
39                        NativeFunctionParam {
40                            name: stringify!($arg).to_string(),
41                            ty: "Value".to_string(),
42                            is_rest: false,
43                        },
44                    )*
45                    $(
46                        NativeFunctionParam {
47                            name: stringify!($rest).to_string(),
48                            ty: "Vec<Value>".to_string(),
49                            is_rest: true,
50                        },
51                    )?
52                ],
53            }
54        }
55    };
56}
57
58#[macro_export]
59macro_rules! as_cast {
60    ($val:expr, $ty:ident) => {
61        match $val {
62            Value::$ty(val) => val,
63            // TODO: Replace with throw! macro
64            _ => panic!("Expected {} but got {:?}", stringify!($ty), $val),
65        }
66    };
67}
68
69native_function!(
70    fn type_of(value) {
71        Value::String(value.type_name())
72    }
73);
74
75native_function!(
76    fn __panic(msg) {
77        let msg = as_cast!(msg, String);
78
79        // Save panic hook to restore it later
80        let old_hook = std::panic::take_hook();
81
82        panic::set_hook(Box::new(|panic_info| {
83            let payload = panic_info.payload().downcast_ref::<String>().unwrap();
84            eprintln!("program panicked");
85            eprintln!("{}", payload);
86        }));
87
88        panic_any(msg);
89
90        // Restore the original hook afterward if needed.
91        panic::set_hook(old_hook);
92
93        Value::Void
94    }
95);
96
97pub fn get_stored_function() -> Vec<StoredFunction> {
98    vec![
99        __print(),
100        __format(),
101        __eprint(),
102        __exit(),
103        __abort(),
104        __pid(),
105        type_of(),
106        __panic(),
107    ]
108    .into_iter()
109    .map(|f| StoredFunction::Native(f))
110    .collect()
111}