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
use crate::{
    Accessor, CompleteFunction, CompleteImpl, Data, Enum, Execution, Field, Generics, Ident,
    Program, Struct, StructStruct, Tracker, TupleStruct, Type, TypeNode, UnitStruct, WipFunction,
};
use proc_macro2::TokenStream;
use syn::DeriveInput;

pub fn derive<TokenStream>(input: TokenStream, run: fn(Execution)) -> TokenStream
where
    TokenStream: Into<proc_macro2::TokenStream> + From<proc_macro2::TokenStream>,
{
    let input = input.into();
    let output = derive2(input, run);
    output.into()
}

fn derive2(input: TokenStream, run: fn(Execution)) -> TokenStream {
    let input = syn::parse2(input).unwrap();
    let ty = syn_to_type(input);

    let tracker = Tracker::new();
    run(Execution {
        ty: &ty,
        tracker: &tracker,
    });

    let program = tracker_to_program(tracker);
    program.compile()
}

fn syn_to_type(input: DeriveInput) -> Type {
    let attrs: Vec<_> = input
        .attrs
        .into_iter()
        .map(std::convert::Into::into)
        .collect();

    Type(TypeNode::DataStructure {
        name: Ident::from(input.ident),
        generics: Generics::syn_to_generics(input.generics),
        data: match input.data {
            syn::Data::Struct(data) => match data.fields {
                syn::Fields::Named(fields) => Data::Struct(Struct::Struct(StructStruct {
                    fields: fields
                        .named
                        .into_iter()
                        .map(|field| Field {
                            attrs: field.attrs,
                            accessor: Accessor::Name(Ident::from(field.ident.unwrap())),
                            element: Type::syn_to_type(field.ty),
                        })
                        .collect(),
                    attrs,
                })),
                syn::Fields::Unnamed(fields) => Data::Struct(Struct::Tuple(TupleStruct {
                    fields: fields
                        .unnamed
                        .into_iter()
                        .enumerate()
                        .map(|(i, field)| Field {
                            attrs: field.attrs,
                            accessor: Accessor::Index(i),
                            element: Type::syn_to_type(field.ty),
                        })
                        .collect(),
                    attrs,
                })),
                syn::Fields::Unit => Data::Struct(Struct::Unit(UnitStruct { attrs })),
            },
            syn::Data::Enum(data) => {
                // FIXME convert enum variants
                Data::Enum(Enum {
                    variants: Vec::new(),
                    attrs,
                })
            }
            syn::Data::Union(_) => unimplemented!("union"),
        },
    })
}

fn tracker_to_program(tracker: Tracker) -> Program {
    Program {
        crates: tracker.crates.into_inner(),
        impls: tracker
            .impls
            .into_inner()
            .into_iter()
            .map(|imp| CompleteImpl {
                trait_ty: imp.trait_ty,
                ty: imp.ty,
                functions: imp
                    .functions
                    .into_inner()
                    .into_iter()
                    .map(|function| {
                        let function: WipFunction = function;
                        let values = function.values;
                        let invokes = function.invokes;
                        let macros = function.macros;
                        CompleteFunction {
                            self_ty: function.self_ty,
                            f: function.f,
                            values,
                            invokes,
                            macros,
                            ret: function.ret,
                        }
                    })
                    .collect(),
            })
            .collect(),
    }
}