diff --git a/Cargo.lock b/Cargo.lock index 0456295..eb747a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "piet-gpu-derive" +version = "0.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "piet-gpu-hal" version = "0.1.0" @@ -32,6 +41,41 @@ dependencies = [ "ash", ] +[[package]] +name = "proc-macro2" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + [[package]] name = "winapi" version = "0.3.8" diff --git a/Cargo.toml b/Cargo.toml index d580bf5..6a9525f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "piet-gpu-derive", "piet-gpu-hal" ] diff --git a/piet-gpu-derive/Cargo.toml b/piet-gpu-derive/Cargo.toml new file mode 100644 index 0000000..5f51963 --- /dev/null +++ b/piet-gpu-derive/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "piet-gpu-derive" +version = "0.0.0" +authors = ["Raph Levien "] +description = "Proc macro derives for piet-gpu." +license = "MIT/Apache-2.0" +edition = "2018" +keywords = ["graphics", "2d"] +categories = ["rendering::graphics-api"] + +[lib] +proc-macro = true + +[dependencies] +syn = {version = "1.0.17", features = ["extra-traits", "full"]} +quote = "1.0.3" +proc-macro2 = "1.0.10" diff --git a/piet-gpu-derive/src/lib.rs b/piet-gpu-derive/src/lib.rs new file mode 100644 index 0000000..31568f3 --- /dev/null +++ b/piet-gpu-derive/src/lib.rs @@ -0,0 +1,16 @@ +mod parse; + +use proc_macro::TokenStream; +use quote::quote; +use syn::parse_macro_input; + +use parse::GpuModule; + +#[proc_macro] +pub fn piet_gpu(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as syn::ItemMod); + //println!("input: {:#?}", input); + let module = GpuModule::from_syn(&input).unwrap(); + let expanded = quote! {}; + expanded.into() +} diff --git a/piet-gpu-derive/src/parse.rs b/piet-gpu-derive/src/parse.rs new file mode 100644 index 0000000..c335006 --- /dev/null +++ b/piet-gpu-derive/src/parse.rs @@ -0,0 +1,199 @@ +//! Parsing of the source + +extern crate proc_macro; + +use std::collections::HashSet; + +use syn::{ + Expr, ExprLit, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, ItemEnum, ItemStruct, + Lit, PathArguments, TypeArray, TypePath, +}; + +/// A scalar that can be represented in a packed data structure. +#[derive(Clone, Copy, PartialEq)] +pub enum GpuScalar { + I8, + I16, + I32, + F32, + U8, + U16, + U32, + // TODO: Add F16 +} + +/// An algebraic datatype. +#[derive(Clone)] +pub enum GpuType { + Scalar(GpuScalar), + Vector(GpuScalar, usize), + /// Used mostly for the body of enum variants. + InlineStruct(String), + Ref(Box), +} + +pub struct GpuEnum { + pub name: String, + pub variants: Vec<(String, Vec)>, +} + +pub enum GpuTypeDef { + Struct(String, Vec<(String, GpuType)>), + Enum(GpuEnum), +} + +pub struct GpuModule { + pub name: String, + pub defs: Vec, +} + +impl GpuScalar { + fn from_syn(ty: &syn::Type) -> Option { + ty_as_single_ident(ty).and_then(|ident| match ident.as_str() { + "f32" => Some(GpuScalar::F32), + "i8" => Some(GpuScalar::I8), + "i16" => Some(GpuScalar::I16), + "i32" => Some(GpuScalar::I32), + "u8" => Some(GpuScalar::U8), + "u16" => Some(GpuScalar::U16), + "u32" => Some(GpuScalar::U32), + _ => None, + }) + } +} + +fn ty_as_single_ident(ty: &syn::Type) -> Option { + if let syn::Type::Path(TypePath { + path: syn::Path { segments, .. }, + .. + }) = ty + { + if segments.len() == 1 { + let seg = &segments[0]; + if seg.arguments == PathArguments::None { + return Some(seg.ident.to_string()); + } + } + } + None +} + +impl GpuType { + fn from_syn(ty: &syn::Type) -> Result { + //println!("gputype {:#?}", ty); + if let Some(scalar) = GpuScalar::from_syn(ty) { + return Ok(GpuType::Scalar(scalar)); + } + if let Some(name) = ty_as_single_ident(ty) { + // Note: we're not doing any validation here. + return Ok(GpuType::InlineStruct(name)); + } + match ty { + syn::Type::Path(TypePath { + path: syn::Path { segments, .. }, + .. + }) => { + if segments.len() == 1 { + let seg = &segments[0]; + if seg.ident == "Ref" { + if let PathArguments::AngleBracketed(args) = &seg.arguments { + if args.args.len() == 1 { + if let GenericArgument::Type(inner) = &args.args[0] { + let inner_ty = GpuType::from_syn(inner)?; + return Ok(GpuType::Ref(Box::new(inner_ty))); + } + } + } + } + } + Err("unknown path case".into()) + } + syn::Type::Array(TypeArray { elem, len, .. }) => { + if let Some(elem) = GpuScalar::from_syn(&elem) { + if let Some(len) = expr_int_lit(len) { + // maybe sanity-check length here + Ok(GpuType::Vector(elem, len)) + } else { + Err("can't deal with variable length scalar arrays".into()) + } + } else { + Err("can't deal with non-scalar arrays".into()) + } + } + _ => Err("unknown type".into()), + } + } +} + +impl GpuTypeDef { + fn from_syn(item: &syn::Item) -> Result { + match item { + syn::Item::Struct(ItemStruct { + ident, + fields: Fields::Named(FieldsNamed { named, .. }), + .. + }) => { + let mut fields = Vec::new(); + for field in named { + let field_ty = GpuType::from_syn(&field.ty)?; + let field_name = field.ident.as_ref().ok_or("need name".to_string())?; + fields.push((field_name.to_string(), field_ty)); + } + Ok(GpuTypeDef::Struct(ident.to_string(), fields)) + } + syn::Item::Enum(ItemEnum { + ident, variants, .. + }) => { + let mut v = Vec::new(); + for variant in variants { + let vname = variant.ident.to_string(); + let mut fields = Vec::new(); + if let Fields::Unnamed(FieldsUnnamed { unnamed, .. }) = &variant.fields { + for field in unnamed { + fields.push(GpuType::from_syn(&field.ty)?); + } + } + v.push((vname, fields)); + } + let en = GpuEnum { + name: ident.to_string(), + variants: v, + }; + Ok(GpuTypeDef::Enum(en)) + } + _ => { + eprintln!("{:#?}", item); + Err("unknown item".into()) + } + } + } +} + +impl GpuModule { + pub fn from_syn(module: &syn::ItemMod) -> Result { + let name = module.ident.to_string(); + let mut defs = Vec::new(); + if let Some((_brace, items)) = &module.content { + for item in items { + let def = GpuTypeDef::from_syn(item)?; + defs.push(def); + } + } + Ok(GpuModule { + name, + defs, + }) + } +} + +fn expr_int_lit(e: &Expr) -> Option { + if let Expr::Lit(ExprLit { + lit: Lit::Int(lit_int), + .. + }) = e + { + lit_int.base10_parse().ok() + } else { + None + } +}