fn derive_operation()

in amzn-smt-ir-derive/src/lib.rs [69:183]


fn derive_operation(s: &synstructure::Structure) -> TokenStream {
    let smt_ir = smt_ir_crate_path();

    #[allow(non_snake_case)]
    let (Term, Logic, Operation, Parse, NumArgs, InvalidOp, QualIdentifier, IIndex, TryFrom, Vec) = (
        quote!(#smt_ir::Term),
        quote!(#smt_ir::Logic),
        quote!(#smt_ir::term::Operation),
        quote!(#smt_ir::term::args::Parse),
        quote!(#smt_ir::term::args::NumArgs),
        quote!(#smt_ir::term::InvalidOp),
        quote!(#smt_ir::QualIdentifier),
        quote!(#smt_ir::IIndex),
        quote!(std::convert::TryFrom),
        quote!(std::vec::Vec),
    );

    let mut bindings = vec![];

    let parse_match_arms: Vec<_> = (s.variants().iter())
        .enumerate()
        .map(|(idx, variant)| {
            let symbol = variant_symbol(variant);
            let mut num_indices = None;
            let mut min_args = vec![];
            let mut max_args = vec![];

            // For each field in the variant, try to parse it from the iterator of arguments
            let constructed = variant.construct(|field, _| {
                let ty = &field.ty;

                // Check for array of indices
                if let Some(len) = index_array(ty) {
                    num_indices = Some(len.clone());
                    quote! {{
                        let indices: std::vec::Vec<_> = func.indices().iter().map(#IIndex::from).collect();
                        #TryFrom::try_from(indices).unwrap()
                    }}
                } else {
                    min_args.push(quote!(<#ty as #NumArgs>::MIN_ARGS));
                    max_args.push(quote!(<#ty as #NumArgs>::MAX_ARGS));
                    quote!(#Parse::from_iter(&mut iter).unwrap())
                }
            });

            let min_args = quote!((0 #(+ #min_args)*));
            let max_args = quote!((0 #(+ #max_args)*));
            let num_indices = num_indices.unwrap_or_else(|| parse_quote!(0));
            let min_args_ident = syn::Ident::new(&format!("MIN_ARGS_{}", idx), Span::call_site());
            let max_args_ident = syn::Ident::new(&format!("MAX_ARGS_{}", idx), Span::call_site());
            let num_indices_ident = syn::Ident::new(&format!("INDICES_{}", idx), Span::call_site());
            bindings.push(quote!(let #min_args_ident = #min_args;));
            bindings.push(quote!(let #max_args_ident = #max_args;));
            bindings.push(quote!(let #num_indices_ident = #num_indices;));

            // Construct a match arm e.g. `"and" => { ... }` where `...` constructs the variant from
            // a slice of arguments.
            quote! {
                (#symbol, num_args) if func.indices().len() == #num_indices_ident && (#min_args_ident..=#max_args_ident).contains(&num_args) => {
                    let mut iter = args.into_iter();
                    #constructed
                }
            }
        })
        .collect();

    let parse_fn = quote! {
        fn parse(func: #QualIdentifier, args: #Vec<#Term<L>>) -> std::result::Result<Self, #InvalidOp<L>> {
            #(#bindings)*
            #[deny(unreachable_patterns)]
            Ok(match (func.sym_str(), args.len()) {
                #(#parse_match_arms)*
                _ => return Err(#InvalidOp { func, args })
            })
        }
    };

    let func_match_arms = s.each_variant(|variant| {
        let symbol = variant_symbol(variant);
        quote!(#symbol.into())
    });

    let func_fn = quote! {
        fn func(&self) -> #smt_ir::ISymbol {
            match self {
                #func_match_arms
            }
        }
    };

    let mut where_clause = None;
    s.add_trait_bounds(
        &parse_quote!(#Parse<L>),
        &mut where_clause,
        synstructure::AddBounds::Fields,
    );
    if has_core_op_attr(s.ast()) {
        s.gen_impl(quote! {
            gen impl<L: #Logic> #Operation<L> for @Self
            #where_clause,
                <L as #Logic>::Op: #Operation<L>,
            {
                #parse_fn
                #func_fn
            }
        })
    } else {
        s.gen_impl(quote! {
            gen impl<L: #Logic> #Operation<L> for @Self #where_clause {
                #parse_fn
                #func_fn
            }
        })
    }
}