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(¶meter.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, ®istry);
575 assert_eq!(context.stack().pop::<i32>().unwrap(), 42);
576
577 assert_eq!(
578 function_meta::define_signature(®istry).meta,
579 Some(Meta::Identifier("foo".to_owned()))
580 );
581 }
582}