intuicio_backend_rust/
host.rs

1use intuicio_core::{
2    context::Context,
3    function::{
4        Function, FunctionBody, FunctionParameter, FunctionQuery, FunctionQueryParameter,
5        FunctionSignature,
6    },
7    host::Host,
8    nativizer::ScriptNativizer,
9    registry::Registry,
10    script::{
11        ScriptExpression, ScriptFunction, ScriptFunctionSignature, ScriptOperation, ScriptStruct,
12        ScriptStructField,
13    },
14    struct_type::{StructFieldQuery, StructQuery},
15    Visibility,
16};
17use std::fmt::{Error, Write};
18
19#[derive(Debug, Copy, Clone, PartialEq, Eq)]
20enum ScopeKind {
21    Normal,
22    Branch,
23    Loop,
24}
25
26pub trait RustHostExpressionNativizer<SE: ScriptExpression> {
27    fn nativize_expression(&mut self, output: &mut dyn Write, input: &SE) -> Result<(), Error>;
28}
29
30impl<SE: ScriptExpression> RustHostExpressionNativizer<SE> for () {
31    fn nativize_expression(&mut self, _: &mut dyn Write, _: &SE) -> Result<(), Error> {
32        Ok(())
33    }
34}
35
36pub struct RustHostNativizer<'a, SE: ScriptExpression> {
37    pub tab_size: usize,
38    indent: usize,
39    scope_kind: Vec<ScopeKind>,
40    registry: &'a Registry,
41    expression_nativizer: Box<dyn RustHostExpressionNativizer<SE>>,
42}
43
44impl<'a, SE: ScriptExpression> RustHostNativizer<'a, SE> {
45    pub fn new(
46        registry: &'a Registry,
47        expression_nativizer: impl RustHostExpressionNativizer<SE> + 'static,
48    ) -> Self {
49        Self {
50            tab_size: 4,
51            indent: 0,
52            scope_kind: vec![],
53            registry,
54            expression_nativizer: Box::new(expression_nativizer),
55        }
56    }
57
58    pub fn with_tab_size(mut self, size: usize) -> Self {
59        self.tab_size = size;
60        self
61    }
62
63    pub fn push_indent(&mut self) {
64        self.indent += 1;
65    }
66
67    pub fn pop_indent(&mut self) {
68        if self.indent > 0 {
69            self.indent -= 1;
70        }
71    }
72
73    pub fn pad(&mut self, output: &mut dyn Write) -> Result<(), Error> {
74        for _ in 0..(self.indent * self.tab_size) {
75            output.write_char(' ')?;
76        }
77        Ok(())
78    }
79
80    pub fn write_visibility(
81        &mut self,
82        output: &mut dyn Write,
83        input: Visibility,
84    ) -> Result<(), Error> {
85        match input {
86            Visibility::Private => {}
87            Visibility::Module => {
88                write!(output, "pub(crate) ")?;
89            }
90            Visibility::Public => {
91                write!(output, "pub ")?;
92            }
93        }
94        Ok(())
95    }
96
97    pub fn write_struct_field_query(
98        &mut self,
99        output: &mut dyn Write,
100        input: &StructFieldQuery,
101    ) -> Result<(), Error> {
102        writeln!(output, "{} {{", std::any::type_name::<StructFieldQuery>())?;
103        self.push_indent();
104        self.pad(output)?;
105        if let Some(name) = input.name.as_ref() {
106            writeln!(output, r#"name: Some("{}".into()),"#, name.as_ref())?;
107        } else {
108            writeln!(output, "name: None,")?;
109        }
110        self.pad(output)?;
111        if let Some(struct_query) = input.struct_query.as_ref() {
112            write!(output, "struct_query: Some(")?;
113            self.write_struct_query(output, struct_query)?;
114            writeln!(output, "),")?;
115        } else {
116            writeln!(output, "struct_query: None,")?;
117        }
118        self.pad(output)?;
119        writeln!(
120            output,
121            "visibility: {}::{:?},",
122            std::any::type_name::<Visibility>(),
123            input.visibility
124        )?;
125        self.pop_indent();
126        self.pad(output)?;
127        write!(output, "}}")
128    }
129
130    pub fn write_struct_query(
131        &mut self,
132        output: &mut dyn Write,
133        input: &StructQuery,
134    ) -> Result<(), Error> {
135        writeln!(output, "{} {{", std::any::type_name::<StructQuery>())?;
136        self.push_indent();
137        self.pad(output)?;
138        if let Some(name) = input.name.as_ref() {
139            writeln!(output, r#"name: Some("{}".into()),"#, name.as_ref())?;
140        } else {
141            writeln!(output, "name: None,")?;
142        }
143        self.pad(output)?;
144        writeln!(output, "type_hash: None,")?;
145        self.pad(output)?;
146        if let Some(module_name) = input.module_name.as_ref() {
147            writeln!(
148                output,
149                r#"module_name: Some("{}".into()),"#,
150                module_name.as_ref()
151            )?;
152        } else {
153            writeln!(output, "module_name: None,")?;
154        }
155        self.pad(output)?;
156        if let Some(visibility) = input.visibility {
157            writeln!(
158                output,
159                "visibility: Some({}::{:?}),",
160                std::any::type_name::<Visibility>(),
161                visibility
162            )?;
163        } else {
164            writeln!(output, "visibility: None,")?;
165        }
166        self.pad(output)?;
167        writeln!(output, "fields: [")?;
168        self.push_indent();
169        for field in input.fields.as_ref() {
170            self.pad(output)?;
171            self.write_struct_field_query(output, field)?;
172            writeln!(output, ",")?;
173        }
174        self.pop_indent();
175        self.pad(output)?;
176        writeln!(output, "].as_slice().into(),")?;
177        self.pop_indent();
178        self.pad(output)?;
179        write!(output, "}}")
180    }
181
182    pub fn write_function_parameter_query(
183        &mut self,
184        output: &mut dyn Write,
185        input: &FunctionQueryParameter,
186    ) -> Result<(), Error> {
187        writeln!(
188            output,
189            "{} {{",
190            std::any::type_name::<FunctionQueryParameter>()
191        )?;
192        self.push_indent();
193        self.pad(output)?;
194        if let Some(name) = input.name.as_ref() {
195            writeln!(output, r#"name: Some("{}".into()),"#, name.as_ref())?;
196        } else {
197            writeln!(output, "name: None,")?;
198        }
199        self.pad(output)?;
200        if let Some(struct_query) = input.struct_query.as_ref() {
201            write!(output, "struct_query: Some(")?;
202            self.write_struct_query(output, struct_query)?;
203            writeln!(output, "),")?;
204        } else {
205            writeln!(output, "struct_query: None,")?;
206        }
207        self.pop_indent();
208        self.pad(output)?;
209        write!(output, "}}")
210    }
211
212    pub fn write_function_query(
213        &mut self,
214        output: &mut dyn Write,
215        input: &FunctionQuery,
216    ) -> Result<(), Error> {
217        writeln!(output, "{} {{", std::any::type_name::<FunctionQuery>())?;
218        self.push_indent();
219        self.pad(output)?;
220        if let Some(name) = input.name.as_ref() {
221            writeln!(output, r#"name: Some("{}".into()),"#, name.as_ref())?;
222        } else {
223            writeln!(output, "name: None,")?;
224        }
225        self.pad(output)?;
226        if let Some(struct_query) = input.struct_query.as_ref() {
227            write!(output, "Some(")?;
228            self.write_struct_query(output, struct_query)?;
229            writeln!(output, "),")?;
230        } else {
231            writeln!(output, "struct_query: None,")?;
232        }
233        self.pad(output)?;
234        if let Some(module_name) = input.module_name.as_ref() {
235            writeln!(
236                output,
237                r#"module_name: Some("{}".into()),"#,
238                module_name.as_ref()
239            )?;
240        } else {
241            writeln!(output, "module_name: None,")?;
242        }
243        self.pad(output)?;
244        if let Some(visibility) = input.visibility {
245            writeln!(
246                output,
247                "visibility: Some({}::{:?}),",
248                std::any::type_name::<Visibility>(),
249                visibility
250            )?;
251        } else {
252            writeln!(output, "visibility: None,")?;
253        }
254        self.pad(output)?;
255        writeln!(output, "inputs: [")?;
256        self.push_indent();
257        for parameter in input.inputs.as_ref() {
258            self.pad(output)?;
259            self.write_function_parameter_query(output, parameter)?;
260            writeln!(output, ",")?;
261        }
262        self.pop_indent();
263        self.pad(output)?;
264        writeln!(output, "].as_slice().into(),")?;
265        self.pad(output)?;
266        writeln!(output, "outputs: [")?;
267        self.push_indent();
268        for parameter in input.outputs.as_ref() {
269            self.pad(output)?;
270            self.write_function_parameter_query(output, parameter)?;
271            writeln!(output, ",")?;
272        }
273        self.pop_indent();
274        self.pad(output)?;
275        writeln!(output, "].as_slice().into(),")?;
276        self.pop_indent();
277        self.pad(output)?;
278        write!(output, "}}")
279    }
280
281    fn write_function(
282        &mut self,
283        output: &mut dyn Write,
284        input: &ScriptFunction<SE>,
285    ) -> Result<(), Error> {
286        self.pad(output)?;
287        self.write_visibility(output, input.signature.visibility)?;
288        write!(output, "fn {}(", input.signature.name,)?;
289        for parameter in &input.signature.inputs {
290            write!(
291                output,
292                "{}: {},",
293                parameter.name,
294                self.registry
295                    .structs()
296                    .find(|struct_type| parameter.struct_query.is_valid(struct_type))
297                    .unwrap()
298                    .type_name()
299            )?;
300        }
301        write!(output, ") -> (")?;
302        for parameter in &input.signature.outputs {
303            write!(
304                output,
305                "{},",
306                self.registry
307                    .structs()
308                    .find(|struct_type| parameter.struct_query.is_valid(struct_type))
309                    .unwrap()
310                    .type_name()
311            )?;
312        }
313        writeln!(output, ") {{")?;
314        self.push_indent();
315        self.pad(output)?;
316        writeln!(
317            output,
318            "{}::with_global(move |mut host| {{",
319            std::any::type_name::<Host>()
320        )?;
321        self.push_indent();
322        self.pad(output)?;
323        writeln!(
324            output,
325            "let (mut __context__, __registry__) = host.context_and_registry();"
326        )?;
327        for parameter in input.signature.inputs.iter().rev() {
328            self.pad(output)?;
329            writeln!(output, "__context__.stack().push({});", parameter.name)?;
330        }
331        self.pad(output)?;
332        writeln!(
333            output,
334            "{}::intuicio_function(__context__, __registry__);",
335            input.signature.name
336        )?;
337        self.pad(output)?;
338        write!(output, "(")?;
339        for parameter in &input.signature.outputs {
340            write!(
341                output,
342                "__context__.stack().pop::<{}>().unwrap(),",
343                self.registry
344                    .structs()
345                    .find(|struct_type| parameter.struct_query.is_valid(struct_type))
346                    .unwrap()
347                    .type_name()
348            )?;
349        }
350        writeln!(output, ")")?;
351        self.pop_indent();
352        self.pad(output)?;
353        writeln!(
354            output,
355            r#"}}).expect("There is no global host for current thread to run function: `{:?}`")"#,
356            input.signature
357        )?;
358        self.pop_indent();
359        self.pad(output)?;
360        writeln!(output, "}}")?;
361        writeln!(output)?;
362        self.pad(output)?;
363        writeln!(output, "pub mod {} {{", input.signature.name)?;
364        self.push_indent();
365        self.pad(output)?;
366        writeln!(
367            output,
368            "pub fn define_signature(registry: &{}) -> {} {{",
369            std::any::type_name::<Registry>(),
370            std::any::type_name::<FunctionSignature>()
371        )?;
372        self.push_indent();
373        self.pad(output)?;
374        writeln!(
375            output,
376            r#"let mut result = {}::new("{}");"#,
377            std::any::type_name::<FunctionSignature>(),
378            input.signature.name
379        )?;
380        if let Some(module_name) = input.signature.module_name.as_ref() {
381            self.pad(output)?;
382            writeln!(
383                output,
384                r#"result.module_name = Some("{}".to_owned());"#,
385                module_name
386            )?;
387        }
388        self.pad(output)?;
389        writeln!(
390            output,
391            "result.visibility = {}::{:?};",
392            std::any::type_name::<Visibility>(),
393            input.signature.visibility
394        )?;
395        for parameter in &input.signature.inputs {
396            self.pad(output)?;
397            writeln!(
398                output,
399                r#"result.inputs.push({}::new("{}", registry.find_struct({}::of::<{}>()).unwrap()));"#,
400                std::any::type_name::<FunctionParameter>(),
401                parameter.name,
402                std::any::type_name::<StructQuery>(),
403                self.registry
404                    .structs()
405                    .find(|struct_type| parameter.struct_query.is_valid(struct_type))
406                    .unwrap()
407                    .type_name()
408            )?;
409        }
410        for parameter in &input.signature.outputs {
411            self.pad(output)?;
412            writeln!(
413                output,
414                r#"result.outputs.push({}::new("{}", registry.find_struct({}::of::<{}>()).unwrap()));"#,
415                std::any::type_name::<FunctionParameter>(),
416                parameter.name,
417                std::any::type_name::<StructQuery>(),
418                self.registry
419                    .structs()
420                    .find(|struct_type| parameter.struct_query.is_valid(struct_type))
421                    .unwrap()
422                    .type_name()
423            )?;
424        }
425        self.pad(output)?;
426        writeln!(output, "result")?;
427        self.pop_indent();
428        self.pad(output)?;
429        writeln!(output, "}}")?;
430        writeln!(output)?;
431        self.pad(output)?;
432        writeln!(
433            output,
434            "pub fn define_function(registry: &{}) -> {} {{",
435            std::any::type_name::<Registry>(),
436            std::any::type_name::<Function>()
437        )?;
438        self.push_indent();
439        self.pad(output)?;
440        writeln!(
441            output,
442            "{}::new(define_signature(registry), {}::pointer(intuicio_function))",
443            std::any::type_name::<Function>(),
444            std::any::type_name::<FunctionBody>()
445        )?;
446        self.pop_indent();
447        self.pad(output)?;
448        writeln!(output, "}}")?;
449        writeln!(output)
450    }
451}
452
453impl<'a, SE: ScriptExpression> ScriptNativizer<SE> for RustHostNativizer<'a, SE> {
454    fn nativize_struct_begin(
455        &mut self,
456        output: &mut dyn Write,
457        input: &ScriptStruct,
458    ) -> Result<(), Error> {
459        self.pad(output)?;
460        writeln!(output, "#[derive(IntuicioScript)]")?;
461        if let Some(module_name) = input.module_name.as_ref() {
462            self.pad(output)?;
463            writeln!(output, r#"#[intuicio(module_name = "{}")]"#, module_name)?;
464        }
465        self.pad(output)?;
466        self.write_visibility(output, input.visibility)?;
467        writeln!(output, "struct {} {{", input.name)?;
468        self.push_indent();
469        Ok(())
470    }
471
472    fn nativize_struct_end(
473        &mut self,
474        output: &mut dyn Write,
475        _: &ScriptStruct,
476    ) -> Result<(), Error> {
477        self.pop_indent();
478        self.pad(output)?;
479        writeln!(output, "}}")
480    }
481
482    fn nativize_struct_field(
483        &mut self,
484        output: &mut dyn Write,
485        input: &ScriptStructField,
486    ) -> Result<(), Error> {
487        self.pad(output)?;
488        self.write_visibility(output, input.visibility)?;
489        writeln!(
490            output,
491            "{}: {},",
492            input.name,
493            self.registry
494                .structs()
495                .find(|struct_type| input.struct_query.is_valid(struct_type))
496                .unwrap()
497                .type_name()
498        )
499    }
500
501    fn nativize_function_begin(
502        &mut self,
503        output: &mut dyn Write,
504        input: &ScriptFunction<SE>,
505    ) -> Result<(), Error> {
506        self.write_function(output, input)
507        // TODO: for now we nativize struct methods as regular functions.
508        // the only difference between them is that standard puts method
509        // generated code in struct implementations.
510        // if input.signature.struct_query.is_some() {
511        //     self.write_method(output, input)
512        // } else {
513        //     self.write_function(output, input)
514        // }
515    }
516
517    fn nativize_function_end(
518        &mut self,
519        output: &mut dyn Write,
520        _: &ScriptFunction<SE>,
521    ) -> Result<(), Error> {
522        writeln!(output)?;
523        self.pop_indent();
524        self.pad(output)?;
525        writeln!(output, "}}")?;
526        Ok(())
527    }
528
529    fn nativize_function_signature(
530        &mut self,
531        output: &mut dyn Write,
532        input: &ScriptFunctionSignature,
533    ) -> Result<(), Error> {
534        self.pad(output)?;
535        self.write_visibility(output, input.visibility)?;
536        write!(
537            output,
538            "fn intuicio_function(context: &mut {}, registry: &{})",
539            std::any::type_name::<Context>(),
540            std::any::type_name::<Registry>()
541        )
542    }
543
544    fn nativize_function_body_begin(
545        &mut self,
546        output: &mut dyn Write,
547        _: &ScriptFunction<SE>,
548    ) -> Result<(), Error> {
549        write!(output, " ")
550    }
551
552    fn nativize_script_begin(
553        &mut self,
554        output: &mut dyn Write,
555        _: &[ScriptOperation<SE>],
556    ) -> Result<(), Error> {
557        if let Some(kind) = self.scope_kind.last() {
558            if *kind != ScopeKind::Loop {
559                write!(output, "'scope{}: ", self.scope_kind.len())?;
560            }
561        }
562        writeln!(output, "{{")?;
563        self.push_indent();
564        Ok(())
565    }
566
567    fn nativize_script_end(
568        &mut self,
569        output: &mut dyn Write,
570        _: &[ScriptOperation<SE>],
571    ) -> Result<(), Error> {
572        self.pop_indent();
573        self.pad(output)?;
574        write!(output, "}}")
575    }
576
577    fn nativize_operation_expression(
578        &mut self,
579        output: &mut dyn Write,
580        input: &SE,
581    ) -> Result<(), Error> {
582        self.expression_nativizer.nativize_expression(output, input)
583    }
584
585    fn nativize_operation_define_register(
586        &mut self,
587        output: &mut dyn Write,
588        query: &StructQuery,
589    ) -> Result<(), Error> {
590        self.pad(output)?;
591        writeln!(output, "{{")?;
592        self.push_indent();
593        self.pad(output)?;
594        write!(output, "let query = ")?;
595        self.write_struct_query(output, query)?;
596        writeln!(output, ";")?;
597        self.pad(output)?;
598        writeln!(
599            output,
600            "let handle = registry.structs().find(|handle| query.is_valid(handle)).unwrap();"
601        )?;
602        self.pad(output)?;
603        writeln!(
604            output,
605            "unsafe {{ context.registers().push_register_raw(handle.type_hash(), *handle.layout()) }};"
606        )?;
607        self.pop_indent();
608        self.pad(output)?;
609        writeln!(output, "}}")
610    }
611
612    fn nativize_operation_drop_register(
613        &mut self,
614        output: &mut dyn Write,
615        index: usize,
616    ) -> Result<(), Error> {
617        self.pad(output)?;
618        writeln!(output, "{{")?;
619        self.push_indent();
620        self.pad(output)?;
621        writeln!(
622            output,
623            "let index = context.absolute_register_index({});",
624            index
625        )?;
626        self.pad(output)?;
627        writeln!(
628            output,
629            "context.registers().access_register(index).unwrap().free();"
630        )?;
631        self.pop_indent();
632        self.pad(output)?;
633        writeln!(output, "}}")
634    }
635
636    fn nativize_operation_push_from_register(
637        &mut self,
638        output: &mut dyn Write,
639        index: usize,
640    ) -> Result<(), Error> {
641        self.pad(output)?;
642        writeln!(output, "{{")?;
643        self.push_indent();
644        self.pad(output)?;
645        writeln!(
646            output,
647            "let index = context.absolute_register_index({});",
648            index
649        )?;
650        self.pad(output)?;
651        writeln!(
652            output,
653            "let (stack, registers) = context.stack_and_registers();"
654        )?;
655        self.pad(output)?;
656        writeln!(
657            output,
658            "let mut register = registers.access_register(index).unwrap();"
659        )?;
660        self.pad(output)?;
661        writeln!(
662            output,
663            r#"if !stack.push_from_register(&mut register) {{ panic!("Could not push data from register: {}"); }}"#,
664            index
665        )?;
666        self.pop_indent();
667        self.pad(output)?;
668        writeln!(output, "}}")
669    }
670
671    fn nativize_operation_pop_to_register(
672        &mut self,
673        output: &mut dyn Write,
674        index: usize,
675    ) -> Result<(), Error> {
676        self.pad(output)?;
677        writeln!(output, "{{")?;
678        self.push_indent();
679        self.pad(output)?;
680        writeln!(
681            output,
682            "let index = context.absolute_register_index({});",
683            index
684        )?;
685        self.pad(output)?;
686        writeln!(
687            output,
688            "let (stack, registers) = context.stack_and_registers();"
689        )?;
690        self.pad(output)?;
691        writeln!(
692            output,
693            "let mut register = registers.access_register(index).unwrap();"
694        )?;
695        self.pad(output)?;
696        writeln!(
697            output,
698            r#"if !stack.pop_to_register(&mut register) {{ panic!("Could not pop data to register: {}"); }}"#,
699            index
700        )?;
701        self.pop_indent();
702        self.pad(output)?;
703        writeln!(output, "}}")
704    }
705
706    fn nativize_operation_move_register(
707        &mut self,
708        output: &mut dyn Write,
709        from: usize,
710        to: usize,
711    ) -> Result<(), Error> {
712        self.pad(output)?;
713        writeln!(output, "{{")?;
714        self.push_indent();
715        self.pad(output)?;
716        writeln!(
717            output,
718            "let from = context.absolute_register_index({});",
719            from
720        )?;
721        self.pad(output)?;
722        writeln!(output, "let to = context.absolute_register_index({});", to)?;
723        self.pad(output)?;
724        writeln!(
725            output,
726            "let (mut source, mut target) = context.registers().access_registers_pair(from, to).unwrap();"
727        )?;
728        self.pad(output)?;
729        writeln!(output, "source.move_to(&mut target);")?;
730        self.pop_indent();
731        self.pad(output)?;
732        writeln!(output, "}}")
733    }
734
735    fn nativize_operation_call_function(
736        &mut self,
737        output: &mut dyn Write,
738        query: &FunctionQuery,
739    ) -> Result<(), Error> {
740        self.pad(output)?;
741        writeln!(output, "{{")?;
742        self.push_indent();
743        self.pad(output)?;
744        write!(output, "let query = ")?;
745        self.write_function_query(output, query)?;
746        writeln!(output, ";")?;
747        self.pad(output)?;
748        writeln!(output, "registry.functions().find(|handle| query.is_valid(handle.signature())).unwrap().invoke(context, registry);")?;
749        self.pop_indent();
750        self.pad(output)?;
751        writeln!(output, "}}")
752    }
753
754    fn nativize_operation_branch_scope(
755        &mut self,
756        output: &mut dyn Write,
757        scope_success: &[ScriptOperation<SE>],
758        scope_failure: Option<&[ScriptOperation<SE>]>,
759    ) -> Result<(), Error> {
760        self.pad(output)?;
761        writeln!(output, "if context.stack().pop::<bool>().unwrap() {{")?;
762        self.push_indent();
763        self.pad(output)?;
764        self.scope_kind.push(ScopeKind::Branch);
765        self.nativize_script(output, scope_success)?;
766        self.scope_kind.pop();
767        self.pop_indent();
768        writeln!(output)?;
769        self.pad(output)?;
770        write!(output, "}}")?;
771        if let Some(scope_failure) = scope_failure.as_ref() {
772            writeln!(output, " else {{")?;
773            self.push_indent();
774            self.pad(output)?;
775            self.scope_kind.push(ScopeKind::Branch);
776            self.nativize_script(output, scope_failure)?;
777            self.scope_kind.pop();
778            self.pop_indent();
779            writeln!(output)?;
780            self.pad(output)?;
781            write!(output, "}}")?;
782        }
783        writeln!(output)
784    }
785
786    fn nativize_operation_loop_scope(
787        &mut self,
788        output: &mut dyn Write,
789        scope: &[ScriptOperation<SE>],
790    ) -> Result<(), Error> {
791        self.pad(output)?;
792        write!(output, "loop ")?;
793        self.scope_kind.push(ScopeKind::Loop);
794        self.nativize_script(output, scope)?;
795        self.scope_kind.pop();
796        writeln!(output)?;
797        Ok(())
798    }
799
800    fn nativize_operation_push_scope(
801        &mut self,
802        output: &mut dyn Write,
803        scope: &[ScriptOperation<SE>],
804    ) -> Result<(), Error> {
805        self.pad(output)?;
806        self.scope_kind.push(ScopeKind::Normal);
807        self.nativize_script(output, scope)?;
808        self.scope_kind.pop();
809        writeln!(output)?;
810        Ok(())
811    }
812
813    fn nativize_operation_pop_scope(&mut self, output: &mut dyn Write) -> Result<(), Error> {
814        self.pad(output)?;
815        match self.scope_kind.last() {
816            Some(ScopeKind::Normal) | Some(ScopeKind::Branch) => {
817                writeln!(output, "break 'scope{};", self.scope_kind.len())
818            }
819            Some(ScopeKind::Loop) => {
820                writeln!(output, "break;")
821            }
822            None => {
823                writeln!(output, "return;")
824            }
825        }
826    }
827
828    fn nativize_operation_continue_scope_conditionally(
829        &mut self,
830        output: &mut dyn Write,
831    ) -> Result<(), Error> {
832        self.pad(output)?;
833        write!(output, "if !context.stack().pop::<bool>().unwrap() {{ ")?;
834        match self.scope_kind.last() {
835            Some(ScopeKind::Normal) | Some(ScopeKind::Branch) => {
836                write!(output, "break 'scope{};", self.scope_kind.len())?;
837            }
838            Some(ScopeKind::Loop) => {
839                write!(output, "break;")?;
840            }
841            None => {
842                write!(output, "return;")?;
843            }
844        }
845        writeln!(output, " }}")
846    }
847}
848
849#[cfg(test)]
850mod tests {
851    use super::RustHostNativizer;
852    use intuicio_core::{
853        function::FunctionQuery,
854        nativizer::ScriptNativizer,
855        registry::Registry,
856        script::{
857            ScriptBuilder, ScriptFunction, ScriptFunctionParameter, ScriptFunctionSignature,
858            ScriptStruct, ScriptStructField,
859        },
860        struct_type::StructQuery,
861        Visibility,
862    };
863    use intuicio_data::type_hash::TypeHash;
864
865    #[test]
866    fn test_rust_host_nativization() {
867        let mut registry = Registry::default().with_basic_types();
868
869        let struct_type = ScriptStruct {
870            meta: None,
871            name: "Foo".to_owned(),
872            module_name: Some("test".to_owned()),
873            visibility: Visibility::Public,
874            fields: vec![
875                ScriptStructField {
876                    meta: None,
877                    name: "a".to_owned(),
878                    visibility: Visibility::Public,
879                    struct_query: StructQuery {
880                        type_hash: Some(TypeHash::of::<bool>()),
881                        ..Default::default()
882                    },
883                },
884                ScriptStructField {
885                    meta: None,
886                    name: "b".to_owned(),
887                    visibility: Visibility::Public,
888                    struct_query: StructQuery {
889                        type_hash: Some(TypeHash::of::<usize>()),
890                        ..Default::default()
891                    },
892                },
893                ScriptStructField {
894                    meta: None,
895                    name: "c".to_owned(),
896                    visibility: Visibility::Public,
897                    struct_query: StructQuery {
898                        type_hash: Some(TypeHash::of::<f32>()),
899                        ..Default::default()
900                    },
901                },
902            ],
903        };
904        struct_type.install(&mut registry);
905        let mut buffer = String::new();
906        RustHostNativizer::<()>::new(&registry, ())
907            .nativize_struct(&mut buffer, &struct_type)
908            .unwrap();
909        println!("{}", buffer);
910
911        let function = ScriptFunction::<()> {
912            signature: ScriptFunctionSignature {
913                meta: None,
914                name: "foo".to_owned(),
915                module_name: Some("test".to_owned()),
916                struct_query: None,
917                visibility: Visibility::Public,
918                inputs: vec![
919                    ScriptFunctionParameter {
920                        meta: None,
921                        name: "a".to_owned(),
922                        struct_query: StructQuery {
923                            type_hash: Some(TypeHash::of::<bool>()),
924                            ..Default::default()
925                        },
926                    },
927                    ScriptFunctionParameter {
928                        meta: None,
929                        name: "b".to_owned(),
930                        struct_query: StructQuery {
931                            type_hash: Some(TypeHash::of::<usize>()),
932                            ..Default::default()
933                        },
934                    },
935                ],
936                outputs: vec![ScriptFunctionParameter {
937                    meta: None,
938                    name: "c".to_owned(),
939                    struct_query: StructQuery {
940                        type_hash: Some(TypeHash::of::<f32>()),
941                        ..Default::default()
942                    },
943                }],
944            },
945            script: ScriptBuilder::default()
946                .define_register(StructQuery {
947                    name: Some("i32".into()),
948                    ..Default::default()
949                })
950                .drop_register(0)
951                .push_from_register(0)
952                .pop_to_register(0)
953                .move_register(0, 1)
954                .call_function(FunctionQuery {
955                    name: Some("foo".into()),
956                    ..Default::default()
957                })
958                .branch_scope(
959                    ScriptBuilder::default()
960                        .pop_to_register(0)
961                        .move_register(0, 1)
962                        .continue_scope_conditionally()
963                        .pop_scope()
964                        .build(),
965                    Some(
966                        ScriptBuilder::default()
967                            .pop_to_register(0)
968                            .move_register(0, 1)
969                            .continue_scope_conditionally()
970                            .pop_scope()
971                            .build(),
972                    ),
973                )
974                .loop_scope(
975                    ScriptBuilder::default()
976                        .pop_to_register(0)
977                        .move_register(0, 1)
978                        .continue_scope_conditionally()
979                        .pop_scope()
980                        .build(),
981                )
982                .push_scope(
983                    ScriptBuilder::default()
984                        .pop_to_register(0)
985                        .move_register(0, 1)
986                        .continue_scope_conditionally()
987                        .pop_scope()
988                        .build(),
989                )
990                .continue_scope_conditionally()
991                .pop_scope()
992                .build(),
993        };
994        let mut buffer = String::new();
995        RustHostNativizer::new(&registry, ())
996            .nativize_function(&mut buffer, &function)
997            .unwrap();
998        println!("{}", buffer);
999    }
1000}