micro_games_kit/
scripting.rs1use 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}