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