~chiefnoah/baregen

df13568679ac7fe9c3d4d3f16070203876e9c808 — Noah Pederson 8 months ago bc0b297 master
Mostly complete proc macro implementation
1 files changed, 268 insertions(+), 97 deletions(-)

M bare_derive/src/lib.rs
M bare_derive/src/lib.rs => bare_derive/src/lib.rs +268 -97
@@ 1,11 1,13 @@
use std::{collections::BTreeMap, fs::read_to_string};

use bare_schema_parser::{parse_string, AnyType, PrimativeType};
use proc_macro2::TokenStream;
use bare_schema_parser::{parse_string, AnyType, PrimativeType, StructField};
use proc_macro;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ExprLit};

type DefRegistry = Vec<TokenStream>;

#[proc_macro]
pub fn bare_schema(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
    // TODO: support Into<PathBuf>


@@ 16,120 18,289 @@ pub fn bare_schema(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
    };
    let file = read_to_string(path.value()).unwrap();
    let user_type_registry: BTreeMap<String, AnyType> = parse_string(&file);
    let user_types = user_type_registry
        .iter()
        .map(|(k, v)| gen_anytype_tokenstream(k.clone(), v.clone()));
    let mut user_type_syntax = DefRegistry::new();
    for (name, user_type) in user_type_registry {
        user_type_syntax = gen_user_type(user_type_syntax, &name, &user_type);
    }
    quote! {
        use std::io::{Error as IOError, Read, Write};
        use bare_parser;
        use serde::{Serialize, Deserialize};

        trait FromBytes {
            fn from_bytes(&[u8]) -> Result<Self, IOError>;
            fn read_from(reader: impl Read) -> Result<Self, IOError>;
        }
        #(#user_type_syntax)*
    }
    .into()
}

        trait ToBytes {
            fn to_bytes(self) -> Vec<u8>;
            fn write(self, writer: impl Write) -> Result<(), IOError>;
/// `gen_user_type` is responsible for generating the token streams of a single user type at a top
/// level. Rust does not support anonymous structs/enums/etc., so we must recursively parse any
/// anonymous definitions and generate top-level definitions. As such, this function may generate
/// multiple types.
fn gen_user_type(mut registry: DefRegistry, name: &String, t: &AnyType) -> DefRegistry {
    let mut def = TokenStream::new();
    use AnyType::*;
    (registry, def) = match t {
        Primative(p) => {
            def = gen_primative_type_def(p);
            (
                registry,
                quote! {
                    #name = #def;
                },
            )
        }
        List { inner, length } => {
            (registry, def) = gen_list(registry, name, inner.as_ref(), length);
            (
                registry,
                quote! {
                    type #name = #def;
                },
            )
        }
        Struct(fields) => {
            (registry, _) = gen_struct(registry, name, fields);
            // `gen_struct` only has side-effects on the registry, so we return nothing
            (registry, TokenStream::new())
        }
        Map { key, value } => {
            let (registry, map_def) = gen_map(registry, name, key.as_ref(), value.as_ref());
            (
                registry,
                quote! {
                    type #name = #map_def;
                },
            )
        }
        Optional(inner) => {
            let (registry, inner_def) = dispatch_type(registry, name, inner);
            (
                registry,
                quote! {
                    type #name = #inner_def;
                },
            )
        }
        TypeReference(_) => (registry, quote! { type #name = #def; }),
        Enum(members) => {
            (registry, _) = gen_enum(registry, name, members);
            // `gen_enum` only has side-effects on the registry, so we return nothing
            (registry, TokenStream::new())
        }
        Union(members) => {
            (registry, _) = gen_union(registry, name, members);
            // `gen_union` only has side-effects on the registry, so we return nothing
            (registry, TokenStream::new())
        }
    };
    registry.push(def);
    registry
}

        #(#user_types)*
fn dispatch_type(
    registry: DefRegistry,
    name: &String,
    any_type: &AnyType,
) -> (DefRegistry, TokenStream) {
    match any_type {
        AnyType::Primative(p) => (registry, gen_primative_type_def(p)),
        AnyType::List { inner, length } => gen_list(registry, name, inner.as_ref(), length),
        AnyType::Struct(fields) => gen_struct(registry, name, fields),
        AnyType::Enum(members) => gen_enum(registry, name, members),
        AnyType::Map { key, value } => gen_map(registry, name, key.as_ref(), value.as_ref()),
        AnyType::Union(members) => gen_union(registry, name, members),
        AnyType::Optional(inner) => gen_option(registry, name, inner),
        AnyType::TypeReference(ident) => (registry, quote! { #ident }),
    }
    .into()
}

fn gen_anytype_tokenstream(name: String, t: AnyType) -> TokenStream {
    match t {
        AnyType::Struct(s) => {
            let fields: Vec<proc_macro2::TokenStream> = s
                .iter()
                .map(|f| {
                    let field_name = f.name.clone();
                    let field_type = gen_anytype_tokenstream(f.name.clone(), f.type_r.clone());
                    quote! {
                        #field_name: #field_type
                    }
                })
                .collect();
            let parsers: Vec<proc_macro2::TokenStream> = s
                .iter()
                .map(|f| {
                    let field_name = f.name.clone();
                    let field_type = gen_anytype_tokenstream(f.name.clone(), f.type_r.clone());
                    quote! {
                        
                    }
                })
                .collect();
fn gen_map(
    registry: DefRegistry,
    name: &String,
    key: &AnyType,
    value: &AnyType,
) -> (DefRegistry, TokenStream) {
    let (registry, key_def) = dispatch_type(registry, name, key);
    let (registry, val_def) = dispatch_type(registry, name, value);
    (
        registry,
        quote! {
            HashMap<#key_def, #val_def>
        },
    )
}

fn gen_list(
    registry: DefRegistry,
    name: &String,
    inner_type: &AnyType,
    size: &Option<usize>,
) -> (DefRegistry, TokenStream) {
    let (registry, inner_def) = dispatch_type(registry, name, inner_type);
    (
        registry,
        if let Some(size) = size {
            // If we know the size, it's an array
            quote! {
                struct #name {
                    #(#fields)*
                }
                [#inner_def; #size]
            }
        } else {
            // otherwise we don't know and must allocate
            quote! {
                Vec<#inner_def>
            }
        },
    )
}

fn gen_struct(
    mut registry: DefRegistry,
    name: &String,
    fields: &Vec<StructField>,
) -> (DefRegistry, TokenStream) {
    // clone so we can safely drain this
    let fields_clone = fields.clone();
    let mut fields_gen: Vec<TokenStream> = Vec::new();
    (registry, fields_gen) = gen_struct_field(registry, name, fields_clone, fields_gen);
    gen_anonymous(registry, name, |name| {
        quote! {
            struct #name {
                #(#fields_gen),*
            }
        }
        AnyType::Enum(e) => todo!(),
        AnyType::Union(u) => todo!(),
        // These are all type aliases and should be treated as such.
        AnyType::Map { key, value } => todo!(),
        AnyType::Optional(o) => todo!(),
        AnyType::TypeReference(u) => todo!(),
        AnyType::Primative(p) => {
            panic!("It is invalid to define a primative as a top-level BARE type.")
        }
        AnyType::List { inner, length } => {
            panic!("It is invalid to define a list as a top-level BARE type.")
    })
}

fn gen_union(
    mut registry: DefRegistry,
    name: &String,
    members: &Vec<AnyType>,
) -> (DefRegistry, TokenStream) {
    let mut members_def: Vec<TokenStream> = Vec::with_capacity(members.len());
    for (i, member) in members.iter().enumerate() {
        // This is to allow the `registry` binding to not shadow the function arg, but instead
        // rebind it as it's used in the subsequent `gen_anonymous` call. We'll get move errors if
        // we don't do it this way.
        #[allow(unused_assignments)]
        let mut member_def = TokenStream::new();
        (registry, member_def) = match member {
            AnyType::Struct(fields) => {
                let mut fields_defs: Vec<TokenStream> = Vec::new();
                (registry, fields_defs) =
                    gen_struct_field(registry, name, fields.clone(), fields_defs);
                (
                    registry,
                    quote! {
                        {
                            #(#fields_defs),*
                        }
                    },
                )
            }
            _ => dispatch_type(registry, &format!("{name}Member{i}"), member),
        };
        members_def.push(member_def);
    }
    gen_anonymous(registry, name, |name| {
        quote! {
            enum #name {
                #(#members_def)*,
            }
        }
    })
}

fn gen_option(registry: DefRegistry, name: &String, inner: &AnyType) -> (DefRegistry, TokenStream) {
    let (registry, inner_def) = dispatch_type(registry, name, inner);
    (
        registry,
        quote! {
           Option<#inner_def>
        },
    )
}

fn gen_struct_field(
    registry: DefRegistry,
    struct_name: &String,
    mut fields: Vec<StructField>,
    mut fields_gen: Vec<TokenStream>,
) -> (DefRegistry, Vec<TokenStream>) {
    if let Some(StructField { name, type_r }) = fields.pop() {
        let (registry, field_gen) =
            dispatch_type(registry, &format!("{struct_name}{name}"), &type_r);
        fields_gen.push(field_gen);
        gen_struct_field(registry, struct_name, fields, fields_gen)
    } else {
        (registry, fields_gen)
    }
    .into()
}

fn gen_primative_parser(p: &PrimativeType, input: TokenStream) -> TokenStream {
fn gen_enum(
    registry: DefRegistry,
    name: &String,
    members: &Vec<(String, Option<usize>)>,
) -> (DefRegistry, TokenStream) {
    let member_defs = members.iter().map(|(name, val)| {
        if let Some(val) = val {
            quote! {
                #name = #val
            }
        } else {
            quote! {
                #name,
            }
        }
    });
    gen_anonymous(registry, name, |name| {
        quote! {
            enum #name {
                #(#member_defs),*
            }
        }
    })
}

fn gen_anonymous(
    mut registry: Vec<TokenStream>,
    name: &String,
    inner: impl FnOnce(&String) -> TokenStream,
) -> (Vec<TokenStream>, TokenStream) {
    registry.push(inner(&name));
    (
        registry,
        quote! {
            #name
        },
    )
}

fn gen_primative_type_def(p: &PrimativeType) -> TokenStream {
    use PrimativeType::*;
    match p {
        PrimativeType::UInt => TokenStream::new(),
        PrimativeType::U64 => TokenStream::new(),
        PrimativeType::U32 => TokenStream::new(),
        PrimativeType::U16 => TokenStream::new(),
        PrimativeType::U8 => TokenStream::new(),
        PrimativeType::Int => TokenStream::new(),
        PrimativeType::I64 => TokenStream::new(),
        PrimativeType::I32 => TokenStream::new(),
        PrimativeType::I16 => TokenStream::new(),
        PrimativeType::I8 => TokenStream::new(),
        PrimativeType::F64 => TokenStream::new(),
        PrimativeType::F32 => TokenStream::new(),
        PrimativeType::Str => TokenStream::new(),
        PrimativeType::Data(_) => TokenStream::new(),
        PrimativeType::Void => TokenStream::new(),
        PrimativeType::Bool => TokenStream::new(),
        UInt | U64 => quote! { u64 },
        U32 => quote! { u32 },
        U16 => quote! { u16 },
        U8 => quote! { u8 },
        Int | I64 => quote! { i64 },
        I32 => quote! { i32 },
        I16 => quote! { i16 },
        I8 => quote! { i8 },
        F64 => quote! { f64 },
        F32 => quote! { f32 },
        Str => quote! { String },
        Data(s) => match s {
            Some(size) => quote! { [u8; #size] },
            None => quote! { Vec<u8> },
        },
        Void => quote! { () },
        Bool => quote! { bool },
    }
}

fn parser_for_type(t: AnyType) -> TokenStream {
    match t {
        AnyType::Primative(p) => match p {
            PrimativeType::UInt => quote! { bare_parser::uint },
            PrimativeType::U64 => todo!(),
            PrimativeType::U32 => todo!(),
            PrimativeType::U16 => todo!(),
            PrimativeType::U8 => todo!(),
            PrimativeType::Int => todo!(),
            PrimativeType::I64 => todo!(),
            PrimativeType::I32 => todo!(),
            PrimativeType::I16 => todo!(),
            PrimativeType::I8 => todo!(),
            PrimativeType::F64 => todo!(),
            PrimativeType::F32 => todo!(),
            PrimativeType::Str => todo!(),
            PrimativeType::Data(_) => todo!(),
            PrimativeType::Void => todo!(),
            PrimativeType::Bool => todo!(),
        },
        AnyType::List { inner, length } => todo!(),
        AnyType::Struct(_) => todo!(),
        AnyType::Enum(_) => todo!(),
        AnyType::Map { key, value } => todo!(),
        AnyType::Union(_) => todo!(),
        AnyType::Optional(_) => todo!(),
        AnyType::TypeReference(_) => todo!(),
#[cfg(test)]
mod test {
    #[test]
    fn test_basic() {
        todo!();
    }
}