lamellar_impl/
lib.rs

1extern crate proc_macro;
2
3mod am_data;
4
5mod parse;
6mod replace;
7
8mod array_ops;
9mod array_reduce;
10
11mod gen_am;
12mod gen_am_group;
13
14mod field_info;
15
16use am_data::derive_am_data;
17
18use proc_macro::TokenStream;
19use proc_macro_error::{abort, emit_error, proc_macro_error};
20use quote::{quote, quote_spanned, ToTokens};
21use syn::parse_macro_input;
22use syn::punctuated::Punctuated;
23use syn::spanned::Spanned;
24// use syn::Meta;
25// use syn::visit_mut::VisitMut;
26use syn::parse::{Parse, ParseStream, Result};
27use syn::Token;
28fn type_name(ty: &syn::Type) -> Option<String> {
29    match ty {
30        syn::Type::Path(syn::TypePath { qself: None, path }) => {
31            Some(path.segments.last().unwrap().ident.to_string())
32        }
33        _ => None,
34    }
35}
36
37#[allow(dead_code)]
38fn get_impl_associated_type(name: String, tys: &Vec<syn::ImplItem>) -> Option<syn::Type> {
39    for ty in tys {
40        match ty {
41            syn::ImplItem::Type(ref item) => {
42                if item.ident.to_string() == name {
43                    return Some(item.ty.clone());
44                }
45            }
46            _ => (),
47        }
48    }
49    None
50}
51
52fn get_return_of_method(name: String, tys: &Vec<syn::ImplItem>) -> Option<syn::Type> {
53    for ty in tys {
54        match ty {
55            syn::ImplItem::Fn(ref item) => {
56                if item.sig.asyncness.is_some() {
57                    if item.sig.ident.to_string() == name {
58                        match item.sig.output.clone() {
59                            syn::ReturnType::Default => {
60                                return None;
61                            }
62                            syn::ReturnType::Type(_, item) => {
63                                return Some(*item);
64                            }
65                        }
66                    }
67                } else {
68                    abort!(item.sig.fn_token.span(),"implementing lamellar::am expects the exec function to be async (e.g. 'async fn exec(...)')")
69                }
70            }
71            _ => (),
72        }
73    }
74    None
75}
76
77fn get_impl_method(name: String, tys: &Vec<syn::ImplItem>) -> Option<syn::Block> {
78    for ty in tys {
79        match ty {
80            syn::ImplItem::Fn(ref item) => {
81                if item.sig.ident.to_string() == name {
82                    return Some(item.block.clone());
83                }
84            }
85            _ => (),
86        }
87    }
88    None
89}
90
91fn get_expr(stmt: &syn::Stmt) -> Option<syn::Expr> {
92    let expr = match stmt {
93        syn::Stmt::Expr(expr, semi) => match expr.clone() {
94            syn::Expr::Return(expr) => Some(*(expr.expr.unwrap())),
95            _ => {
96                if semi.is_some() {
97                    None
98                } else {
99                    Some(expr.clone())
100                }
101            }
102        },
103        syn::Stmt::Macro(mac) => {
104            abort!(mac.span(),"we currently do not support macros in return position, assign macro output to a variable, and return the variable");
105        }
106        _ => {
107            println!("something else!");
108            None
109        }
110    };
111    expr
112}
113
114#[derive(Clone)]
115enum AmType {
116    NoReturn,
117    ReturnData(syn::Type),
118    ReturnAm(syn::Type, proc_macro2::TokenStream),
119}
120
121fn get_return_am_return_type(
122    args: &Punctuated<syn::Meta, syn::Token![,]>,
123) -> Option<(proc_macro2::TokenStream, proc_macro2::TokenStream)> {
124    for arg in args.iter() {
125        let arg_str = arg.to_token_stream().to_string();
126        if arg_str.contains("return_am") {
127            let mut the_am = arg_str
128                .split("return_am")
129                .collect::<Vec<&str>>()
130                .last()
131                .expect("error in lamellar::am argument")
132                .trim_matches(&['=', ' ', '"'][..])
133                .to_string();
134            let mut return_type = "".to_string();
135            if the_am.find("->") != None {
136                let temp = the_am.split("->").collect::<Vec<&str>>();
137                return_type = temp
138                    .last()
139                    .expect("error in lamellar::am argument")
140                    .trim()
141                    .to_string();
142                the_am = temp[0].trim_matches(&[' ', '"'][..]).to_string();
143            }
144            let ret_am_type: syn::Type = syn::parse_str(&the_am).expect("invalid type");
145            if return_type.len() > 0 {
146                // let ident = syn::Ident::new(&return_type, Span::call_site());
147                let ret_type: syn::Type = syn::parse_str(&return_type).expect("invalid type");
148                return Some((
149                    quote_spanned! {arg.span() => #ret_am_type},
150                    quote_spanned! {arg.span() => #ret_type},
151                ));
152            } else {
153                return Some((quote! {#ret_am_type}, quote! {()}));
154            }
155        }
156    }
157    None
158}
159
160fn check_for_am_group(args: &Punctuated<syn::Meta, syn::Token![,]>) -> bool {
161    for arg in args.iter() {
162        let t = arg.to_token_stream().to_string();
163        if t.contains("AmGroup") {
164            if t.contains("(") {
165                let attrs = &t[t.find("(").unwrap()
166                    ..t.find(")")
167                        .expect("missing \")\" in when declaring ArrayOp macro")
168                        + 1];
169                if attrs.contains("false") {
170                    return false;
171                }
172            }
173        }
174    }
175    true
176}
177
178/// # Examples
179///
180///```
181/// use lamellar::active_messaging::prelude::*;
182/// use lamellar::darc::prelude*;
183///
184/// #[AmData(Debug,Clone)]
185/// struct HelloWorld {
186///    originial_pe: usize,
187///    #[AmData(static)]
188///    msg: Darc<String>,
189/// }
190///
191/// #[lamellar::am]
192/// impl LamellarAM for HelloWorld {
193///     async fn exec(self) {
194///         println!(
195///             "{:?}}  on PE {:?} of {:?} using thread {:?}, received from PE {:?}",
196///             self.msg,
197///             lamellar::current_pe,
198///             lamellar::num_pes,
199///             std::thread::current().id(),
200///             self.originial_pe.lock(),
201///         );
202///     }
203/// }
204/// fn main() {
205///     let world = lamellar::LamellarWorldBuilder::new().build();
206///     let my_pe = world.my_pe();
207///     world.barrier();
208///     let msg = Darc::<String>::new(&world, "Hello World".to_string());
209///     //Send a Hello World Active Message to all pes
210///     let request = world.exec_am_all(HelloWorld {
211///         originial_pe: my_pe,
212///         msg: msg,
213///     });
214///
215///     //wait for the request to complete
216///     request.block();
217/// } //when world drops there is an implicit world.barrier() that occurs
218///```
219#[allow(non_snake_case)]
220#[proc_macro_error]
221#[proc_macro_attribute]
222pub fn AmData(args: TokenStream, input: TokenStream) -> TokenStream {
223    let args =
224        parse_macro_input!(args with Punctuated<syn::Meta, syn::Token![,]>::parse_terminated);
225    derive_am_data(input, args, quote! {__lamellar}, false, false, false)
226}
227
228///```
229/// use lamellar::active_messaging::prelude::*;
230///
231/// #[AmLocalData(Debug,Clone)]
232/// struct HelloWorld {
233///     originial_pe: Arc<Mutex<usize>>, //This would not be allowed in a non-local AM as Arc<Mutex<<>> is not (de)serializable
234/// }
235///
236/// #[lamellar::local_am]
237/// impl LamellarAM for HelloWorld {
238///     async fn exec(self) {
239///         println!(
240///             "Hello World  on PE {:?} of {:?} using thread {:?}, received from PE {:?}",
241///             lamellar::current_pe,
242///             lamellar::num_pes,
243///             std::thread::current().id(),
244///             self.originial_pe.lock(),
245///         );
246///     }
247/// }
248/// fn main() {
249///     let world = lamellar::LamellarWorldBuilder::new().build();
250///     let my_pe = Arc::new(Mutex::new(world.my_pe()));
251///     world.barrier();
252///
253///     let request = world.exec_am_local(HelloWorld {
254///         originial_pe: my_pe,
255///     });
256///
257///     //wait for the request to complete
258///     request.block();
259/// } //when world drops there is an implicit world.barrier() that occurs
260///```
261#[allow(non_snake_case)]
262#[proc_macro_error]
263#[proc_macro_attribute]
264pub fn AmLocalData(args: TokenStream, input: TokenStream) -> TokenStream {
265    let args =
266        parse_macro_input!(args with Punctuated<syn::Meta, syn::Token![,]>::parse_terminated);
267    derive_am_data(input, args, quote! {__lamellar}, true, false, false)
268}
269
270#[allow(non_snake_case)]
271#[proc_macro_error]
272#[proc_macro_attribute]
273pub fn AmGroupData(args: TokenStream, input: TokenStream) -> TokenStream {
274    let args =
275        parse_macro_input!(args with Punctuated<syn::Meta, syn::Token![,]>::parse_terminated);
276    derive_am_data(input, args, quote! {__lamellar}, false, true, false)
277}
278
279//#[doc(hidden)]
280#[allow(non_snake_case)]
281#[proc_macro_error]
282#[proc_macro_attribute]
283pub fn AmDataRT(args: TokenStream, input: TokenStream) -> TokenStream {
284    let args =
285        parse_macro_input!(args with Punctuated<syn::Meta, syn::Token![,]>::parse_terminated);
286    derive_am_data(input, args, quote! {crate}, false, false, true)
287}
288
289//#[doc(hidden)]
290#[allow(non_snake_case)]
291#[proc_macro_error]
292#[proc_macro_attribute]
293pub fn AmLocalDataRT(args: TokenStream, input: TokenStream) -> TokenStream {
294    let args =
295        parse_macro_input!(args with Punctuated<syn::Meta, syn::Token![,]>::parse_terminated);
296    derive_am_data(input, args, quote! {crate}, true, false, true)
297}
298
299fn parse_am(
300    args: TokenStream,
301    input: TokenStream,
302    local: bool,
303    rt: bool,
304    _am_group: bool,
305) -> TokenStream {
306    let args =
307        parse_macro_input!(args with Punctuated<syn::Meta, syn::Token![,]>::parse_terminated);
308
309    // let args = args.to_string();
310    // if args.len() > 0 {
311    //     if !args.starts_with("return_am") {
312    //         abort!(args.span(),"#[lamellar::am] only accepts an (optional) argument of the form:
313    //         #[lamellar::am(return_am = \"<am to exec upon return>\")]");
314    //     }
315    // }
316    // println!("args: {:?}", args);
317    let input: syn::Item = parse_macro_input!(input);
318
319    let lamellar = if rt {
320        // quote::format_ident!("crate")
321        quote! {crate}
322    } else {
323        // quote::format_ident!("__lamellar")
324        quote! {__lamellar}
325    };
326
327    let am_data_header = if rt {
328        if !local {
329            quote! {#[lamellar_impl::AmDataRT]}
330        } else {
331            quote! {#[lamellar_impl::AmLocalDataRT]}
332        }
333    } else {
334        if !local {
335            quote! {#[#lamellar::AmData]}
336        } else {
337            quote! {#[#lamellar::AmLocalData]}
338        }
339    };
340
341    let am_group_data_header = quote! {#[#lamellar::AmGroupData]};
342    let create_am_group = check_for_am_group(&args);
343
344    let output = match input.clone() {
345        syn::Item::Impl(input) => {
346            let output = get_return_of_method("exec".to_string(), &input.items);
347            match output {
348                Some(output) => {
349                    if let Some((return_am, return_output)) = get_return_am_return_type(&args) {
350                        if return_am.to_string() != output.to_token_stream().to_string() {
351                            emit_error!(
352                                return_am.span(),
353                                "am specified in attribute {} does not match return type {}",
354                                return_am,
355                                output.to_token_stream().to_string()
356                            );
357                            abort!(
358                                output.span(),
359                                "am specified in attribute {} does not match return type {}",
360                                return_am,
361                                output.to_token_stream().to_string()
362                            );
363                        }
364                        let mut impls = gen_am::generate_am(
365                            &input,
366                            local,
367                            AmType::ReturnAm(output.clone(), return_output.clone()),
368                            &lamellar,
369                            &am_data_header,
370                        );
371                        if !rt && !local && create_am_group {
372                            impls.extend(gen_am_group::generate_am_group(
373                                &input,
374                                local,
375                                AmType::ReturnAm(output.clone(), return_output.clone()),
376                                &lamellar,
377                                &am_group_data_header,
378                            ));
379                        }
380                        impls
381                    } else {
382                        let mut impls = gen_am::generate_am(
383                            &input,
384                            local,
385                            AmType::ReturnData(output.clone()),
386                            &lamellar,
387                            &am_data_header,
388                        );
389                        if !rt && !local && create_am_group {
390                            impls.extend(gen_am_group::generate_am_group(
391                                &input,
392                                local,
393                                AmType::ReturnData(output.clone()),
394                                &lamellar,
395                                &am_group_data_header,
396                            ));
397                        }
398                        impls
399                    }
400                }
401
402                None => {
403                    let mut impls = gen_am::generate_am(
404                        &input,
405                        local,
406                        AmType::NoReturn,
407                        &lamellar,
408                        &am_data_header,
409                    );
410                    if !rt && !local && create_am_group {
411                        impls.extend(gen_am_group::generate_am_group(
412                            &input,
413                            local,
414                            AmType::NoReturn,
415                            &lamellar,
416                            &am_group_data_header,
417                        ));
418                    }
419                    impls
420                }
421            }
422        }
423        _ => {
424            println!("lamellar am attribute only valid for impl blocks");
425            let output = quote! { #input };
426            output.into()
427        }
428    };
429    output
430}
431
432/// # Examples
433///
434///```
435/// use lamellar::active_messaging::prelude::*;
436/// use lamellar::darc::prelude::*;
437///
438/// #[AmData(Debug,Clone)]
439/// struct HelloWorld {
440///    originial_pe: usize,
441///    #[AmData(static)]
442///    msg: Darc<String>,
443/// }
444///
445/// #[lamellar::am]
446/// impl LamellarAM for HelloWorld {
447///     async fn exec(self) {
448///         println!(
449///             "{:?}}  on PE {:?} of {:?} using thread {:?}, received from PE {:?}",
450///             self.msg,
451///             lamellar::current_pe,
452///             lamellar::num_pes,
453///             std::thread::current().id(),
454///             self.originial_pe.lock(),
455///         );
456///     }
457/// }
458/// fn main() {
459///     let world = lamellar::LamellarWorldBuilder::new().build();
460///     let my_pe = world.my_pe();
461///     world.barrier();
462///     let msg = Darc::<String>::new(&world, "Hello World".to_string());
463///     //Send a Hello World Active Message to all pes
464///     let request = world.exec_am_all(HelloWorld {
465///         originial_pe: my_pe,
466///         msg: msg,
467///     });
468///
469///     //wait for the request to complete
470///     request.block();
471/// } //when world drops there is an implicit world.barrier() that occurs
472///```
473#[proc_macro_error]
474#[proc_macro_attribute]
475pub fn am(args: TokenStream, input: TokenStream) -> TokenStream {
476    parse_am(args, input, false, false, true)
477}
478
479//#[doc(hidden)]
480#[proc_macro_error]
481#[proc_macro_attribute]
482pub fn am_group(args: TokenStream, input: TokenStream) -> TokenStream {
483    parse_am(args, input, false, false, false)
484}
485
486/// # Examples
487///
488///```
489/// use lamellar::active_messaging::prelude::*;
490///
491/// #[AmLocalData(Debug,Clone)]
492/// struct HelloWorld {
493///     originial_pe: Arc<Mutex<usize>>, //This would not be allowed in a non-local AM as Arc<Mutex<<>> is not (de)serializable
494/// }
495///
496/// #[lamellar::local_am]
497/// impl LamellarAM for HelloWorld {
498///     async fn exec(self) {
499///         println!(
500///             "Hello World  on PE {:?} of {:?} using thread {:?}, received from PE {:?}",
501///             lamellar::current_pe,
502///             lamellar::num_pes,
503///             std::thread::current().id(),
504///             self.originial_pe.lock(),
505///         );
506///     }
507/// }
508/// fn main() {
509///     let world = lamellar::LamellarWorldBuilder::new().build();
510///     let my_pe = Arc::new(Mutex::new(world.my_pe()));
511///     world.barrier();
512///
513///     let request = world.exec_am_local(HelloWorld {
514///         originial_pe: my_pe,
515///     });
516///
517///     //wait for the request to complete
518///     request.block();
519/// } //when world drops there is an implicit world.barrier() that occurs
520///```
521#[proc_macro_error]
522#[proc_macro_attribute]
523pub fn local_am(args: TokenStream, input: TokenStream) -> TokenStream {
524    parse_am(args, input, true, false, false)
525}
526
527//#[doc(hidden)]
528#[proc_macro_error]
529#[proc_macro_attribute]
530pub fn rt_am(args: TokenStream, input: TokenStream) -> TokenStream {
531    parse_am(args, input, false, true, false)
532}
533
534//#[doc(hidden)]
535#[proc_macro_error]
536#[proc_macro_attribute]
537pub fn rt_am_local(args: TokenStream, input: TokenStream) -> TokenStream {
538    parse_am(args, input, true, true, false)
539}
540
541//#[doc(hidden)]
542#[proc_macro_error]
543#[proc_macro_derive(Dist)]
544pub fn derive_dist(input: TokenStream) -> TokenStream {
545    let mut output = quote! {};
546
547    let input = parse_macro_input!(input as syn::DeriveInput);
548    let name = input.ident;
549    output.extend(quote! {
550        const _: () = {
551            extern crate lamellar as __lamellar;
552            impl __lamellar::Dist for #name {}
553        };
554    });
555
556    // output.extend(create_ops(name.clone(), &write_array_types, false, false));
557    TokenStream::from(output)
558}
559
560#[proc_macro_error]
561#[proc_macro]
562pub fn register_reduction(item: TokenStream) -> TokenStream {
563    array_reduce::__register_reduction(item)
564}
565
566// probalby should turn this into a derive macro
567// #[proc_macro_error]
568// #[proc_macro]
569// pub fn generate_reductions_for_type(item: TokenStream) -> TokenStream {
570//     array_reduce::__generate_reductions_for_type(item)
571// }
572
573//#[doc(hidden)]
574#[proc_macro_error]
575#[proc_macro]
576pub fn generate_reductions_for_type_rt(item: TokenStream) -> TokenStream {
577    array_reduce::__generate_reductions_for_type_rt(item)
578}
579
580// / This macro automatically implements various LamellarArray "Op" traits for user defined types
581// /
582// / The following "Op" traits will be implemented:
583// / - [AccessOps][lamellar::array::AccessOps]
584// / - [ArithmeticOps][lamellar::array::AccessOps]
585// / - [BitWiseOps][lamellar::array::AccessOps]
586// / - [CompareExchangeEpsilonOps][lamellar::array::AccessOps]
587// / - [CompareExchangeOps][lamellar::array::AccessOps]
588// /
589// / The required trait bounds can be found by viewing each "Op" traits documentation.
590// / Generally though the type must be able to be used in an active message,
591// / # Examples
592// /
593// /```
594// / use lamellar::array::prelude::*;
595// #[proc_macro_error]
596// #[proc_macro]
597// pub fn generate_ops_for_type(item: TokenStream) -> TokenStream {
598//     array_ops::__generate_ops_for_type(item)
599// }
600
601//#[doc(hidden)]
602#[proc_macro_error]
603#[proc_macro]
604pub fn generate_ops_for_type_rt(item: TokenStream) -> TokenStream {
605    array_ops::__generate_ops_for_type_rt(item)
606}
607
608//#[doc(hidden)]
609#[proc_macro_error]
610#[proc_macro]
611pub fn generate_ops_for_bool_rt(_item: TokenStream) -> TokenStream {
612    array_ops::__generate_ops_for_bool_rt()
613}
614
615///
616/// This derive macro is intended to be used with the [macro@AmData] attribute macro to enable a user defined type to be used in ActiveMessages.
617///
618/// # Examples
619///
620/// ```
621/// // this import includes everything we need
622/// use lamellar::array::prelude::*;
623///
624///
625/// #[lamellar::AmData(
626///     // Lamellar traits
627///     ArrayOps(Arithmetic,CompExEps,Shift), // needed to derive various LamellarArray Op traits (provided as a list)
628///     Default,       // needed to be able to initialize a LamellarArray
629///     //  Notice we use `lamellar::AmData` instead of `derive`
630///     //  for common traits, e.g. Debug, Clone.    
631///     PartialEq,     // needed for CompareExchangeEpsilonOps
632///     PartialOrd,    // needed for CompareExchangeEpsilonOps
633///     Debug,         // any addition traits you want derived
634///     Clone,
635/// )]
636/// struct Custom {
637///     int: usize,
638///     float: f32,
639/// }
640///
641/// // We need to impl various arithmetic ops if we want to be able to
642/// // perform remote arithmetic operations with this type
643/// impl std::ops::AddAssign for Custom {
644///     fn add_assign(&mut self, other: Self) {
645///         *self = Self {
646///             int: self.int + other.int,
647///             float: self.float + other.float,
648///         }
649///     }
650/// }
651///
652/// impl std::ops::SubAssign for Custom {
653///     fn sub_assign(&mut self, other: Self) {
654///         *self = Self {
655///             int: self.int - other.int,
656///             float: self.float - other.float,
657///         }
658///     }
659/// }
660///
661/// impl std::ops::Sub for Custom {
662///     type Output = Self;
663///     fn sub(self, other: Self) -> Self {
664///         Self {
665///             int: self.int - other.int,
666///             float: self.float - other.float,
667///         }
668///     }
669/// }
670///
671/// impl std::ops::MulAssign for Custom {
672///     fn mul_assign(&mut self, other: Self) {
673///         *self = Self {
674///             int: self.int * other.int,
675///             float: self.float * other.float,
676///         }
677///     }
678/// }
679///
680/// impl std::ops::DivAssign for Custom {
681///     fn div_assign(&mut self, other: Self) {
682///         *self = Self {
683///             int: self.int / other.int,
684///             float: self.float / other.float,
685///         }
686///     }
687/// }
688/// impl std::ops::ShlAssign for Custom {
689///     fn shl_assign(&mut self,other: Custom){
690///         self.int <<= other.int;
691///     }
692/// }
693///
694/// impl std::ops::ShrAssign for Custom {
695///     fn shr_assign(&mut self,other: Custom){
696///         self.int >>= other.int;
697///     }
698/// }
699///
700/// fn main(){
701///
702///     // initialize
703///     // -----------
704///     
705///     let world = LamellarWorldBuilder::new().build(); // the world
706///     
707///     let array =  // the atomic distributed array
708///         AtomicArray::<Custom>::new(&world,3,Distribution::Block).block();
709///
710///     println!();
711///     println!("initialize a length-3 array:\n");  // print the entries
712///     array.dist_iter()
713///         .enumerate()
714///         .for_each(|(i,entry)| println!("entry {:?}: {:?}", i, entry ) );
715///     array.wait_all();
716///     
717///     // call various operations on the array!
718///     // -------------------------------------
719///
720///     world.block_on( async move {  // we will just use the world as our future driver so we dont have to deal with cloning array
721///
722///         println!();
723///         println!("add (1, 0.01) to the first entry:\n");
724///         let val = Custom{int: 1, float: 0.01};
725///         array.add(0, val ).await;
726///         array.dist_iter().enumerate().for_each(|(i,entry)| println!("entry {:?}: {:?}", i, entry ) );
727///         array.wait_all();
728///
729///         println!();
730///         println!("batch compare/exchange:");    
731///         let indices = vec![0,1,2,];
732///         let current = val;
733///         let new = Custom{int: 1, float: 0.0};
734///         let epsilon = Custom{int: 0, float: 0.01};
735///         let _results = array.batch_compare_exchange_epsilon(indices,current,new,epsilon).await;
736///         println!();
737///         println!("(1) the updatd array");
738///         array.dist_iter().enumerate().for_each(|(i,entry)| println!("entry {:?}: {:?}", i, entry ) );
739///         array.wait_all();
740///         println!();
741///         println!("(2) the return values");        
742///         for (i, entry) in _results.iter().enumerate() { println!("entry {:?}: {:?}", i, entry ) }
743///     });
744///
745///     // inspect the results
746///     // -------------------------------------    
747///     // NB:  because thewe're working with multithreaded async
748///     //      environments, entries may be printed out of order
749///     //
750///     // initialize a length-3 array:
751///     //
752///     // entry 1: Custom { int: 0, float: 0.0 }
753///     // entry 0: Custom { int: 0, float: 0.0 }
754///     // entry 2: Custom { int: 0, float: 0.0 }
755///     //
756///     // add (1, 0.01) to the first entry:
757///     //
758///     // entry 0: Custom { int: 1, float: 0.01 }
759///     // entry 2: Custom { int: 0, float: 0.0 }
760///     // entry 1: Custom { int: 0, float: 0.0 }
761///     //
762///     // batch compare/exchange:
763///     //
764///     // (1) the updatd array
765///     // entry 0: Custom { int: 1, float: 0.0 }
766///     // entry 1: Custom { int: 0, float: 0.0 }
767///     // entry 2: Custom { int: 0, float: 0.0 }
768///     //
769///     // (2) the return values
770///     // entry 0: Ok(Custom { int: 1, float: 0.01 })
771///     // entry 1: Err(Custom { int: 0, float: 0.0 })
772///     // entry 2: Err(Custom { int: 0, float: 0.0 })   
773/// }
774/// ```
775#[proc_macro_error]
776#[proc_macro_derive(ArrayOps, attributes(array_ops))]
777pub fn derive_arrayops(input: TokenStream) -> TokenStream {
778    array_ops::__derive_arrayops(input)
779}
780
781struct AmGroups {
782    am: syn::TypePath,
783    team: syn::Expr,
784}
785
786impl Parse for AmGroups {
787    fn parse(input: ParseStream) -> Result<Self> {
788        // println!("in am groups parse");
789        let am = if let Ok(syn::Type::Path(ty)) = input.parse() {
790            ty.clone()
791        } else {
792            abort!(input.span(),"typed_am_group expects the first argument to be Struct name if your active message e.g. 
793            #[AmData]
794            Struct MyAmStruct {}
795            ...
796            typed_am_group!(MyAmStruct,...)");
797        };
798        // println!("am: {:?}",am);
799        input.parse::<Token![,]>()?;
800        let team_error_msg = "typed_am_group expects a LamellarWorld or LamellarTeam instance as it's only argument e.g. 
801        'typed_am_group!(...,&world)', 
802        'typed_am_group!(...,world.clone())'
803        'typed_am_group!(...,&team)', 
804        'typed_am_group!(...,team.clone())'";
805        let team = if let Ok(expr) = input.parse::<syn::Expr>() {
806            match expr {
807                syn::Expr::Path(_) => expr.clone(),
808                syn::Expr::Reference(_) => expr.clone(),
809                syn::Expr::MethodCall(_) => expr.clone(),
810                _ => abort!(input.span(), team_error_msg),
811            }
812        } else {
813            abort!(input.span(), team_error_msg);
814        };
815        Ok(AmGroups { am, team })
816    }
817}
818
819/// The macro used to create an new instance of a `TypedAmGroup` which is an Active Message Group that can only include AMs of a specific type (but this type can return data).
820/// Data is returned in the same order as the AMs were added
821/// (You can think of this as similar to `Vec<T>`)
822/// This macro which expects two parameters, the first being the type (name) of the AM and the second being a reference to a lamellar team.
823/// ```
824/// use lamellar::active_messaging::prelude::*;
825/// use lamellar::darc::prelude::*;
826/// use std::sync::atomic::AtomicUsize;
827/// #[AmData(Debug,Clone)]
828/// struct ExampleAm {
829///    cnt: Darc<AtomicUsize>,
830/// }
831/// #[lamellar::am]
832/// impl LamellarAm for ExampleAm{
833///     async fn exec(self) -> usize{
834///         self.cnt.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
835///     }
836/// }
837///
838/// fn main(){
839///     let world = lamellar::LamellarWorldBuilder::new().build();
840///     let my_pe = world.my_pe();
841///     let num_pes = world.num_pes();
842///
843///     if my_pe == 0 { // we only want to run this on PE0 for sake of illustration
844///         let am_group = typed_am_group!{ExampleAm,&world};
845///         let am = ExampleAm{cnt: 0};
846///         // add the AMs to the group
847///         // we can specify individual PEs to execute on or all PEs
848///         am_group.add_am_pe(0,am.clone());
849///         am_group.add_am_all(am.clone());
850///         am_group.add_am_pe(1,am.clone());
851///         am_group.add_am_all(am.clone());
852///
853///         //execute and await the completion of all AMs in the group
854///         let results = world.block_on(am_group.exec()); // we want to process the returned data
855///         //we can index into the results
856///         if let AmGroupResult::Pe((pe,val)) = results.at(2){
857///             assert_eq!(pe, 1); //the third add_am_* call in the group was to execute on PE1
858///             assert_eq!(val, 1); // this was the second am to execute on PE1 so the fetched value is 1
859///         }
860///         //or we can iterate over the results
861///         for res in results{
862///             match res{
863///                 AmGroupResult::Pe((pe,val)) => { println!("{} from PE{}",val,pe)},
864///                 AmGroupResult::All(val) => { println!("{} on all PEs",val)},
865///             }
866///         }
867///     }
868/// }
869///```
870/// Expected output on each PE1:
871/// ```text
872/// 0 from PE0
873/// [1,0] on all PEs
874/// 1 from PE1
875/// [2,2] on all PEs
876/// ```
877/// ### Static Members
878/// In the above code, the `ExampleAm` stuct contains a member that is a `Darc` (Distributed Arc).
879/// In order to properly calculate distributed reference counts Darcs implements specialized Serialize and Deserialize operations.
880/// While, the cost to any single serialization/deserialization operation is small, doing this for every active message containing
881/// a Darc can become expensive.
882///
883/// In certain cases Typed Am Groups can avoid the repeated serialization/deserialization of Darc members if the user guarantees
884/// that every Active Message in the group is using a reference to the same Darc. In this case, we simply would only need
885/// to serialize the Darc once for each PE it gets sent to.
886///
887/// This can be accomplished by using the [macro@AmData] attribute macro with the `static` keyword passed in as an argument as illustrated below:
888/// ```
889/// use lamellar::active_messaging::prelude::*;
890/// use lamellar::darc::prelude::*;
891/// use std::sync::atomic::AtomicUsize;
892/// #[AmData(Debug,Clone)]
893/// struct ExampleAm {
894///    #[AmData(static)]
895///    cnt: Darc<AtomicUsize>,
896/// }
897///```
898/// Other than the addition of `#[AmData(static)]` the rest of the code as the previous example would be the same.
899#[proc_macro_error]
900#[proc_macro]
901pub fn typed_am_group(input: TokenStream) -> TokenStream {
902    // println!("typed_am_group {:?}",input);
903    let am_group: AmGroups = syn::parse(input).unwrap();
904    let am_type = am_group.am;
905    let team = am_group.team;
906    quote! {
907        #am_type::create_am_group(#team)
908    }
909    .into()
910}