intuicio_core/
script.rs

1use crate::{
2    Visibility,
3    context::Context,
4    function::{Function, FunctionBody, FunctionParameter, FunctionQuery, FunctionSignature},
5    meta::Meta,
6    registry::Registry,
7    types::{
8        TypeQuery,
9        enum_type::{EnumVariant, RuntimeEnumBuilder},
10        struct_type::{RuntimeStructBuilder, StructField},
11    },
12};
13use std::{
14    collections::HashMap,
15    error::Error,
16    path::{Path, PathBuf},
17    sync::Arc,
18};
19
20pub type ScriptHandle<'a, SE> = Arc<Script<'a, SE>>;
21pub type Script<'a, SE> = Vec<ScriptOperation<'a, SE>>;
22
23pub trait ScriptExpression: Send + Sync {
24    fn evaluate(&self, context: &mut Context, registry: &Registry);
25}
26
27impl ScriptExpression for () {
28    fn evaluate(&self, _: &mut Context, _: &Registry) {}
29}
30
31#[allow(clippy::type_complexity)]
32pub struct InlineExpression(Arc<dyn Fn(&mut Context, &Registry) + Send + Sync>);
33
34impl InlineExpression {
35    pub fn copied<T: Copy + Send + Sync + 'static>(value: T) -> Self {
36        Self(Arc::new(move |context, _| {
37            context.stack().push(value);
38        }))
39    }
40
41    pub fn cloned<T: Clone + Send + Sync + 'static>(value: T) -> Self {
42        Self(Arc::new(move |context, _| {
43            context.stack().push(value.clone());
44        }))
45    }
46
47    pub fn closure<F: Fn(&mut Context, &Registry) + Send + Sync + 'static>(f: F) -> Self {
48        Self(Arc::new(f))
49    }
50}
51
52impl ScriptExpression for InlineExpression {
53    fn evaluate(&self, context: &mut Context, registry: &Registry) {
54        (self.0)(context, registry);
55    }
56}
57
58#[derive(Debug)]
59pub enum ScriptOperation<'a, SE: ScriptExpression> {
60    None,
61    Expression {
62        expression: SE,
63    },
64    DefineRegister {
65        query: TypeQuery<'a>,
66    },
67    DropRegister {
68        index: usize,
69    },
70    PushFromRegister {
71        index: usize,
72    },
73    PopToRegister {
74        index: usize,
75    },
76    MoveRegister {
77        from: usize,
78        to: usize,
79    },
80    CallFunction {
81        query: FunctionQuery<'a>,
82    },
83    BranchScope {
84        scope_success: ScriptHandle<'a, SE>,
85        scope_failure: Option<ScriptHandle<'a, SE>>,
86    },
87    LoopScope {
88        scope: ScriptHandle<'a, SE>,
89    },
90    PushScope {
91        scope: ScriptHandle<'a, SE>,
92    },
93    PopScope,
94    ContinueScopeConditionally,
95    Suspend,
96}
97
98impl<SE: ScriptExpression> ScriptOperation<'_, SE> {
99    pub fn label(&self) -> &str {
100        match self {
101            Self::None => "None",
102            Self::Expression { .. } => "Expression",
103            Self::DefineRegister { .. } => "DefineRegister",
104            Self::DropRegister { .. } => "DropRegister",
105            Self::PushFromRegister { .. } => "PushFromRegister",
106            Self::PopToRegister { .. } => "PopToRegister",
107            Self::MoveRegister { .. } => "MoveRegister",
108            Self::CallFunction { .. } => "CallFunction",
109            Self::BranchScope { .. } => "BranchScope",
110            Self::LoopScope { .. } => "LoopScope",
111            Self::PushScope { .. } => "PushScope",
112            Self::PopScope => "PopScope",
113            Self::ContinueScopeConditionally => "ContinueScopeConditionally",
114            Self::Suspend => "Suspend",
115        }
116    }
117}
118
119pub struct ScriptBuilder<'a, SE: ScriptExpression>(Script<'a, SE>);
120
121impl<SE: ScriptExpression> Default for ScriptBuilder<'_, SE> {
122    fn default() -> Self {
123        Self(vec![])
124    }
125}
126
127impl<'a, SE: ScriptExpression> ScriptBuilder<'a, SE> {
128    pub fn build(self) -> ScriptHandle<'a, SE> {
129        ScriptHandle::new(self.0)
130    }
131
132    pub fn expression(mut self, expression: SE) -> Self {
133        self.0.push(ScriptOperation::Expression { expression });
134        self
135    }
136
137    pub fn define_register(mut self, query: TypeQuery<'a>) -> Self {
138        self.0.push(ScriptOperation::DefineRegister { query });
139        self
140    }
141
142    pub fn drop_register(mut self, index: usize) -> Self {
143        self.0.push(ScriptOperation::DropRegister { index });
144        self
145    }
146
147    pub fn push_from_register(mut self, index: usize) -> Self {
148        self.0.push(ScriptOperation::PushFromRegister { index });
149        self
150    }
151
152    pub fn pop_to_register(mut self, index: usize) -> Self {
153        self.0.push(ScriptOperation::PopToRegister { index });
154        self
155    }
156
157    pub fn move_register(mut self, from: usize, to: usize) -> Self {
158        self.0.push(ScriptOperation::MoveRegister { from, to });
159        self
160    }
161
162    pub fn call_function(mut self, query: FunctionQuery<'a>) -> Self {
163        self.0.push(ScriptOperation::CallFunction { query });
164        self
165    }
166
167    pub fn branch_scope(
168        mut self,
169        scope_success: ScriptHandle<'a, SE>,
170        scope_failure: Option<ScriptHandle<'a, SE>>,
171    ) -> Self {
172        self.0.push(ScriptOperation::BranchScope {
173            scope_success,
174            scope_failure,
175        });
176        self
177    }
178
179    pub fn loop_scope(mut self, scope: ScriptHandle<'a, SE>) -> Self {
180        self.0.push(ScriptOperation::LoopScope { scope });
181        self
182    }
183
184    pub fn push_scope(mut self, scope: ScriptHandle<'a, SE>) -> Self {
185        self.0.push(ScriptOperation::PushScope { scope });
186        self
187    }
188
189    pub fn pop_scope(mut self) -> Self {
190        self.0.push(ScriptOperation::PopScope);
191        self
192    }
193
194    pub fn continue_scope_conditionally(mut self) -> Self {
195        self.0.push(ScriptOperation::ContinueScopeConditionally);
196        self
197    }
198
199    pub fn suspend(mut self) -> Self {
200        self.0.push(ScriptOperation::Suspend);
201        self
202    }
203}
204
205#[derive(Debug)]
206pub struct ScriptFunctionParameter<'a> {
207    pub meta: Option<Meta>,
208    pub name: String,
209    pub type_query: TypeQuery<'a>,
210}
211
212impl ScriptFunctionParameter<'_> {
213    pub fn build(&self, registry: &Registry) -> FunctionParameter {
214        FunctionParameter {
215            meta: self.meta.to_owned(),
216            name: self.name.to_owned(),
217            type_handle: registry
218                .types()
219                .find(|type_| self.type_query.is_valid(type_))
220                .unwrap()
221                .clone(),
222        }
223    }
224}
225
226#[derive(Debug)]
227pub struct ScriptFunctionSignature<'a> {
228    pub meta: Option<Meta>,
229    pub name: String,
230    pub module_name: Option<String>,
231    pub type_query: Option<TypeQuery<'a>>,
232    pub visibility: Visibility,
233    pub inputs: Vec<ScriptFunctionParameter<'a>>,
234    pub outputs: Vec<ScriptFunctionParameter<'a>>,
235}
236
237impl ScriptFunctionSignature<'_> {
238    pub fn build(&self, registry: &Registry) -> FunctionSignature {
239        FunctionSignature {
240            meta: self.meta.to_owned(),
241            name: self.name.to_owned(),
242            module_name: self.module_name.to_owned(),
243            type_handle: self.type_query.as_ref().map(|type_query| {
244                registry
245                    .types()
246                    .find(|type_| type_query.is_valid(type_))
247                    .unwrap()
248                    .clone()
249            }),
250            visibility: self.visibility,
251            inputs: self
252                .inputs
253                .iter()
254                .map(|parameter| parameter.build(registry))
255                .collect(),
256            outputs: self
257                .outputs
258                .iter()
259                .map(|parameter| parameter.build(registry))
260                .collect(),
261        }
262    }
263}
264
265#[derive(Debug)]
266pub struct ScriptFunction<'a, SE: ScriptExpression> {
267    pub signature: ScriptFunctionSignature<'a>,
268    pub script: ScriptHandle<'a, SE>,
269}
270
271impl<SE: ScriptExpression> ScriptFunction<'static, SE> {
272    pub fn install<SFG: ScriptFunctionGenerator<SE>>(
273        &self,
274        registry: &mut Registry,
275        input: SFG::Input,
276    ) -> Option<SFG::Output> {
277        let (function, output) = SFG::generate_function(self, registry, input)?;
278        registry.add_function(function);
279        Some(output)
280    }
281}
282
283pub trait ScriptFunctionGenerator<SE: ScriptExpression> {
284    type Input;
285    type Output;
286
287    fn generate_function_body(
288        script: ScriptHandle<'static, SE>,
289        input: Self::Input,
290    ) -> Option<(FunctionBody, Self::Output)>;
291
292    fn generate_function(
293        function: &ScriptFunction<'static, SE>,
294        registry: &Registry,
295        input: Self::Input,
296    ) -> Option<(Function, Self::Output)> {
297        let (body, output) = Self::generate_function_body(function.script.clone(), input)?;
298        Some((
299            Function::new(function.signature.build(registry), body),
300            output,
301        ))
302    }
303}
304
305#[derive(Debug)]
306pub struct ScriptStructField<'a> {
307    pub meta: Option<Meta>,
308    pub name: String,
309    pub visibility: Visibility,
310    pub type_query: TypeQuery<'a>,
311}
312
313impl ScriptStructField<'_> {
314    pub fn build(&self, registry: &Registry) -> StructField {
315        let mut result = StructField::new(
316            &self.name,
317            registry
318                .types()
319                .find(|type_| self.type_query.is_valid(type_))
320                .unwrap()
321                .clone(),
322        )
323        .with_visibility(self.visibility);
324        result.meta.clone_from(&self.meta);
325        result
326    }
327}
328
329#[derive(Debug)]
330pub struct ScriptStruct<'a> {
331    pub meta: Option<Meta>,
332    pub name: String,
333    pub module_name: Option<String>,
334    pub visibility: Visibility,
335    pub fields: Vec<ScriptStructField<'a>>,
336}
337
338impl ScriptStruct<'_> {
339    pub fn declare(&self, registry: &mut Registry) {
340        let mut builder = RuntimeStructBuilder::new(&self.name);
341        builder = builder.visibility(self.visibility);
342        if let Some(module_name) = self.module_name.as_ref() {
343            builder = builder.module_name(module_name);
344        }
345        if let Some(meta) = self.meta.as_ref() {
346            builder = builder.meta(meta.to_owned());
347        }
348        registry.add_type(builder.build());
349    }
350
351    pub fn define(&self, registry: &mut Registry) {
352        let query = TypeQuery {
353            name: Some(self.name.as_str().into()),
354            module_name: self
355                .module_name
356                .as_ref()
357                .map(|module_name| module_name.into()),
358            ..Default::default()
359        };
360        if let Some(handle) = registry.find_type(query) {
361            let mut builder = RuntimeStructBuilder::new(&self.name);
362            builder = builder.visibility(self.visibility);
363            if let Some(module_name) = self.module_name.as_ref() {
364                builder = builder.module_name(module_name);
365            }
366            if let Some(meta) = self.meta.as_ref() {
367                builder = builder.meta(meta.to_owned());
368            }
369            for field in &self.fields {
370                builder = builder.field(field.build(registry));
371            }
372            unsafe {
373                let type_ = Arc::as_ptr(&handle).cast_mut();
374                *type_ = builder.build().into();
375            }
376        }
377    }
378
379    pub fn install(&self, registry: &mut Registry) {
380        let mut builder = RuntimeStructBuilder::new(&self.name);
381        builder = builder.visibility(self.visibility);
382        if let Some(module_name) = self.module_name.as_ref() {
383            builder = builder.module_name(module_name);
384        }
385        for field in &self.fields {
386            builder = builder.field(field.build(registry));
387        }
388        registry.add_type(builder.build());
389    }
390}
391
392#[derive(Debug)]
393pub struct ScriptEnumVariant<'a> {
394    pub meta: Option<Meta>,
395    pub name: String,
396    pub fields: Vec<ScriptStructField<'a>>,
397    pub discriminant: Option<u8>,
398}
399
400impl ScriptEnumVariant<'_> {
401    pub fn build(&self, registry: &Registry) -> EnumVariant {
402        let mut result = EnumVariant::new(&self.name);
403        result.fields = self
404            .fields
405            .iter()
406            .map(|field| field.build(registry))
407            .collect();
408        result.meta.clone_from(&self.meta);
409        result
410    }
411}
412
413#[derive(Debug)]
414pub struct ScriptEnum<'a> {
415    pub meta: Option<Meta>,
416    pub name: String,
417    pub module_name: Option<String>,
418    pub visibility: Visibility,
419    pub variants: Vec<ScriptEnumVariant<'a>>,
420    pub default_variant: Option<u8>,
421}
422
423impl ScriptEnum<'_> {
424    pub fn declare(&self, registry: &mut Registry) {
425        let mut builder = RuntimeEnumBuilder::new(&self.name);
426        if let Some(discriminant) = self.default_variant {
427            builder = builder.set_default_variant(discriminant);
428        }
429        builder = builder.visibility(self.visibility);
430        if let Some(module_name) = self.module_name.as_ref() {
431            builder = builder.module_name(module_name);
432        }
433        if let Some(meta) = self.meta.as_ref() {
434            builder = builder.meta(meta.to_owned());
435        }
436        registry.add_type(builder.build());
437    }
438
439    pub fn define(&self, registry: &mut Registry) {
440        let query = TypeQuery {
441            name: Some(self.name.as_str().into()),
442            module_name: self
443                .module_name
444                .as_ref()
445                .map(|module_name| module_name.into()),
446            ..Default::default()
447        };
448        if let Some(handle) = registry.find_type(query) {
449            let mut builder = RuntimeEnumBuilder::new(&self.name);
450            if let Some(discriminant) = self.default_variant {
451                builder = builder.set_default_variant(discriminant);
452            }
453            builder = builder.visibility(self.visibility);
454            if let Some(module_name) = self.module_name.as_ref() {
455                builder = builder.module_name(module_name);
456            }
457            if let Some(meta) = self.meta.as_ref() {
458                builder = builder.meta(meta.to_owned());
459            }
460            for variant in &self.variants {
461                if let Some(discriminant) = variant.discriminant {
462                    builder =
463                        builder.variant_with_discriminant(variant.build(registry), discriminant);
464                } else {
465                    builder = builder.variant(variant.build(registry));
466                }
467            }
468            unsafe {
469                let type_ = Arc::as_ptr(&handle).cast_mut();
470                *type_ = builder.build().into();
471            }
472        }
473    }
474
475    pub fn install(&self, registry: &mut Registry) {
476        let mut builder = RuntimeEnumBuilder::new(&self.name);
477        if let Some(discriminant) = self.default_variant {
478            builder = builder.set_default_variant(discriminant);
479        }
480        builder = builder.visibility(self.visibility);
481        if let Some(module_name) = self.module_name.as_ref() {
482            builder = builder.module_name(module_name);
483        }
484        for variant in &self.variants {
485            if let Some(discriminant) = variant.discriminant {
486                builder = builder.variant_with_discriminant(variant.build(registry), discriminant);
487            } else {
488                builder = builder.variant(variant.build(registry));
489            }
490        }
491        registry.add_type(builder.build());
492    }
493}
494
495#[derive(Debug, Default)]
496pub struct ScriptModule<'a, SE: ScriptExpression> {
497    pub name: String,
498    pub structs: Vec<ScriptStruct<'a>>,
499    pub enums: Vec<ScriptEnum<'a>>,
500    pub functions: Vec<ScriptFunction<'a, SE>>,
501}
502
503impl<SE: ScriptExpression> ScriptModule<'_, SE> {
504    pub fn fix_module_names(&mut self) {
505        for type_ in &mut self.structs {
506            type_.module_name = Some(self.name.to_owned());
507        }
508        for type_ in &mut self.enums {
509            type_.module_name = Some(self.name.to_owned());
510        }
511        for function in &mut self.functions {
512            function.signature.module_name = Some(self.name.to_owned());
513        }
514    }
515
516    pub fn declare_types(&self, registry: &mut Registry) {
517        for type_ in &self.structs {
518            type_.declare(registry);
519        }
520        for type_ in &self.enums {
521            type_.declare(registry);
522        }
523    }
524
525    pub fn define_types(&self, registry: &mut Registry) {
526        for type_ in &self.structs {
527            type_.define(registry);
528        }
529        for type_ in &self.enums {
530            type_.define(registry);
531        }
532    }
533
534    pub fn install_types(&self, registry: &mut Registry) {
535        self.declare_types(registry);
536        self.define_types(registry);
537    }
538}
539
540impl<SE: ScriptExpression> ScriptModule<'static, SE> {
541    pub fn install_functions<SFG: ScriptFunctionGenerator<SE>>(
542        &self,
543        registry: &mut Registry,
544        input: SFG::Input,
545    ) where
546        SFG::Input: Clone,
547    {
548        for function in &self.functions {
549            function.install::<SFG>(registry, input.clone());
550        }
551    }
552}
553
554#[derive(Debug, Default)]
555pub struct ScriptPackage<'a, SE: ScriptExpression> {
556    pub modules: Vec<ScriptModule<'a, SE>>,
557}
558
559impl<SE: ScriptExpression> ScriptPackage<'static, SE> {
560    pub fn install<SFG: ScriptFunctionGenerator<SE>>(
561        &self,
562        registry: &mut Registry,
563        input: SFG::Input,
564    ) where
565        SFG::Input: Clone,
566    {
567        for module in &self.modules {
568            module.install_types(registry);
569        }
570        for module in &self.modules {
571            module.install_functions::<SFG>(registry, input.clone());
572        }
573    }
574}
575
576pub struct ScriptContent<T> {
577    pub path: String,
578    pub name: String,
579    pub data: Result<Option<T>, Box<dyn Error>>,
580}
581
582pub trait ScriptContentProvider<T> {
583    fn load(&mut self, path: &str) -> Result<Option<T>, Box<dyn Error>>;
584
585    fn unpack_load(&mut self, path: &str) -> Result<Vec<ScriptContent<T>>, Box<dyn Error>> {
586        Ok(vec![ScriptContent {
587            path: path.to_owned(),
588            name: path.to_owned(),
589            data: self.load(path),
590        }])
591    }
592
593    fn sanitize_path(&self, path: &str) -> Result<String, Box<dyn Error>> {
594        Ok(path.to_owned())
595    }
596
597    fn join_paths(&self, parent: &str, relative: &str) -> Result<String, Box<dyn Error>>;
598}
599
600pub struct ExtensionContentProvider<S> {
601    default_extension: Option<String>,
602    extension_providers: HashMap<String, Box<dyn ScriptContentProvider<S>>>,
603}
604
605impl<S> Default for ExtensionContentProvider<S> {
606    fn default() -> Self {
607        Self {
608            default_extension: None,
609            extension_providers: Default::default(),
610        }
611    }
612}
613
614impl<S> ExtensionContentProvider<S> {
615    pub fn default_extension(mut self, extension: impl ToString) -> Self {
616        self.default_extension = Some(extension.to_string());
617        self
618    }
619
620    pub fn extension(
621        mut self,
622        extension: &str,
623        content_provider: impl ScriptContentProvider<S> + 'static,
624    ) -> Self {
625        self.extension_providers
626            .insert(extension.to_owned(), Box::new(content_provider));
627        self
628    }
629}
630
631impl<S> ScriptContentProvider<S> for ExtensionContentProvider<S> {
632    fn load(&mut self, _: &str) -> Result<Option<S>, Box<dyn Error>> {
633        Ok(None)
634    }
635
636    fn unpack_load(&mut self, path: &str) -> Result<Vec<ScriptContent<S>>, Box<dyn Error>> {
637        let extension = match Path::new(path).extension() {
638            Some(extension) => extension.to_string_lossy().to_string(),
639            None => match &self.default_extension {
640                Some(extension) => extension.to_owned(),
641                None => return Err(Box::new(ExtensionContentProviderError::NoDefaultExtension)),
642            },
643        };
644        if let Some(content_provider) = self.extension_providers.get_mut(&extension) {
645            content_provider.unpack_load(path)
646        } else {
647            Err(Box::new(
648                ExtensionContentProviderError::ContentProviderForExtensionNotFound(extension),
649            ))
650        }
651    }
652
653    fn sanitize_path(&self, path: &str) -> Result<String, Box<dyn Error>> {
654        let extension = match Path::new(path).extension() {
655            Some(extension) => extension.to_string_lossy().to_string(),
656            None => match &self.default_extension {
657                Some(extension) => extension.to_owned(),
658                None => return Err(Box::new(ExtensionContentProviderError::NoDefaultExtension)),
659            },
660        };
661        if let Some(content_provider) = self.extension_providers.get(&extension) {
662            content_provider.sanitize_path(path)
663        } else {
664            Err(Box::new(
665                ExtensionContentProviderError::ContentProviderForExtensionNotFound(extension),
666            ))
667        }
668    }
669
670    fn join_paths(&self, parent: &str, relative: &str) -> Result<String, Box<dyn Error>> {
671        let extension = match Path::new(relative).extension() {
672            Some(extension) => extension.to_string_lossy().to_string(),
673            None => match &self.default_extension {
674                Some(extension) => extension.to_owned(),
675                None => return Err(Box::new(ExtensionContentProviderError::NoDefaultExtension)),
676            },
677        };
678        if let Some(content_provider) = self.extension_providers.get(&extension) {
679            content_provider.join_paths(parent, relative)
680        } else {
681            Err(Box::new(
682                ExtensionContentProviderError::ContentProviderForExtensionNotFound(extension),
683            ))
684        }
685    }
686}
687
688#[derive(Debug)]
689pub enum ExtensionContentProviderError {
690    NoDefaultExtension,
691    ContentProviderForExtensionNotFound(String),
692}
693
694impl std::fmt::Display for ExtensionContentProviderError {
695    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
696        match self {
697            ExtensionContentProviderError::NoDefaultExtension => {
698                write!(f, "No default extension set")
699            }
700            ExtensionContentProviderError::ContentProviderForExtensionNotFound(extension) => {
701                write!(
702                    f,
703                    "Could not find content provider for extension: `{extension}`"
704                )
705            }
706        }
707    }
708}
709
710impl Error for ExtensionContentProviderError {}
711
712pub struct IgnoreContentProvider;
713
714impl<S> ScriptContentProvider<S> for IgnoreContentProvider {
715    fn load(&mut self, _: &str) -> Result<Option<S>, Box<dyn Error>> {
716        Ok(None)
717    }
718
719    fn join_paths(&self, parent: &str, relative: &str) -> Result<String, Box<dyn Error>> {
720        Ok(format!("{parent}/{relative}"))
721    }
722}
723
724pub trait BytesContentParser<T> {
725    fn parse(&self, bytes: Vec<u8>) -> Result<T, Box<dyn Error>>;
726}
727
728pub struct FileContentProvider<T> {
729    extension: String,
730    parser: Box<dyn BytesContentParser<T>>,
731}
732
733impl<T> FileContentProvider<T> {
734    pub fn new(extension: impl ToString, parser: impl BytesContentParser<T> + 'static) -> Self {
735        Self {
736            extension: extension.to_string(),
737            parser: Box::new(parser),
738        }
739    }
740}
741
742impl<T> ScriptContentProvider<T> for FileContentProvider<T> {
743    fn load(&mut self, path: &str) -> Result<Option<T>, Box<dyn Error>> {
744        Ok(Some(self.parser.parse(std::fs::read(path)?)?))
745    }
746
747    fn sanitize_path(&self, path: &str) -> Result<String, Box<dyn Error>> {
748        let mut result = PathBuf::from(path);
749        if result.extension().is_none() {
750            result.set_extension(&self.extension);
751        }
752        Ok(result.canonicalize()?.to_string_lossy().into_owned())
753    }
754
755    fn join_paths(&self, parent: &str, relative: &str) -> Result<String, Box<dyn Error>> {
756        let mut path = PathBuf::from(parent);
757        path.pop();
758        Ok(path.join(relative).to_string_lossy().into_owned())
759    }
760}