micro_games_kit/
scripting.rs

1use intuicio_backend_vm::scope::VmScope;
2use intuicio_core::{context::Context, host::Host, registry::Registry};
3use intuicio_frontend_simpleton::{
4    library,
5    script::{SimpletonModule, SimpletonPackage, SimpletonScriptExpression},
6    Reference, Type,
7};
8use serde::{Deserialize, Serialize};
9
10#[macro_export]
11macro_rules! script_contents {
12    ( $const_name:ident => $($path:literal),* ) => {
13        const $const_name: &[&str] = &[ $( include_str!($path) ),* ];
14    };
15}
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub struct ScriptingConfig {
19    pub stack_capacity: usize,
20    pub registers_capacity: usize,
21}
22
23impl Default for ScriptingConfig {
24    fn default() -> Self {
25        Self {
26            stack_capacity: 10240,
27            registers_capacity: 10240,
28        }
29    }
30}
31
32pub fn create_host(
33    config: ScriptingConfig,
34    script_contents: &[&str],
35    registry_setup: impl IntoIterator<Item = fn(&mut Registry)>,
36) {
37    let package = SimpletonPackage {
38        modules: script_contents
39            .iter()
40            .enumerate()
41            .map(|(index, content)| (index.to_string(), SimpletonModule::parse(content).unwrap()))
42            .collect(),
43    };
44    let mut registry = Registry::default();
45    library::install(&mut registry);
46    for setup in registry_setup.into_iter() {
47        (setup)(&mut registry);
48    }
49    package
50        .compile()
51        .install::<VmScope<SimpletonScriptExpression>>(&mut registry, None);
52    let context = Context::new(config.stack_capacity, config.registers_capacity);
53    let host = Host::new(context, registry.into());
54    if host.push_global().is_err() {
55        panic!("Could not make global scripting host!");
56    }
57}
58
59pub fn destroy_host() {
60    Host::pop_global();
61}
62
63pub fn call_function(name: &str, module_name: &str, args: &[Reference]) -> Reference {
64    Host::with_global(|host| {
65        let Some(handle) = host.find_function(name, module_name, None) else {
66            return Reference::null();
67        };
68        let (context, registry) = host.context_and_registry();
69        for arg in args.iter().rev() {
70            context.stack().push(arg.clone());
71        }
72        handle.invoke(context, registry);
73        context.stack().pop().unwrap_or_default()
74    })
75    .unwrap_or_default()
76}
77
78pub fn call_object(object: Reference, name: &str, args: &[Reference]) -> Reference {
79    Host::with_global(move |host| {
80        let Some(ty) = object.type_of() else {
81            return Reference::null();
82        };
83        let Some(ty) = ty.handle() else {
84            return Reference::null();
85        };
86        let Some(handle) = host.find_function(name, ty.module_name().unwrap_or_default(), None)
87        else {
88            return Reference::null();
89        };
90        let (context, registry) = host.context_and_registry();
91        for arg in args.iter().rev() {
92            context.stack().push(arg.clone());
93        }
94        context.stack().push(object);
95        handle.invoke(context, registry);
96        context.stack().pop().unwrap_or_default()
97    })
98    .unwrap_or_default()
99}
100
101pub fn new(type_name: &str, module_name: &str) -> Reference {
102    new_init(type_name, module_name, &[])
103}
104
105pub fn new_init(type_name: &str, module_name: &str, properties: &[(&str, Reference)]) -> Reference {
106    Host::with_global(move |host| {
107        let ty = Reference::new_type(
108            Type::by_name(type_name, module_name, host.registry()).unwrap(),
109            host.registry(),
110        );
111        let properties = Reference::new_map(
112            properties
113                .iter()
114                .map(|(name, value)| ((*name).to_owned(), value.clone()))
115                .collect(),
116            host.registry(),
117        );
118        library::reflect::new(ty, properties)
119    })
120    .unwrap_or_default()
121}
122
123pub fn new_typed<T: 'static>(value: T) -> Reference {
124    Host::with_global(move |host| Reference::new(value, host.registry())).unwrap_or_default()
125}
126
127pub fn get(object: Reference, field: &str) -> Reference {
128    object
129        .read_object()
130        .unwrap()
131        .read_field::<Reference>(field)
132        .unwrap()
133        .clone()
134}
135
136pub fn set(mut object: Reference, field: &str, value: Reference) {
137    *object
138        .write_object()
139        .unwrap()
140        .write_field::<Reference>(field)
141        .unwrap() = value;
142}