1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
extern crate proc_macro;

use crate::proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::DeriveInput;
use syn::Error;
use syn::{parse_macro_input, Ident, Result};

mod child_nodes;
mod into_dyn_ast_ref;
mod span;

#[proc_macro_derive(Ast, attributes(peepmatic))]
pub fn derive_ast(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let span_impl = match span::derive_span(&input) {
        Ok(s) => s,
        Err(e) => return e.to_compile_error().into(),
    };

    let child_nodes_impl = match child_nodes::derive_child_nodes(&input) {
        Ok(c) => c,
        Err(e) => return e.to_compile_error().into(),
    };

    let into_dyn_ast_ref_impl = match into_dyn_ast_ref::derive_into_dyn_ast_ref(&input) {
        Ok(n) => n,
        Err(e) => return e.to_compile_error().into(),
    };

    let expanded = quote! {
        #span_impl
        #child_nodes_impl
        #into_dyn_ast_ref_impl
    };

    TokenStream::from(expanded)
}

#[derive(Default)]
pub(crate) struct PeepmaticOpts {
    // `ChildNodes` options.
    skip_child: bool,
    flatten: bool,

    // `From<&'a Self> for DynAstRef<'a>` options.
    no_into_dyn_node: bool,

    // Peepmatic operator options.
    immediates_paren: syn::token::Paren,
    immediates: Vec<syn::Ident>,
    params_paren: syn::token::Paren,
    params: Vec<syn::Ident>,
    result: Option<syn::Ident>,
}

impl Parse for PeepmaticOpts {
    fn parse(input: ParseStream) -> Result<Self> {
        enum Attr {
            Immediates(syn::token::Paren, Vec<syn::Ident>),
            Params(syn::token::Paren, Vec<syn::Ident>),
            Result(syn::Ident),
            NoIntoDynNode,
            SkipChild,
            Flatten,
        }

        let attrs = Punctuated::<_, syn::token::Comma>::parse_terminated(input)?;
        let mut ret = PeepmaticOpts::default();
        for attr in attrs {
            match attr {
                Attr::Immediates(paren, imms) => {
                    ret.immediates_paren = paren;
                    ret.immediates = imms;
                }
                Attr::Params(paren, ps) => {
                    ret.params_paren = paren;
                    ret.params = ps;
                }
                Attr::Result(r) => ret.result = Some(r),
                Attr::NoIntoDynNode => ret.no_into_dyn_node = true,
                Attr::SkipChild => ret.skip_child = true,
                Attr::Flatten => ret.flatten = true,
            }
        }

        return Ok(ret);

        impl Parse for Attr {
            fn parse(input: ParseStream) -> Result<Self> {
                let attr: Ident = input.parse()?;
                if attr == "immediates" {
                    let inner;
                    let paren = syn::parenthesized!(inner in input);
                    let imms = Punctuated::<_, syn::token::Comma>::parse_terminated(&inner)?;
                    return Ok(Attr::Immediates(paren, imms.into_iter().collect()));
                }
                if attr == "params" {
                    let inner;
                    let paren = syn::parenthesized!(inner in input);
                    let params = Punctuated::<_, syn::token::Comma>::parse_terminated(&inner)?;
                    return Ok(Attr::Params(paren, params.into_iter().collect()));
                }
                if attr == "result" {
                    let inner;
                    syn::parenthesized!(inner in input);
                    return Ok(Attr::Result(syn::Ident::parse(&inner)?));
                }
                if attr == "skip_child" {
                    return Ok(Attr::SkipChild);
                }
                if attr == "no_into_dyn_node" {
                    return Ok(Attr::NoIntoDynNode);
                }
                if attr == "flatten" {
                    return Ok(Attr::Flatten);
                }
                return Err(Error::new(attr.span(), "unexpected attribute"));
            }
        }
    }
}

fn peepmatic_attrs(attrs: &mut Vec<syn::Attribute>) -> TokenStream {
    let mut ret = proc_macro2::TokenStream::new();
    let ident = syn::Path::from(syn::Ident::new("peepmatic", Span::call_site()));
    for i in (0..attrs.len()).rev() {
        if attrs[i].path != ident {
            continue;
        }
        let attr = attrs.remove(i);
        let group = match attr.tokens.into_iter().next().unwrap() {
            proc_macro2::TokenTree::Group(g) => g,
            _ => panic!("#[peepmatic(...)] expected"),
        };
        ret.extend(group.stream());
        ret.extend(quote! { , });
    }
    return ret.into();
}

impl PeepmaticOpts {
    pub(crate) fn from_attrs(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Self> {
        syn::parse(peepmatic_attrs(attrs))
    }
}