intuicio_core/
function.rs

1#![allow(unpredictable_function_pointer_comparisons)]
2
3use crate::{
4    Visibility,
5    context::Context,
6    meta::Meta,
7    registry::Registry,
8    types::{Type, TypeHandle, TypeQuery},
9};
10use intuicio_data::data_stack::DataStackPack;
11use rustc_hash::FxHasher;
12use std::{
13    borrow::Cow,
14    hash::{Hash, Hasher},
15    sync::Arc,
16};
17
18pub type FunctionHandle = Arc<Function>;
19pub type FunctionMetaQuery = fn(&Meta) -> bool;
20
21pub enum FunctionBody {
22    Pointer(fn(&mut Context, &Registry)),
23    #[allow(clippy::type_complexity)]
24    Closure(Arc<dyn Fn(&mut Context, &Registry) + Send + Sync>),
25}
26
27impl FunctionBody {
28    pub fn pointer(pointer: fn(&mut Context, &Registry)) -> Self {
29        Self::Pointer(pointer)
30    }
31
32    pub fn closure<T>(closure: T) -> Self
33    where
34        T: Fn(&mut Context, &Registry) + Send + Sync + 'static,
35    {
36        Self::Closure(Arc::new(closure))
37    }
38
39    pub fn invoke(&self, context: &mut Context, registry: &Registry) {
40        match self {
41            Self::Pointer(pointer) => pointer(context, registry),
42            Self::Closure(closure) => closure(context, registry),
43        }
44    }
45}
46
47impl std::fmt::Debug for FunctionBody {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        match self {
50            Self::Pointer(_) => write!(f, "<Pointer>"),
51            Self::Closure(_) => write!(f, "<Closure>"),
52        }
53    }
54}
55
56#[derive(Clone, PartialEq)]
57pub struct FunctionParameter {
58    pub meta: Option<Meta>,
59    pub name: String,
60    pub type_handle: TypeHandle,
61}
62
63impl FunctionParameter {
64    pub fn new(name: impl ToString, type_handle: TypeHandle) -> Self {
65        Self {
66            meta: None,
67            name: name.to_string(),
68            type_handle,
69        }
70    }
71
72    pub fn with_meta(mut self, meta: Meta) -> Self {
73        self.meta = Some(meta);
74        self
75    }
76}
77
78impl std::fmt::Debug for FunctionParameter {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        f.debug_struct("FunctionParameter")
81            .field("meta", &self.meta)
82            .field("name", &self.name)
83            .field("type_handle", &self.type_handle.name())
84            .finish()
85    }
86}
87
88#[derive(Clone, PartialEq)]
89pub struct FunctionSignature {
90    pub meta: Option<Meta>,
91    pub name: String,
92    pub module_name: Option<String>,
93    pub type_handle: Option<TypeHandle>,
94    pub visibility: Visibility,
95    pub inputs: Vec<FunctionParameter>,
96    pub outputs: Vec<FunctionParameter>,
97}
98
99impl FunctionSignature {
100    pub fn new(name: impl ToString) -> Self {
101        Self {
102            meta: None,
103            name: name.to_string(),
104            module_name: None,
105            type_handle: None,
106            visibility: Visibility::default(),
107            inputs: vec![],
108            outputs: vec![],
109        }
110    }
111
112    pub fn with_meta(mut self, meta: Meta) -> Self {
113        self.meta = Some(meta);
114        self
115    }
116
117    pub fn with_module_name(mut self, name: impl ToString) -> Self {
118        self.module_name = Some(name.to_string());
119        self
120    }
121
122    pub fn with_type_handle(mut self, handle: TypeHandle) -> Self {
123        self.type_handle = Some(handle);
124        self
125    }
126
127    pub fn with_visibility(mut self, visibility: Visibility) -> Self {
128        self.visibility = visibility;
129        self
130    }
131
132    pub fn with_input(mut self, parameter: FunctionParameter) -> Self {
133        self.inputs.push(parameter);
134        self
135    }
136
137    pub fn with_output(mut self, parameter: FunctionParameter) -> Self {
138        self.outputs.push(parameter);
139        self
140    }
141}
142
143impl std::fmt::Debug for FunctionSignature {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        f.debug_struct("FunctionSignature")
146            .field("meta", &self.meta)
147            .field("name", &self.name)
148            .field("module_name", &self.module_name)
149            .field(
150                "type_handle",
151                &match self.type_handle.as_ref() {
152                    Some(type_handle) => type_handle.name().to_owned(),
153                    None => "!".to_owned(),
154                },
155            )
156            .field("visibility", &self.visibility)
157            .field("inputs", &self.inputs)
158            .field("outputs", &self.outputs)
159            .finish()
160    }
161}
162
163impl std::fmt::Display for FunctionSignature {
164    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165        if let Some(meta) = self.meta.as_ref() {
166            write!(f, "#{meta} ")?;
167        }
168        if let Some(module_name) = self.module_name.as_ref() {
169            write!(f, "mod {module_name} ")?;
170        }
171        if let Some(type_handle) = self.type_handle.as_ref() {
172            match &**type_handle {
173                Type::Struct(value) => {
174                    write!(f, "struct {} ", value.type_name())?;
175                }
176                Type::Enum(value) => {
177                    write!(f, "enum {} ", value.type_name())?;
178                }
179            }
180        }
181        write!(f, "fn {}(", self.name)?;
182        for (index, parameter) in self.inputs.iter().enumerate() {
183            if index > 0 {
184                write!(f, ", ")?;
185            }
186            write!(
187                f,
188                "{}: {}",
189                parameter.name,
190                parameter.type_handle.type_name()
191            )?;
192        }
193        write!(f, ") -> (")?;
194        for (index, parameter) in self.outputs.iter().enumerate() {
195            if index > 0 {
196                write!(f, ", ")?;
197            }
198            write!(
199                f,
200                "{}: {}",
201                parameter.name,
202                parameter.type_handle.type_name()
203            )?;
204        }
205        write!(f, ")")
206    }
207}
208
209#[derive(Debug)]
210pub struct Function {
211    signature: FunctionSignature,
212    body: FunctionBody,
213}
214
215impl Function {
216    pub fn new(signature: FunctionSignature, body: FunctionBody) -> Self {
217        Self { signature, body }
218    }
219
220    pub fn signature(&self) -> &FunctionSignature {
221        &self.signature
222    }
223
224    pub fn invoke(&self, context: &mut Context, registry: &Registry) {
225        context.store_registers();
226        self.body.invoke(context, registry);
227        context.restore_registers();
228    }
229
230    pub fn call<O: DataStackPack, I: DataStackPack>(
231        &self,
232        context: &mut Context,
233        registry: &Registry,
234        inputs: I,
235        verify: bool,
236    ) -> O {
237        if verify {
238            self.verify_inputs_outputs::<O, I>();
239        }
240        inputs.stack_push_reversed(context.stack());
241        self.invoke(context, registry);
242        O::stack_pop(context.stack())
243    }
244
245    pub fn verify_inputs_outputs<O: DataStackPack, I: DataStackPack>(&self) {
246        let input_types = I::pack_types();
247        if input_types.len() != self.signature.inputs.len() {
248            panic!("Function: {} got wrong inputs number!", self.signature.name);
249        }
250        let output_types = O::pack_types();
251        if output_types.len() != self.signature.outputs.len() {
252            panic!(
253                "Function: {} got wrong outputs number!",
254                self.signature.name
255            );
256        }
257        for (parameter, type_hash) in self.signature.inputs.iter().zip(input_types) {
258            if parameter.type_handle.type_hash() != type_hash {
259                panic!(
260                    "Function: {} input parameter: {} got wrong value type!",
261                    self.signature.name, parameter.name
262                );
263            }
264        }
265        for (parameter, type_hash) in self.signature.outputs.iter().zip(output_types) {
266            if parameter.type_handle.type_hash() != type_hash {
267                panic!(
268                    "Function: {} output parameter: {} got wrong value type!",
269                    self.signature.name, parameter.name
270                );
271            }
272        }
273    }
274
275    pub fn into_handle(self) -> FunctionHandle {
276        self.into()
277    }
278}
279
280#[derive(Debug, Default, Clone, PartialEq, Hash)]
281pub struct FunctionQueryParameter<'a> {
282    pub name: Option<Cow<'a, str>>,
283    pub type_query: Option<TypeQuery<'a>>,
284    pub meta: Option<FunctionMetaQuery>,
285}
286
287impl FunctionQueryParameter<'_> {
288    pub fn is_valid(&self, parameter: &FunctionParameter) -> bool {
289        self.name
290            .as_ref()
291            .map(|name| name.as_ref() == parameter.name)
292            .unwrap_or(true)
293            && self
294                .type_query
295                .as_ref()
296                .map(|query| query.is_valid(&parameter.type_handle))
297                .unwrap_or(true)
298            && self
299                .meta
300                .as_ref()
301                .map(|query| parameter.meta.as_ref().map(query).unwrap_or(false))
302                .unwrap_or(true)
303    }
304
305    pub fn to_static(&self) -> FunctionQueryParameter<'static> {
306        FunctionQueryParameter {
307            name: self
308                .name
309                .as_ref()
310                .map(|name| name.as_ref().to_owned().into()),
311            type_query: self.type_query.as_ref().map(|query| query.to_static()),
312            meta: self.meta,
313        }
314    }
315}
316
317#[derive(Debug, Default, Clone, PartialEq, Hash)]
318pub struct FunctionQuery<'a> {
319    pub name: Option<Cow<'a, str>>,
320    pub module_name: Option<Cow<'a, str>>,
321    pub type_query: Option<TypeQuery<'a>>,
322    pub visibility: Option<Visibility>,
323    pub inputs: Cow<'a, [FunctionQueryParameter<'a>]>,
324    pub outputs: Cow<'a, [FunctionQueryParameter<'a>]>,
325    pub meta: Option<FunctionMetaQuery>,
326}
327
328impl FunctionQuery<'_> {
329    pub fn is_valid(&self, signature: &FunctionSignature) -> bool {
330        self.name
331            .as_ref()
332            .map(|name| name.as_ref() == signature.name)
333            .unwrap_or(true)
334            && self
335                .module_name
336                .as_ref()
337                .map(|name| {
338                    signature
339                        .module_name
340                        .as_ref()
341                        .map(|module_name| name.as_ref() == module_name)
342                        .unwrap_or(false)
343                })
344                .unwrap_or(true)
345            && self
346                .type_query
347                .as_ref()
348                .map(|query| {
349                    signature
350                        .type_handle
351                        .as_ref()
352                        .map(|handle| query.is_valid(handle))
353                        .unwrap_or(false)
354                })
355                .unwrap_or(true)
356            && self
357                .visibility
358                .map(|visibility| signature.visibility.is_visible(visibility))
359                .unwrap_or(true)
360            && self
361                .inputs
362                .iter()
363                .zip(signature.inputs.iter())
364                .all(|(query, parameter)| query.is_valid(parameter))
365            && self
366                .outputs
367                .iter()
368                .zip(signature.outputs.iter())
369                .all(|(query, parameter)| query.is_valid(parameter))
370            && self
371                .meta
372                .as_ref()
373                .map(|query| signature.meta.as_ref().map(query).unwrap_or(false))
374                .unwrap_or(true)
375    }
376
377    pub fn as_hash(&self) -> u64 {
378        let mut hasher = FxHasher::default();
379        self.hash(&mut hasher);
380        hasher.finish()
381    }
382
383    pub fn to_static(&self) -> FunctionQuery<'static> {
384        FunctionQuery {
385            name: self
386                .name
387                .as_ref()
388                .map(|name| name.as_ref().to_owned().into()),
389            module_name: self
390                .module_name
391                .as_ref()
392                .map(|name| name.as_ref().to_owned().into()),
393            type_query: self.type_query.as_ref().map(|query| query.to_static()),
394            visibility: self.visibility,
395            inputs: self
396                .inputs
397                .as_ref()
398                .iter()
399                .map(|query| query.to_static())
400                .collect(),
401            outputs: self
402                .outputs
403                .as_ref()
404                .iter()
405                .map(|query| query.to_static())
406                .collect(),
407            meta: self.meta,
408        }
409    }
410}
411
412#[macro_export]
413macro_rules! function_signature {
414    (
415        $registry:expr
416        =>
417        $(mod $module_name:ident)?
418        $(type ($type:ty))?
419        fn
420        $name:ident
421        ($( $input_name:ident : $input_type:ty ),*)
422        ->
423        ($( $output_name:ident : $output_type:ty ),*)
424    ) => {{
425        let mut result = $crate::function::FunctionSignature::new(stringify!($name));
426        $(
427            result.module_name = Some(stringify!($module_name).to_owned());
428        )?
429        $(
430            result.type_handle = Some($registry.find_type($crate::types::TypeQuery::of::<$type>()).unwrap());
431        )?
432        $(
433            result.inputs.push(
434                $crate::function::FunctionParameter::new(
435                    stringify!($input_name).to_owned(),
436                    $registry.find_type($crate::types::TypeQuery::of::<$input_type>()).unwrap()
437                )
438            );
439        )*
440        $(
441            result.outputs.push(
442                $crate::function::FunctionParameter::new(
443                    stringify!($output_name).to_owned(),
444                    $registry.find_type($crate::types::TypeQuery::of::<$output_type>()).unwrap()
445                )
446            );
447        )*
448        result
449    }};
450}
451
452#[macro_export]
453macro_rules! define_function {
454    (
455        $registry:expr
456        =>
457        $(mod $module_name:ident)?
458        $(type ($type:ty))?
459        fn
460        $name:ident
461        ($( $input_name:ident : $input_type:ty),*)
462        ->
463        ($( $output_name:ident : $output_type:ty),*)
464        $code:block
465    ) => {
466        $crate::function::Function::new(
467            $crate::function_signature! {
468                $registry
469                =>
470                $(mod $module_name)?
471                $(type ($type))?
472                fn
473                $name
474                ($($input_name : $input_type),*)
475                ->
476                ($($output_name : $output_type),*)
477            },
478            $crate::function::FunctionBody::closure(move |context, registry| {
479                use intuicio_data::data_stack::DataStackPack;
480                #[allow(unused_mut)]
481                let ($(mut $input_name,)*) = <($($input_type,)*)>::stack_pop(context.stack());
482                $code.stack_push_reversed(context.stack());
483            }),
484        )
485    };
486}
487
488#[cfg(test)]
489mod tests {
490    use crate as intuicio_core;
491    use crate::{context::*, function::*, registry::*, types::struct_type::*};
492    use intuicio_data;
493    use intuicio_derive::*;
494
495    #[intuicio_function(meta = "foo", args_meta(_bar = "foo"))]
496    fn function_meta(_bar: bool) {}
497
498    #[test]
499    fn test_function() {
500        fn add(context: &mut Context, _: &Registry) {
501            let a = context.stack().pop::<i32>().unwrap();
502            let b = context.stack().pop::<i32>().unwrap();
503            context.stack().push(a + b);
504        }
505
506        let i32_handle = NativeStructBuilder::new::<i32>()
507            .build()
508            .into_type()
509            .into_handle();
510        let signature = FunctionSignature::new("add")
511            .with_input(FunctionParameter::new("a", i32_handle.clone()))
512            .with_input(FunctionParameter::new("b", i32_handle.clone()))
513            .with_output(FunctionParameter::new("result", i32_handle));
514        let function = Function::new(signature.to_owned(), FunctionBody::pointer(add));
515
516        assert!(FunctionQuery::default().is_valid(&signature));
517        assert!(
518            FunctionQuery {
519                name: Some("add".into()),
520                ..Default::default()
521            }
522            .is_valid(&signature)
523        );
524        assert!(
525            FunctionQuery {
526                name: Some("add".into()),
527                inputs: [
528                    FunctionQueryParameter {
529                        name: Some("a".into()),
530                        ..Default::default()
531                    },
532                    FunctionQueryParameter {
533                        name: Some("b".into()),
534                        ..Default::default()
535                    }
536                ]
537                .as_slice()
538                .into(),
539                outputs: [FunctionQueryParameter {
540                    name: Some("result".into()),
541                    ..Default::default()
542                }]
543                .as_slice()
544                .into(),
545                ..Default::default()
546            }
547            .is_valid(&signature)
548        );
549        assert!(
550            !FunctionQuery {
551                name: Some("add".into()),
552                inputs: [
553                    FunctionQueryParameter {
554                        name: Some("b".into()),
555                        ..Default::default()
556                    },
557                    FunctionQueryParameter {
558                        name: Some("a".into()),
559                        ..Default::default()
560                    }
561                ]
562                .as_slice()
563                .into(),
564                ..Default::default()
565            }
566            .is_valid(&signature)
567        );
568
569        let mut context = Context::new(10240, 10240);
570        let registry = Registry::default().with_basic_types();
571
572        context.stack().push(2);
573        context.stack().push(40);
574        function.invoke(&mut context, &registry);
575        assert_eq!(context.stack().pop::<i32>().unwrap(), 42);
576
577        assert_eq!(
578            function_meta::define_signature(&registry).meta,
579            Some(Meta::Identifier("foo".to_owned()))
580        );
581    }
582}