intuicio_frontend_vault/
lib.rs

1use intuicio_core::{
2    IntuicioVersion, Visibility,
3    context::Context,
4    crate_version,
5    function::{FunctionQuery, FunctionQueryParameter},
6    registry::Registry,
7    script::{
8        BytesContentParser, ScriptContentProvider, ScriptEnum, ScriptEnumVariant, ScriptExpression,
9        ScriptFunction, ScriptFunctionParameter, ScriptFunctionSignature, ScriptHandle,
10        ScriptModule, ScriptOperation, ScriptPackage, ScriptStruct, ScriptStructField,
11    },
12    types::TypeQuery,
13};
14use serde::{Deserialize, Serialize};
15use std::{collections::HashMap, error::Error};
16
17pub fn frontend_vault_version() -> IntuicioVersion {
18    crate_version!()
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub enum VaultLiteral {
23    Unit,
24    Bool(bool),
25    I8(i8),
26    I16(i16),
27    I32(i32),
28    I64(i64),
29    I128(i128),
30    Isize(isize),
31    U8(u8),
32    U16(u16),
33    U32(u32),
34    U64(u64),
35    U128(u128),
36    Usize(usize),
37    F32(f32),
38    F64(f64),
39    Char(char),
40    String(String),
41}
42
43impl VaultLiteral {
44    fn evaluate(&self, context: &mut Context) {
45        match self {
46            Self::Unit => context.stack().push(()),
47            Self::Bool(value) => context.stack().push(*value),
48            Self::I8(value) => context.stack().push(*value),
49            Self::I16(value) => context.stack().push(*value),
50            Self::I32(value) => context.stack().push(*value),
51            Self::I64(value) => context.stack().push(*value),
52            Self::I128(value) => context.stack().push(*value),
53            Self::Isize(value) => context.stack().push(*value),
54            Self::U8(value) => context.stack().push(*value),
55            Self::U16(value) => context.stack().push(*value),
56            Self::U32(value) => context.stack().push(*value),
57            Self::U64(value) => context.stack().push(*value),
58            Self::U128(value) => context.stack().push(*value),
59            Self::Usize(value) => context.stack().push(*value),
60            Self::F32(value) => context.stack().push(*value),
61            Self::F64(value) => context.stack().push(*value),
62            Self::Char(value) => context.stack().push(*value),
63            Self::String(value) => context.stack().push(value.to_owned()),
64        };
65    }
66}
67
68#[derive(Debug)]
69pub enum VaultScriptExpression {
70    Literal(VaultLiteral),
71    StackDrop,
72    StackProduce { name: String },
73}
74
75impl ScriptExpression for VaultScriptExpression {
76    fn evaluate(&self, context: &mut Context, registry: &Registry) {
77        match self {
78            Self::Literal(literal) => {
79                literal.evaluate(context);
80            }
81            Self::StackDrop => {
82                context.stack().drop();
83            }
84            Self::StackProduce { name } => {
85                let type_hash = context.stack().peek().unwrap();
86                registry
87                    .find_function(FunctionQuery {
88                        name: Some(name.into()),
89                        type_query: Some(TypeQuery {
90                            type_hash: Some(type_hash),
91                            ..Default::default()
92                        }),
93                        inputs: [FunctionQueryParameter {
94                            type_query: Some(TypeQuery {
95                                type_hash: Some(type_hash),
96                                ..Default::default()
97                            }),
98                            ..Default::default()
99                        }]
100                        .as_slice()
101                        .into(),
102                        ..Default::default()
103                    })
104                    .unwrap()
105                    .invoke(context, registry);
106            }
107        }
108    }
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub enum VaultExpression {
113    DefineVariable {
114        name: String,
115    },
116    TakeVariable {
117        name: String,
118    },
119    CloneVariable {
120        name: String,
121    },
122    VariableRef {
123        name: String,
124    },
125    VariableRefMut {
126        name: String,
127    },
128    Literal(VaultLiteral),
129    CallFunction {
130        #[serde(default, skip_serializing_if = "Option::is_none")]
131        module_name: Option<String>,
132        name: String,
133        #[serde(default, skip_serializing_if = "Vec::is_empty")]
134        arguments: Vec<VaultExpression>,
135    },
136    CallMethod {
137        #[serde(default, skip_serializing_if = "Option::is_none")]
138        module_name: Option<String>,
139        type_name: String,
140        name: String,
141        #[serde(default, skip_serializing_if = "Vec::is_empty")]
142        arguments: Vec<VaultExpression>,
143    },
144    If {
145        condition: Box<VaultExpression>,
146        success: Vec<VaultStatement>,
147        #[serde(default, skip_serializing_if = "Option::is_none")]
148        failure: Option<Vec<VaultStatement>>,
149    },
150}
151
152impl VaultExpression {
153    pub fn compile(
154        &self,
155        result: &mut Vec<ScriptOperation<VaultScriptExpression>>,
156        registers: &mut Vec<String>,
157    ) {
158        match self {
159            Self::DefineVariable { name } => {
160                if let Some(item) = registers.iter_mut().find(|n| n == &name) {
161                    item.clone_from(name);
162                } else {
163                    registers.push(name.to_owned());
164                }
165            }
166            Self::TakeVariable { name } => {
167                result.push(ScriptOperation::PushFromRegister {
168                    index: registers.iter().position(|n| n == name.as_str()).unwrap(),
169                });
170            }
171            Self::CloneVariable { name } => {
172                result.push(ScriptOperation::PushFromRegister {
173                    index: registers.iter().position(|n| n == name.as_str()).unwrap(),
174                });
175                result.push(ScriptOperation::Expression {
176                    expression: VaultScriptExpression::StackProduce {
177                        name: "clone".to_owned(),
178                    },
179                });
180                result.push(ScriptOperation::PopToRegister {
181                    index: registers.iter().position(|n| n == name.as_str()).unwrap(),
182                });
183            }
184            Self::VariableRef { name } => {
185                result.push(ScriptOperation::PushFromRegister {
186                    index: registers.iter().position(|n| n == name.as_str()).unwrap(),
187                });
188                result.push(ScriptOperation::Expression {
189                    expression: VaultScriptExpression::StackProduce {
190                        name: "ref".to_owned(),
191                    },
192                });
193                result.push(ScriptOperation::PopToRegister {
194                    index: registers.iter().position(|n| n == name.as_str()).unwrap(),
195                });
196            }
197            Self::VariableRefMut { name } => {
198                result.push(ScriptOperation::PushFromRegister {
199                    index: registers.iter().position(|n| n == name.as_str()).unwrap(),
200                });
201                result.push(ScriptOperation::Expression {
202                    expression: VaultScriptExpression::StackProduce {
203                        name: "ref_mut".to_owned(),
204                    },
205                });
206                result.push(ScriptOperation::PopToRegister {
207                    index: registers.iter().position(|n| n == name.as_str()).unwrap(),
208                });
209            }
210            Self::Literal(literal) => {
211                result.push(ScriptOperation::Expression {
212                    expression: VaultScriptExpression::Literal(literal.to_owned()),
213                });
214            }
215            Self::CallFunction {
216                module_name,
217                name,
218                arguments,
219            } => {
220                for argument in arguments.iter().rev() {
221                    argument.compile(result, registers);
222                }
223                result.push(ScriptOperation::CallFunction {
224                    query: FunctionQuery {
225                        name: Some(name.to_owned().into()),
226                        module_name: module_name.as_ref().map(|name| name.to_owned().into()),
227                        ..Default::default()
228                    },
229                });
230            }
231            Self::CallMethod {
232                module_name,
233                type_name,
234                name,
235                arguments,
236            } => {
237                for argument in arguments.iter().rev() {
238                    argument.compile(result, registers);
239                }
240                result.push(ScriptOperation::CallFunction {
241                    query: FunctionQuery {
242                        name: Some(name.to_owned().into()),
243                        type_query: Some(TypeQuery {
244                            name: Some(type_name.to_owned().into()),
245                            ..Default::default()
246                        }),
247                        module_name: module_name.as_ref().map(|name| name.to_owned().into()),
248                        ..Default::default()
249                    },
250                });
251            }
252            Self::If {
253                condition,
254                success,
255                failure,
256            } => {
257                condition.compile(result, registers);
258                let mut success_operations = vec![];
259                for statement in success {
260                    statement.compile(&mut success_operations, registers);
261                }
262                let failure_handle = failure.as_ref().map(|failure| {
263                    let mut operations = vec![];
264                    for statement in failure {
265                        statement.compile(&mut operations, registers);
266                    }
267                    ScriptHandle::new(operations)
268                });
269                result.push(ScriptOperation::BranchScope {
270                    scope_success: ScriptHandle::new(success_operations),
271                    scope_failure: failure_handle,
272                });
273            }
274        }
275    }
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub enum VaultStatement {
280    MakeVariable {
281        name: String,
282        expression: VaultExpression,
283    },
284    Expression(VaultExpression),
285    Return(VaultExpression),
286    Scope(Vec<VaultStatement>),
287    While {
288        condition: Box<VaultExpression>,
289        statements: Vec<VaultStatement>,
290    },
291    For {
292        setup: Vec<VaultStatement>,
293        condition: Box<VaultExpression>,
294        advancement: Vec<VaultStatement>,
295        statements: Vec<VaultStatement>,
296    },
297}
298
299impl VaultStatement {
300    pub fn compile(
301        &self,
302        result: &mut Vec<ScriptOperation<VaultScriptExpression>>,
303        registers: &mut Vec<String>,
304    ) {
305        match self {
306            Self::MakeVariable { name, expression } => {
307                expression.compile(result, registers);
308                result.push(ScriptOperation::PopToRegister {
309                    index: registers.iter().position(|n| n == name.as_str()).unwrap(),
310                });
311            }
312            Self::Expression(expression) => {
313                expression.compile(result, registers);
314                result.push(ScriptOperation::Expression {
315                    expression: VaultScriptExpression::StackDrop,
316                });
317            }
318            Self::Return(expression) => {
319                expression.compile(result, registers);
320                result.push(ScriptOperation::Expression {
321                    expression: VaultScriptExpression::Literal(VaultLiteral::Bool(false)),
322                });
323                result.push(ScriptOperation::ContinueScopeConditionally);
324            }
325            Self::Scope(expressions) => {
326                let mut operations = vec![];
327                for statement in expressions {
328                    statement.compile(&mut operations, registers);
329                }
330                result.push(ScriptOperation::PushScope {
331                    scope: ScriptHandle::new(operations),
332                });
333            }
334            Self::While {
335                condition,
336                statements,
337            } => {
338                let mut operations = vec![];
339                condition.compile(&mut operations, registers);
340                operations.push(ScriptOperation::ContinueScopeConditionally);
341                for statement in statements {
342                    statement.compile(&mut operations, registers);
343                }
344                result.push(ScriptOperation::LoopScope {
345                    scope: ScriptHandle::new(operations),
346                });
347            }
348            Self::For {
349                setup,
350                condition,
351                advancement,
352                statements,
353            } => {
354                for statement in setup {
355                    statement.compile(result, registers);
356                }
357                let mut operations = vec![];
358                condition.compile(&mut operations, registers);
359                operations.push(ScriptOperation::ContinueScopeConditionally);
360                for statement in statements {
361                    statement.compile(&mut operations, registers);
362                }
363                for statement in advancement {
364                    statement.compile(result, registers);
365                }
366                result.push(ScriptOperation::LoopScope {
367                    scope: ScriptHandle::new(operations),
368                });
369            }
370        }
371    }
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct VaultFunctionParameter {
376    pub name: String,
377    pub arg_type: String,
378}
379
380impl VaultFunctionParameter {
381    pub fn build(&self) -> ScriptFunctionParameter<'static> {
382        ScriptFunctionParameter {
383            meta: None,
384            name: self.name.to_owned(),
385            type_query: TypeQuery {
386                name: Some(self.arg_type.as_str().to_owned().into()),
387                ..Default::default()
388            },
389        }
390    }
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
394pub struct VaultFunction {
395    pub name: String,
396    #[serde(default, skip_serializing_if = "Vec::is_empty")]
397    pub arguments: Vec<VaultFunctionParameter>,
398    #[serde(default, skip_serializing_if = "Option::is_none")]
399    pub return_type: Option<String>,
400    pub statements: Vec<VaultStatement>,
401}
402
403impl VaultFunction {
404    pub fn compile(
405        &self,
406        module_name: &str,
407        type_query: Option<TypeQuery<'static>>,
408    ) -> ScriptFunction<'static, VaultScriptExpression> {
409        let signature = ScriptFunctionSignature {
410            meta: None,
411            name: self.name.to_owned(),
412            module_name: module_name.to_owned().into(),
413            type_query,
414            visibility: Visibility::Public,
415            inputs: self.arguments.iter().map(|input| input.build()).collect(),
416            outputs: vec![ScriptFunctionParameter {
417                meta: None,
418                name: "result".to_owned(),
419                type_query: if let Some(return_type) = &self.return_type {
420                    TypeQuery {
421                        name: Some(return_type.to_owned().into()),
422                        ..Default::default()
423                    }
424                } else {
425                    TypeQuery::of::<()>()
426                },
427            }],
428        };
429        let mut registers = Vec::<String>::new();
430        let mut operations = vec![];
431        for argument in &self.arguments {
432            if let Some(item) = registers.iter_mut().find(|n| *n == &argument.name) {
433                item.clone_from(&argument.name);
434            } else {
435                registers.push(argument.name.to_owned());
436            }
437            operations.push(ScriptOperation::DefineRegister {
438                query: TypeQuery {
439                    name: Some(argument.arg_type.to_owned().into()),
440                    ..Default::default()
441                },
442            });
443            operations.push(ScriptOperation::PopToRegister {
444                index: registers.iter().position(|n| n == &argument.name).unwrap(),
445            });
446        }
447        for statement in &self.statements {
448            statement.compile(&mut operations, &mut registers);
449        }
450        ScriptFunction {
451            signature,
452            script: ScriptHandle::new(operations),
453        }
454    }
455}
456
457#[derive(Debug, Clone, Serialize, Deserialize)]
458pub struct VaultStructField {
459    pub name: String,
460    pub type_name: String,
461}
462
463impl VaultStructField {
464    pub fn build(&self) -> ScriptStructField<'static> {
465        ScriptStructField {
466            meta: None,
467            name: self.name.to_owned(),
468            visibility: Visibility::Public,
469            type_query: TypeQuery {
470                name: Some(self.type_name.as_str().to_owned().into()),
471                ..Default::default()
472            },
473        }
474    }
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize)]
478pub struct VaultStruct {
479    pub name: String,
480    #[serde(default, skip_serializing_if = "Vec::is_empty")]
481    pub fields: Vec<VaultStructField>,
482    #[serde(default, skip_serializing_if = "Vec::is_empty")]
483    pub methods: Vec<VaultFunction>,
484}
485
486impl VaultStruct {
487    pub fn compile_struct(&self, module_name: &str) -> ScriptStruct<'static> {
488        ScriptStruct {
489            meta: None,
490            name: self.name.to_owned(),
491            module_name: Some(module_name.to_owned()),
492            visibility: Visibility::Public,
493            fields: self.fields.iter().map(|field| field.build()).collect(),
494        }
495    }
496
497    pub fn compile_methods(
498        &self,
499        module_name: &str,
500    ) -> Vec<ScriptFunction<'static, VaultScriptExpression>> {
501        let type_query = TypeQuery {
502            name: Some(self.name.as_str().to_owned().into()),
503            module_name: Some(module_name.to_owned().into()),
504            ..Default::default()
505        };
506        self.methods
507            .iter()
508            .map(|method| method.compile(module_name, Some(type_query.clone())))
509            .collect()
510    }
511}
512
513#[derive(Debug, Clone, Serialize, Deserialize)]
514pub struct VaultEnumVariant {
515    pub name: String,
516    pub fields: Vec<VaultStructField>,
517}
518
519impl VaultEnumVariant {
520    pub fn build(&self) -> ScriptEnumVariant<'static> {
521        ScriptEnumVariant {
522            meta: None,
523            name: self.name.to_owned(),
524            fields: self.fields.iter().map(|field| field.build()).collect(),
525            discriminant: None,
526        }
527    }
528}
529
530#[derive(Debug, Clone, Serialize, Deserialize)]
531pub struct VaultEnum {
532    pub name: String,
533    #[serde(default, skip_serializing_if = "Vec::is_empty")]
534    pub variants: Vec<VaultEnumVariant>,
535    #[serde(default, skip_serializing_if = "Vec::is_empty")]
536    pub methods: Vec<VaultFunction>,
537}
538
539impl VaultEnum {
540    pub fn compile_enum(&self, module_name: &str) -> ScriptEnum<'static> {
541        ScriptEnum {
542            meta: None,
543            name: self.name.to_owned(),
544            module_name: Some(module_name.to_owned()),
545            visibility: Visibility::Public,
546            variants: self.variants.iter().map(|field| field.build()).collect(),
547            default_variant: None,
548        }
549    }
550
551    pub fn compile_methods(
552        &self,
553        module_name: &str,
554    ) -> Vec<ScriptFunction<'static, VaultScriptExpression>> {
555        let type_query = TypeQuery {
556            name: Some(self.name.as_str().to_owned().into()),
557            module_name: Some(module_name.to_owned().into()),
558            ..Default::default()
559        };
560        self.methods
561            .iter()
562            .map(|method| method.compile(module_name, Some(type_query.clone())))
563            .collect()
564    }
565}
566
567#[derive(Debug, Clone, Serialize, Deserialize)]
568pub enum VaultDefinition {
569    Function(VaultFunction),
570    Struct(VaultStruct),
571    Enum(VaultEnum),
572}
573
574#[derive(Debug, Clone, Serialize, Deserialize)]
575pub struct VaultModule {
576    pub name: String,
577    #[serde(default, skip_serializing_if = "Vec::is_empty")]
578    pub dependencies: Vec<String>,
579    #[serde(default, skip_serializing_if = "Vec::is_empty")]
580    pub definitions: Vec<VaultDefinition>,
581}
582
583impl VaultModule {
584    pub fn parse(content: &str) -> Result<Self, serde_lexpr::Error> {
585        serde_lexpr::from_str(content)
586    }
587
588    pub fn compile(&self) -> ScriptModule<'static, VaultScriptExpression> {
589        ScriptModule {
590            name: self.name.to_owned(),
591            structs: self
592                .definitions
593                .iter()
594                .filter_map(|definition| match definition {
595                    VaultDefinition::Struct(struct_type) => {
596                        Some(struct_type.compile_struct(&self.name))
597                    }
598                    _ => None,
599                })
600                .collect(),
601            enums: self
602                .definitions
603                .iter()
604                .filter_map(|definition| match definition {
605                    VaultDefinition::Enum(enum_type) => Some(enum_type.compile_enum(&self.name)),
606                    _ => None,
607                })
608                .collect(),
609            functions: self
610                .definitions
611                .iter()
612                .filter_map(|definition| match definition {
613                    VaultDefinition::Function(function) => Some(function.compile(&self.name, None)),
614                    _ => None,
615                })
616                .chain(
617                    self.definitions
618                        .iter()
619                        .filter_map(|definition| match definition {
620                            VaultDefinition::Struct(struct_type) => {
621                                Some(struct_type.compile_methods(&self.name))
622                            }
623                            VaultDefinition::Enum(enum_type) => {
624                                Some(enum_type.compile_methods(&self.name))
625                            }
626                            _ => None,
627                        })
628                        .flatten(),
629                )
630                .collect(),
631        }
632    }
633}
634
635#[derive(Default)]
636pub struct VaultPackage {
637    pub modules: HashMap<String, VaultModule>,
638}
639
640impl VaultPackage {
641    pub fn new<CP>(path: &str, content_provider: &mut CP) -> Result<Self, Box<dyn Error>>
642    where
643        CP: ScriptContentProvider<VaultModule>,
644    {
645        let mut result = Self::default();
646        result.load(path, content_provider)?;
647        Ok(result)
648    }
649
650    pub fn load<CP>(&mut self, path: &str, content_provider: &mut CP) -> Result<(), Box<dyn Error>>
651    where
652        CP: ScriptContentProvider<VaultModule>,
653    {
654        let path = content_provider.sanitize_path(path)?;
655        if self.modules.contains_key(&path) {
656            return Ok(());
657        }
658        for content in content_provider.unpack_load(&path)? {
659            if let Some(module) = content.data? {
660                let dependencies = module.dependencies.to_owned();
661                self.modules.insert(content.name, module);
662                for relative in dependencies {
663                    let path = content_provider.join_paths(&content.path, &relative)?;
664                    self.load(&path, content_provider)?;
665                }
666            }
667        }
668        Ok(())
669    }
670
671    pub fn compile(&self) -> ScriptPackage<'static, VaultScriptExpression> {
672        ScriptPackage {
673            modules: self
674                .modules
675                .values()
676                .map(|module| module.compile())
677                .collect(),
678        }
679    }
680}
681
682pub struct VaultContentParser;
683
684impl BytesContentParser<VaultModule> for VaultContentParser {
685    fn parse(&self, bytes: Vec<u8>) -> Result<VaultModule, Box<dyn Error>> {
686        let content = String::from_utf8(bytes)?;
687        Ok(VaultModule::parse(&content)?)
688    }
689}
690
691#[macro_export]
692macro_rules! define_vault_function {
693    (
694        $registry:expr
695        =>
696        $(mod $module_name:ident)?
697        fn
698        $name:ident
699        ($( $argument_name:ident : $argument_type:ty),*)
700        ->
701        $return_type:ty
702        $code:block
703    ) => {
704        intuicio_core::function::Function::new(
705            intuicio_core::function_signature! {
706                $registry
707                =>
708                $(mod $module_name)?
709                fn
710                $name
711                ($($argument_name : $argument_type),*)
712                ->
713                (result : $return_type)
714            },
715            intuicio_core::function::FunctionBody::closure(move |context, #[allow(unused_variables)] registry| {
716                use intuicio_data::data_stack::DataStackPack;
717                #[allow(unused_mut)]
718                let ($(mut $argument_name,)*) = <($($argument_type,)*)>::stack_pop(context.stack());
719                context.stack().push($code);
720            }),
721        )
722    };
723}
724
725#[macro_export]
726macro_rules! define_vault_method {
727    (
728        $registry:expr
729        =>
730        $(mod $module_name:ident)?
731        type ($type:ty)
732        fn
733        $name:ident
734        ($( $argument_name:ident : $argument_type:ty),*)
735        ->
736        $return_type:ty
737        $code:block
738    ) => {
739        intuicio_core::function::Function::new(
740            intuicio_core::function_signature! {
741                $registry
742                =>
743                $(mod $module_name)?
744                type ($type)
745                fn
746                $name
747                ($($argument_name : $argument_type),*)
748                ->
749                (result : $return_type)
750            },
751            intuicio_core::function::FunctionBody::closure(move |context, registry| {
752                use intuicio_data::data_stack::DataStackPack;
753                #[allow(unused_mut)]
754                let ($(mut $argument_name,)*) = <($($argument_type,)*)>::stack_pop(context.stack());
755                context.stack().push($code);
756            }),
757        )
758    };
759}
760
761#[cfg(test)]
762mod tests {
763    use super::*;
764    use intuicio_backend_vm::scope::VmScope;
765    use intuicio_core::{define_function, host::Host, script::FileContentProvider};
766
767    #[test]
768    fn test_vault_script() {
769        let mut registry = Registry::default().with_basic_types();
770        registry.add_function(define_vault_function! {
771            registry => mod intrinsics fn print(content: String) -> () {
772                println!("PRINT: {content}");
773            }
774        });
775        registry.add_function(define_vault_function! {
776            registry => mod intrinsics fn add(a: usize, b: usize) -> usize {
777                a + b
778            }
779        });
780        registry.add_function(define_vault_function! {
781            registry => mod intrinsics fn sub(a: usize, b: usize) -> usize {
782                a - b
783            }
784        });
785        registry.add_function(define_vault_function! {
786            registry => mod intrinsics fn less_than(a: usize, b: usize) -> bool {
787                a < b
788            }
789        });
790        registry.add_function(define_function! {
791            registry => mod intrinsics type (usize) fn clone(this: usize) -> (original: usize, clone: usize) {
792                (this, this)
793            }
794        });
795        let mut content_provider = FileContentProvider::new("vault", VaultContentParser);
796        VaultPackage::new("../../resources/package.vault", &mut content_provider)
797            .unwrap()
798            .compile()
799            .install::<VmScope<VaultScriptExpression>>(
800                &mut registry,
801                None,
802                // Some(
803                //     PrintDebugger::full()
804                //         .basic_printables()
805                //         .stack_bytes(false)
806                //         .registers_bytes(false)
807                //         .into_handle(),
808                // ),
809            );
810        let mut vm = Host::new(Context::new(10240, 10240), registry.into());
811        let (result,) = vm
812            .call_function::<(usize,), _>("main", "test", None)
813            .unwrap()
814            .run(());
815        assert_eq!(vm.context().stack().position(), 0);
816        assert_eq!(result, 42);
817        let (result,) = vm
818            .call_function::<(usize,), (usize,)>("fib", "test", None)
819            .unwrap()
820            .run((20,));
821        assert_eq!(vm.context().stack().position(), 0);
822        assert_eq!(result, 6765);
823    }
824}