1fad3a1d3Sopenharmony_ciuse crate::{file, full, gen}; 2fad3a1d3Sopenharmony_ciuse anyhow::Result; 3fad3a1d3Sopenharmony_ciuse proc_macro2::{Ident, Span, TokenStream}; 4fad3a1d3Sopenharmony_ciuse quote::{format_ident, quote}; 5fad3a1d3Sopenharmony_ciuse syn::Index; 6fad3a1d3Sopenharmony_ciuse syn_codegen::{Data, Definitions, Features, Node, Type}; 7fad3a1d3Sopenharmony_ci 8fad3a1d3Sopenharmony_ciconst FOLD_SRC: &str = "src/gen/fold.rs"; 9fad3a1d3Sopenharmony_ci 10fad3a1d3Sopenharmony_cifn simple_visit(item: &str, name: &TokenStream) -> TokenStream { 11fad3a1d3Sopenharmony_ci let ident = gen::under_name(item); 12fad3a1d3Sopenharmony_ci let method = format_ident!("fold_{}", ident); 13fad3a1d3Sopenharmony_ci quote! { 14fad3a1d3Sopenharmony_ci f.#method(#name) 15fad3a1d3Sopenharmony_ci } 16fad3a1d3Sopenharmony_ci} 17fad3a1d3Sopenharmony_ci 18fad3a1d3Sopenharmony_cifn visit( 19fad3a1d3Sopenharmony_ci ty: &Type, 20fad3a1d3Sopenharmony_ci features: &Features, 21fad3a1d3Sopenharmony_ci defs: &Definitions, 22fad3a1d3Sopenharmony_ci name: &TokenStream, 23fad3a1d3Sopenharmony_ci) -> Option<TokenStream> { 24fad3a1d3Sopenharmony_ci match ty { 25fad3a1d3Sopenharmony_ci Type::Box(t) => { 26fad3a1d3Sopenharmony_ci let res = visit(t, features, defs, "e!(*#name))?; 27fad3a1d3Sopenharmony_ci Some(quote! { 28fad3a1d3Sopenharmony_ci Box::new(#res) 29fad3a1d3Sopenharmony_ci }) 30fad3a1d3Sopenharmony_ci } 31fad3a1d3Sopenharmony_ci Type::Vec(t) => { 32fad3a1d3Sopenharmony_ci let operand = quote!(it); 33fad3a1d3Sopenharmony_ci let val = visit(t, features, defs, &operand)?; 34fad3a1d3Sopenharmony_ci Some(quote! { 35fad3a1d3Sopenharmony_ci FoldHelper::lift(#name, |it| #val) 36fad3a1d3Sopenharmony_ci }) 37fad3a1d3Sopenharmony_ci } 38fad3a1d3Sopenharmony_ci Type::Punctuated(p) => { 39fad3a1d3Sopenharmony_ci let operand = quote!(it); 40fad3a1d3Sopenharmony_ci let val = visit(&p.element, features, defs, &operand)?; 41fad3a1d3Sopenharmony_ci Some(quote! { 42fad3a1d3Sopenharmony_ci FoldHelper::lift(#name, |it| #val) 43fad3a1d3Sopenharmony_ci }) 44fad3a1d3Sopenharmony_ci } 45fad3a1d3Sopenharmony_ci Type::Option(t) => { 46fad3a1d3Sopenharmony_ci let it = quote!(it); 47fad3a1d3Sopenharmony_ci let val = visit(t, features, defs, &it)?; 48fad3a1d3Sopenharmony_ci Some(quote! { 49fad3a1d3Sopenharmony_ci (#name).map(|it| #val) 50fad3a1d3Sopenharmony_ci }) 51fad3a1d3Sopenharmony_ci } 52fad3a1d3Sopenharmony_ci Type::Tuple(t) => { 53fad3a1d3Sopenharmony_ci let mut code = TokenStream::new(); 54fad3a1d3Sopenharmony_ci for (i, elem) in t.iter().enumerate() { 55fad3a1d3Sopenharmony_ci let i = Index::from(i); 56fad3a1d3Sopenharmony_ci let it = quote!((#name).#i); 57fad3a1d3Sopenharmony_ci let val = visit(elem, features, defs, &it).unwrap_or(it); 58fad3a1d3Sopenharmony_ci code.extend(val); 59fad3a1d3Sopenharmony_ci code.extend(quote!(,)); 60fad3a1d3Sopenharmony_ci } 61fad3a1d3Sopenharmony_ci Some(quote! { 62fad3a1d3Sopenharmony_ci (#code) 63fad3a1d3Sopenharmony_ci }) 64fad3a1d3Sopenharmony_ci } 65fad3a1d3Sopenharmony_ci Type::Syn(t) => { 66fad3a1d3Sopenharmony_ci fn requires_full(features: &Features) -> bool { 67fad3a1d3Sopenharmony_ci features.any.contains("full") && features.any.len() == 1 68fad3a1d3Sopenharmony_ci } 69fad3a1d3Sopenharmony_ci let mut res = simple_visit(t, name); 70fad3a1d3Sopenharmony_ci let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap(); 71fad3a1d3Sopenharmony_ci if requires_full(&target.features) && !requires_full(features) { 72fad3a1d3Sopenharmony_ci res = quote!(full!(#res)); 73fad3a1d3Sopenharmony_ci } 74fad3a1d3Sopenharmony_ci Some(res) 75fad3a1d3Sopenharmony_ci } 76fad3a1d3Sopenharmony_ci Type::Ext(t) if gen::TERMINAL_TYPES.contains(&&t[..]) => Some(simple_visit(t, name)), 77fad3a1d3Sopenharmony_ci Type::Ext(_) | Type::Std(_) | Type::Token(_) | Type::Group(_) => None, 78fad3a1d3Sopenharmony_ci } 79fad3a1d3Sopenharmony_ci} 80fad3a1d3Sopenharmony_ci 81fad3a1d3Sopenharmony_cifn node(traits: &mut TokenStream, impls: &mut TokenStream, s: &Node, defs: &Definitions) { 82fad3a1d3Sopenharmony_ci let under_name = gen::under_name(&s.ident); 83fad3a1d3Sopenharmony_ci let ty = Ident::new(&s.ident, Span::call_site()); 84fad3a1d3Sopenharmony_ci let fold_fn = format_ident!("fold_{}", under_name); 85fad3a1d3Sopenharmony_ci 86fad3a1d3Sopenharmony_ci let mut fold_impl = TokenStream::new(); 87fad3a1d3Sopenharmony_ci 88fad3a1d3Sopenharmony_ci match &s.data { 89fad3a1d3Sopenharmony_ci Data::Enum(variants) => { 90fad3a1d3Sopenharmony_ci let mut fold_variants = TokenStream::new(); 91fad3a1d3Sopenharmony_ci 92fad3a1d3Sopenharmony_ci for (variant, fields) in variants { 93fad3a1d3Sopenharmony_ci let variant_ident = Ident::new(variant, Span::call_site()); 94fad3a1d3Sopenharmony_ci 95fad3a1d3Sopenharmony_ci if fields.is_empty() { 96fad3a1d3Sopenharmony_ci fold_variants.extend(quote! { 97fad3a1d3Sopenharmony_ci #ty::#variant_ident => { 98fad3a1d3Sopenharmony_ci #ty::#variant_ident 99fad3a1d3Sopenharmony_ci } 100fad3a1d3Sopenharmony_ci }); 101fad3a1d3Sopenharmony_ci } else { 102fad3a1d3Sopenharmony_ci let mut bind_fold_fields = TokenStream::new(); 103fad3a1d3Sopenharmony_ci let mut fold_fields = TokenStream::new(); 104fad3a1d3Sopenharmony_ci 105fad3a1d3Sopenharmony_ci for (idx, ty) in fields.iter().enumerate() { 106fad3a1d3Sopenharmony_ci let binding = format_ident!("_binding_{}", idx); 107fad3a1d3Sopenharmony_ci 108fad3a1d3Sopenharmony_ci bind_fold_fields.extend(quote! { 109fad3a1d3Sopenharmony_ci #binding, 110fad3a1d3Sopenharmony_ci }); 111fad3a1d3Sopenharmony_ci 112fad3a1d3Sopenharmony_ci let owned_binding = quote!(#binding); 113fad3a1d3Sopenharmony_ci 114fad3a1d3Sopenharmony_ci fold_fields.extend( 115fad3a1d3Sopenharmony_ci visit(ty, &s.features, defs, &owned_binding).unwrap_or(owned_binding), 116fad3a1d3Sopenharmony_ci ); 117fad3a1d3Sopenharmony_ci 118fad3a1d3Sopenharmony_ci fold_fields.extend(quote!(,)); 119fad3a1d3Sopenharmony_ci } 120fad3a1d3Sopenharmony_ci 121fad3a1d3Sopenharmony_ci fold_variants.extend(quote! { 122fad3a1d3Sopenharmony_ci #ty::#variant_ident(#bind_fold_fields) => { 123fad3a1d3Sopenharmony_ci #ty::#variant_ident( 124fad3a1d3Sopenharmony_ci #fold_fields 125fad3a1d3Sopenharmony_ci ) 126fad3a1d3Sopenharmony_ci } 127fad3a1d3Sopenharmony_ci }); 128fad3a1d3Sopenharmony_ci } 129fad3a1d3Sopenharmony_ci } 130fad3a1d3Sopenharmony_ci 131fad3a1d3Sopenharmony_ci fold_impl.extend(quote! { 132fad3a1d3Sopenharmony_ci match node { 133fad3a1d3Sopenharmony_ci #fold_variants 134fad3a1d3Sopenharmony_ci } 135fad3a1d3Sopenharmony_ci }); 136fad3a1d3Sopenharmony_ci } 137fad3a1d3Sopenharmony_ci Data::Struct(fields) => { 138fad3a1d3Sopenharmony_ci let mut fold_fields = TokenStream::new(); 139fad3a1d3Sopenharmony_ci 140fad3a1d3Sopenharmony_ci for (field, ty) in fields { 141fad3a1d3Sopenharmony_ci let id = Ident::new(field, Span::call_site()); 142fad3a1d3Sopenharmony_ci let ref_toks = quote!(node.#id); 143fad3a1d3Sopenharmony_ci 144fad3a1d3Sopenharmony_ci let fold = visit(ty, &s.features, defs, &ref_toks).unwrap_or(ref_toks); 145fad3a1d3Sopenharmony_ci 146fad3a1d3Sopenharmony_ci fold_fields.extend(quote! { 147fad3a1d3Sopenharmony_ci #id: #fold, 148fad3a1d3Sopenharmony_ci }); 149fad3a1d3Sopenharmony_ci } 150fad3a1d3Sopenharmony_ci 151fad3a1d3Sopenharmony_ci if fields.is_empty() { 152fad3a1d3Sopenharmony_ci if ty == "Ident" { 153fad3a1d3Sopenharmony_ci fold_impl.extend(quote! { 154fad3a1d3Sopenharmony_ci let mut node = node; 155fad3a1d3Sopenharmony_ci let span = f.fold_span(node.span()); 156fad3a1d3Sopenharmony_ci node.set_span(span); 157fad3a1d3Sopenharmony_ci }); 158fad3a1d3Sopenharmony_ci } 159fad3a1d3Sopenharmony_ci fold_impl.extend(quote! { 160fad3a1d3Sopenharmony_ci node 161fad3a1d3Sopenharmony_ci }); 162fad3a1d3Sopenharmony_ci } else { 163fad3a1d3Sopenharmony_ci fold_impl.extend(quote! { 164fad3a1d3Sopenharmony_ci #ty { 165fad3a1d3Sopenharmony_ci #fold_fields 166fad3a1d3Sopenharmony_ci } 167fad3a1d3Sopenharmony_ci }); 168fad3a1d3Sopenharmony_ci } 169fad3a1d3Sopenharmony_ci } 170fad3a1d3Sopenharmony_ci Data::Private => { 171fad3a1d3Sopenharmony_ci if ty == "Ident" { 172fad3a1d3Sopenharmony_ci fold_impl.extend(quote! { 173fad3a1d3Sopenharmony_ci let mut node = node; 174fad3a1d3Sopenharmony_ci let span = f.fold_span(node.span()); 175fad3a1d3Sopenharmony_ci node.set_span(span); 176fad3a1d3Sopenharmony_ci }); 177fad3a1d3Sopenharmony_ci } 178fad3a1d3Sopenharmony_ci fold_impl.extend(quote! { 179fad3a1d3Sopenharmony_ci node 180fad3a1d3Sopenharmony_ci }); 181fad3a1d3Sopenharmony_ci } 182fad3a1d3Sopenharmony_ci } 183fad3a1d3Sopenharmony_ci 184fad3a1d3Sopenharmony_ci let fold_span_only = 185fad3a1d3Sopenharmony_ci s.data == Data::Private && !gen::TERMINAL_TYPES.contains(&s.ident.as_str()); 186fad3a1d3Sopenharmony_ci if fold_span_only { 187fad3a1d3Sopenharmony_ci fold_impl = quote! { 188fad3a1d3Sopenharmony_ci let span = f.fold_span(node.span()); 189fad3a1d3Sopenharmony_ci let mut node = node; 190fad3a1d3Sopenharmony_ci node.set_span(span); 191fad3a1d3Sopenharmony_ci node 192fad3a1d3Sopenharmony_ci }; 193fad3a1d3Sopenharmony_ci } 194fad3a1d3Sopenharmony_ci 195fad3a1d3Sopenharmony_ci traits.extend(quote! { 196fad3a1d3Sopenharmony_ci fn #fold_fn(&mut self, i: #ty) -> #ty { 197fad3a1d3Sopenharmony_ci #fold_fn(self, i) 198fad3a1d3Sopenharmony_ci } 199fad3a1d3Sopenharmony_ci }); 200fad3a1d3Sopenharmony_ci 201fad3a1d3Sopenharmony_ci impls.extend(quote! { 202fad3a1d3Sopenharmony_ci pub fn #fold_fn<F>(f: &mut F, node: #ty) -> #ty 203fad3a1d3Sopenharmony_ci where 204fad3a1d3Sopenharmony_ci F: Fold + ?Sized, 205fad3a1d3Sopenharmony_ci { 206fad3a1d3Sopenharmony_ci #fold_impl 207fad3a1d3Sopenharmony_ci } 208fad3a1d3Sopenharmony_ci }); 209fad3a1d3Sopenharmony_ci} 210fad3a1d3Sopenharmony_ci 211fad3a1d3Sopenharmony_cipub fn generate(defs: &Definitions) -> Result<()> { 212fad3a1d3Sopenharmony_ci let (traits, impls) = gen::traverse(defs, node); 213fad3a1d3Sopenharmony_ci let full_macro = full::get_macro(); 214fad3a1d3Sopenharmony_ci file::write( 215fad3a1d3Sopenharmony_ci FOLD_SRC, 216fad3a1d3Sopenharmony_ci quote! { 217fad3a1d3Sopenharmony_ci // Unreachable code is generated sometimes without the full feature. 218fad3a1d3Sopenharmony_ci #![allow(unreachable_code, unused_variables)] 219fad3a1d3Sopenharmony_ci #![allow( 220fad3a1d3Sopenharmony_ci clippy::match_wildcard_for_single_variants, 221fad3a1d3Sopenharmony_ci clippy::needless_match, 222fad3a1d3Sopenharmony_ci clippy::needless_pass_by_ref_mut, 223fad3a1d3Sopenharmony_ci )] 224fad3a1d3Sopenharmony_ci 225fad3a1d3Sopenharmony_ci #[cfg(any(feature = "full", feature = "derive"))] 226fad3a1d3Sopenharmony_ci use crate::gen::helper::fold::*; 227fad3a1d3Sopenharmony_ci use crate::*; 228fad3a1d3Sopenharmony_ci use proc_macro2::Span; 229fad3a1d3Sopenharmony_ci 230fad3a1d3Sopenharmony_ci #full_macro 231fad3a1d3Sopenharmony_ci 232fad3a1d3Sopenharmony_ci /// Syntax tree traversal to transform the nodes of an owned syntax tree. 233fad3a1d3Sopenharmony_ci /// 234fad3a1d3Sopenharmony_ci /// See the [module documentation] for details. 235fad3a1d3Sopenharmony_ci /// 236fad3a1d3Sopenharmony_ci /// [module documentation]: self 237fad3a1d3Sopenharmony_ci pub trait Fold { 238fad3a1d3Sopenharmony_ci #traits 239fad3a1d3Sopenharmony_ci } 240fad3a1d3Sopenharmony_ci 241fad3a1d3Sopenharmony_ci #impls 242fad3a1d3Sopenharmony_ci }, 243fad3a1d3Sopenharmony_ci )?; 244fad3a1d3Sopenharmony_ci Ok(()) 245fad3a1d3Sopenharmony_ci} 246