Skip to main content

lang_interpreter_macros/
lib.rs

1mod util;
2
3use proc_macro::TokenStream;
4use darling::ast::NestedMeta;
5use darling::{Error, FromMeta};
6use proc_macro2::Span;
7use quote::{quote, quote_spanned};
8use syn::LitStr;
9use syn::parse::Parser;
10
11#[derive(Debug, FromMeta)]
12enum TypeConstraintMacroArgs {
13    #[darling(rename = "allowed")]
14    Allowed(Vec<LitStr>),
15
16    #[darling(rename = "not_allowed")]
17    NotAllowed(Vec<LitStr>),
18}
19
20#[derive(Debug, Default, FromMeta)]
21enum ParameterTypeMacroArgs {
22    #[default]
23    Normal,
24
25    #[darling(rename = "number")]
26    Number,
27
28    #[darling(rename = "callable")]
29    Callable,
30
31    #[darling(rename = "boolean")]
32    Boolean,
33
34    #[darling(rename = "call_by_pointer")]
35    CallByPointer,
36
37    #[darling(rename = "var_args")]
38    VarArgs,
39
40    #[darling(rename = "raw_var_args")]
41    RawVarArgs,
42}
43
44impl ParameterTypeMacroArgs {
45    fn type_name(&self) -> &'static str {
46        match self {
47            ParameterTypeMacroArgs::Normal => "Normal",
48            ParameterTypeMacroArgs::Number => "Number",
49            ParameterTypeMacroArgs::Callable => "Callable",
50            ParameterTypeMacroArgs::Boolean => "Boolean",
51            ParameterTypeMacroArgs::CallByPointer => "CallByPointer",
52            ParameterTypeMacroArgs::VarArgs => "VarArgs",
53            ParameterTypeMacroArgs::RawVarArgs => "RawVarArgs",
54        }
55    }
56}
57
58#[derive(Debug, FromMeta)]
59struct ParameterMacroArgs {
60    name: String,
61
62    #[darling(default)]
63    info: Option<String>,
64
65    #[darling(default)]
66    parameter_type: ParameterTypeMacroArgs,
67
68    #[darling(default)]
69    type_constraint: Option<TypeConstraintMacroArgs>,
70}
71
72#[derive(Debug, FromMeta)]
73struct DeprecationInfo {
74    #[darling(default)]
75    remove_version: Option<String>,
76
77    #[darling(default)]
78    replacement_function: Option<String>,
79}
80
81#[derive(Debug, FromMeta)]
82struct LangFuncMetadata {
83    name: String,
84
85    #[darling(default)]
86    info: Option<String>,
87
88    #[darling(multiple, rename="parameter")]
89    parameters: Vec<ParameterMacroArgs>,
90
91    #[darling(default)]
92    has_info: bool,
93
94    #[darling(default)]
95    combinator_function: bool,
96
97    #[darling(default)]
98    linker_function: bool,
99
100    #[darling(default)]
101    deprecated: Option<DeprecationInfo>,
102
103    #[darling(default)]
104    return_type_constraint: Option<TypeConstraintMacroArgs>,
105}
106
107#[proc_macro]
108pub fn lang_func_id(
109    args: TokenStream
110) -> TokenStream {
111    let func_id_ident = syn::parse_macro_input!(args as syn::Ident);
112
113    let native_func_id_path: proc_macro2::TokenStream = "::lang_interpreter::interpreter::data::function::native::NativeFuncId".parse().unwrap();
114    let gen_next_native_func_id_path: proc_macro2::TokenStream = "::lang_interpreter::interpreter::data::function::native::gen_next_native_func_id".parse().unwrap();
115    let lazy_lock_path: proc_macro2::TokenStream = "::std::sync::LazyLock".parse().unwrap();
116
117    TokenStream::from(quote! {
118        static #func_id_ident: #lazy_lock_path<#native_func_id_path, fn() -> #native_func_id_path> = #lazy_lock_path::new(#gen_next_native_func_id_path);
119
120        //Generate new ID instantly
121        let _ = *#func_id_ident;
122    })
123}
124
125#[proc_macro]
126pub fn lang_func_metadata(
127    args: TokenStream,
128) -> TokenStream {
129    let attr_args = match NestedMeta::parse_meta_list(args.into()) {
130        Ok(v) => v,
131        Err(e) => {
132            return TokenStream::from(Error::from(e).write_errors());
133        }
134    };
135
136    let args = match LangFuncMetadata::from_list(&attr_args) {
137        Ok(v) => v,
138        Err(e) => {
139            return TokenStream::from(e.write_errors());
140        }
141    };
142
143    let func_name = util::option_string_to_token_stream(&Some(args.name));
144    let func_info = util::option_string_to_token_stream(&args.info);
145
146    let has_info: proc_macro2::TokenStream = args.has_info.to_string().parse().unwrap();
147
148    let combinator_function: proc_macro2::TokenStream = args.combinator_function.to_string().parse().unwrap();
149
150    let linker_function: proc_macro2::TokenStream = args.linker_function.to_string().parse().unwrap();
151
152    let deprecation_info = util::option_deprecation_info_to_token_stream(&args.deprecated);
153
154    let vec_macro_path: proc_macro2::TokenStream = "::std::vec!".parse().unwrap();
155    let parameter_metadata_path: proc_macro2::TokenStream = "::lang_interpreter::interpreter::data::function::ParameterMetadata".parse().unwrap();
156    let parameter_type_path: proc_macro2::TokenStream = "::lang_interpreter::interpreter::data::function::ParameterType".parse().unwrap();
157
158    let mut parameters = Vec::new();
159    for parameter in &args.parameters {
160        let parameter_name = proc_macro2::Literal::string(&parameter.name);
161
162        let parameter_info = util::option_string_to_token_stream(&parameter.info);
163
164        let parameter_data_type_constraint = util::option_type_constraint_macro_args_to_token_stream(&parameter.type_constraint);
165
166        let parameter_type = proc_macro2::Ident::new(
167            parameter.parameter_type.type_name(),
168            Span::mixed_site(),
169        );
170
171        parameters.push(quote! {
172            #parameter_metadata_path::new(
173                #parameter_name,
174                #parameter_info,
175                #parameter_data_type_constraint,
176                #parameter_type_path::#parameter_type,
177            )
178        });
179    }
180
181    let return_value_type_constraint = util::option_type_constraint_macro_args_to_token_stream(&args.return_type_constraint);
182
183    let function_metadata_path: proc_macro2::TokenStream = "::lang_interpreter::interpreter::data::function::FunctionMetadata".parse().unwrap();
184
185    TokenStream::from(quote!{
186        #function_metadata_path::new(
187            #func_name,
188            #func_info,
189
190            #has_info,
191
192            #combinator_function,
193
194            #linker_function,
195            #deprecation_info,
196
197            #vec_macro_path[
198                #(#parameters,)*
199            ],
200
201            #return_value_type_constraint,
202        )
203    })
204}
205
206#[proc_macro]
207pub fn lang_func_adapter(
208    args: TokenStream,
209) -> TokenStream {
210    let data = syn::punctuated::Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated.parse(args);
211    let data = match data {
212        Ok(v) => v,
213        Err(e) => {
214            return TokenStream::from(Error::from(e).write_errors());
215        }
216    };
217
218    if data.len() != 2 {
219        return TokenStream::from(quote!{
220            compile_error!("Expected two arguments: function identifier, metadata")
221        });
222    }
223
224    let func_ident = &data[0];
225    let metadata = &data[1];
226
227    let box_path: proc_macro2::TokenStream = "::std::boxed::Box".parse().unwrap();
228
229    let lang_func_id_macro_path: proc_macro2::TokenStream = "::lang_interpreter::lang_func_id!".parse().unwrap();
230    let func_trait_path: proc_macro2::TokenStream =
231            "::lang_interpreter::interpreter::data::function::native::ConvertToFuncTrait::func_trait".parse().unwrap();
232    let native_function_adapter_path: proc_macro2::TokenStream =
233            "::lang_interpreter::interpreter::data::function::native::NativeFunctionAdapter".parse().unwrap();
234
235    let span = Span::mixed_site();
236
237    quote_spanned!(span => {
238        #lang_func_id_macro_path(FUNC_ID);
239
240        (
241            #metadata,
242            #box_path::new(#func_trait_path(#func_ident)) as #box_path<dyn #native_function_adapter_path + 'static>,
243            *FUNC_ID,
244        )
245    }).into()
246}
247
248#[doc(hidden)]
249#[proc_macro]
250pub fn internal_tuple_from_lang_args_impl(
251    args: TokenStream,
252) -> TokenStream {
253    let count = syn::parse_macro_input!(args as syn::LitInt).base10_parse::<usize>();
254    let count = match count {
255        Ok(v) => v,
256        Err(e) => {
257            return TokenStream::from(e.into_compile_error());
258        }
259    };
260
261    let data_object_ref_ident = proc_macro2::Ident::new(
262        "DataObjectRef",
263        Span::mixed_site(),
264    );
265
266    let args_tokens = vec![data_object_ref_ident; count];
267
268    let count_lit = proc_macro2::Literal::usize_unsuffixed(count);
269    let count_plus_1_lit = proc_macro2::Literal::usize_unsuffixed(count + 1);
270
271    let next_ident = proc_macro2::Ident::new(
272        "next",
273        Span::mixed_site(),
274    );
275    let next_tokens = vec![next_ident; count];
276
277    //TODO implement var args (normal and raw) in any position (text is of type DataObject anyway)
278
279    TokenStream::from(quote! {
280        //Args only
281        impl FromLangArgs for (#(#args_tokens,)*) {
282            fn from_lang_args(
283                this_object: OptionLangObjectRef,
284                args: Vec<DataObjectRef>,
285            ) -> Result<Self> {
286                if args.len() != #count_lit {
287                    return Err(NativeError::new("Invalid argument count for native function", None));
288                }
289
290                if this_object.is_some() {
291                    return Err(NativeError::new("This object may not be set for native function without a this parameter", None));
292                }
293                
294                let mut args = args;
295                let mut args = args.drain(..);
296
297                Ok((#(args.#next_tokens().unwrap(),)*))
298            }
299
300            fn lang_parameter_count() -> usize {
301                #count_lit
302            }
303
304            fn is_method() -> bool {
305                false
306            }
307        }
308
309        //VarArgs + args
310        impl FromLangArgs for (Vec<DataObjectRef>, #(#args_tokens,)*) {
311            fn from_lang_args(
312                this_object: OptionLangObjectRef,
313                args: Vec<DataObjectRef>,
314            ) -> Result<Self> {
315                if args.len() < #count_lit {
316                    return Err(NativeError::new("Not enough arguments for native function", None));
317                }
318
319                if this_object.is_some() {
320                    return Err(NativeError::new("This object may not be set for native function without a this parameter", None));
321                }
322
323                let var_args_count = args.len() - #count_lit;
324
325                let mut args = args;
326                let mut var_args = args.split_off(var_args_count);
327                mem::swap(&mut args, &mut var_args);
328                
329                let mut args = args.drain(..);
330
331                Ok((var_args, #(args.#next_tokens().unwrap(),)*))
332            }
333
334            fn lang_parameter_count() -> usize {
335                #count_plus_1_lit
336            }
337
338            fn is_method() -> bool {
339                false
340            }
341        }
342
343        //Args + VarArgs
344        impl FromLangArgs for (#(#args_tokens,)* Vec<DataObjectRef>,) {
345            fn from_lang_args(
346                this_object: OptionLangObjectRef,
347                args: Vec<DataObjectRef>,
348            ) -> Result<Self> {
349                if args.len() < #count_lit {
350                    return Err(NativeError::new("Not enough arguments for native function", None));
351                }
352
353                if this_object.is_some() {
354                    return Err(NativeError::new("This object may not be set for native function without a this parameter", None));
355                }
356
357                let mut args = args;
358                let var_args = args.split_off(#count_lit);
359                
360                let mut args = args.drain(..);
361
362                Ok((#(args.#next_tokens().unwrap(),)* var_args,))
363            }
364
365            fn lang_parameter_count() -> usize {
366                #count_plus_1_lit
367            }
368
369            fn is_method() -> bool {
370                false
371            }
372        }
373
374        //This arg + args
375        impl FromLangArgs for (LangObjectRef, #(#args_tokens,)*) {
376            fn from_lang_args(
377                this_object: OptionLangObjectRef,
378                args: Vec<DataObjectRef>,
379            ) -> Result<Self> {
380                if args.len() != #count_lit {
381                    return Err(NativeError::new("Invalid argument count for native function", None));
382                }
383
384                let Some(this_object) = this_object.as_ref() else {
385                    return Err(NativeError::new("This object must be set for native function with a this parameter", None));
386                };
387
388                let mut args = args;
389                let mut args = args.drain(..);
390
391                Ok((this_object.clone(), #(args.#next_tokens().unwrap(),)*))
392            }
393
394            fn lang_parameter_count() -> usize {
395                #count_lit
396            }
397
398            fn is_method() -> bool {
399                true
400            }
401        }
402        
403        //This arg + VarArgs + args
404        impl FromLangArgs for (LangObjectRef, Vec<DataObjectRef>, #(#args_tokens,)*) {
405            fn from_lang_args(
406                this_object: OptionLangObjectRef,
407                args: Vec<DataObjectRef>,
408            ) -> Result<Self> {
409                if args.len() < #count_lit {
410                    return Err(NativeError::new("Not enough arguments for native function", None));
411                }
412
413                let Some(this_object) = this_object.as_ref() else {
414                    return Err(NativeError::new("This object must be set for native function with a this parameter", None));
415                };
416
417                let var_args_count = args.len() - #count_lit;
418
419                let mut args = args;
420                let mut var_args = args.split_off(var_args_count);
421                mem::swap(&mut args, &mut var_args);
422
423                let mut args = args.drain(..);
424
425                Ok((this_object.clone(), var_args, #(args.#next_tokens().unwrap(),)*))
426            }
427
428            fn lang_parameter_count() -> usize {
429                #count_plus_1_lit
430            }
431
432            fn is_method() -> bool {
433                true
434            }
435        }
436
437        //This arg + args + VarArgs
438        impl FromLangArgs for (LangObjectRef, #(#args_tokens,)* Vec<DataObjectRef>,) {
439            fn from_lang_args(
440                this_object: OptionLangObjectRef,
441                args: Vec<DataObjectRef>,
442            ) -> Result<Self> {
443                if args.len() < #count_lit {
444                    return Err(NativeError::new("Not enough arguments for native function", None));
445                }
446
447                let Some(this_object) = this_object.as_ref() else {
448                    return Err(NativeError::new("This object must be set for native function with a this parameter", None));
449                };
450
451                let mut args = args;
452                let var_args = args.split_off(#count_lit);
453
454                let mut args = args.drain(..);
455
456                Ok((this_object.clone(), #(args.#next_tokens().unwrap(),)* var_args,))
457            }
458
459            fn lang_parameter_count() -> usize {
460                #count_plus_1_lit
461            }
462
463            fn is_method() -> bool {
464                true
465            }
466        }
467    })
468}
469
470#[doc(hidden)]
471#[proc_macro]
472pub fn internal_native_function_adapter_impl(
473    args: TokenStream,
474) -> TokenStream {
475    let count = syn::parse_macro_input!(args as syn::LitInt).base10_parse::<usize>();
476    let count = match count {
477        Ok(v) => v,
478        Err(e) => {
479            return TokenStream::from(e.into_compile_error());
480        }
481    };
482
483    let index_literal_tokens = (0..count).
484            map(proc_macro2::Literal::usize_unsuffixed).
485            collect::<Vec<_>>();
486
487    let generic_type_tokens = (0..count).
488            map(|i| proc_macro2::Ident::new(
489                &format!("T{}", i + 1),
490                Span::mixed_site(),
491            )).
492            collect::<Vec<_>>();
493
494    TokenStream::from(quote! {
495        impl<
496            #(#generic_type_tokens,)*
497            Ret: ReturnType,
498            F: Fn(&mut Interpreter, #(#generic_type_tokens,)*) -> Ret + 'static,
499        > ConvertToFuncTrait<Box<dyn Fn(&mut Interpreter, #(#generic_type_tokens,)*) -> Ret + 'static>> for F {
500            #[inline(always)]
501            fn func_trait(self) -> Box<dyn Fn(&mut Interpreter, #(#generic_type_tokens,)*) -> Ret + 'static> {
502                Box::new(self)
503            }
504        }
505
506        impl<
507            #(#generic_type_tokens,)*
508            Ret: ReturnType,
509        > NativeFunctionAdapter for Box<dyn Fn(&mut Interpreter, #(#generic_type_tokens,)*) -> Ret> where
510                (#(#generic_type_tokens,)*): FromLangArgs,
511        {
512            fn lang_call(
513                &self,
514                interpreter: &mut Interpreter,
515                this_object: OptionLangObjectRef,
516                args: Vec<DataObjectRef>,
517            ) -> Result<OptionDataObjectRef> {
518                let args = <(#(#generic_type_tokens,)*)>::from_lang_args(this_object, args)?;
519
520                self(interpreter, #(args.#index_literal_tokens,)*).into()
521            }
522
523            fn lang_parameter_count(&self) -> usize {
524                <(#(#generic_type_tokens,)*)>::lang_parameter_count()
525            }
526
527            fn is_method(&self) -> bool {
528                <(#(#generic_type_tokens,)*)>::is_method()
529            }
530        }
531
532        impl<
533            #(#generic_type_tokens,)*
534            Ret: ReturnType,
535        > private::Sealed for Box<dyn Fn(&mut Interpreter, #(#generic_type_tokens,)*) -> Ret> where
536                (#(#generic_type_tokens,)*): FromLangArgs,
537        {}
538    })
539}