From 23e12ffc4421414abbd089759dab9c50aefeac0c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 26 Mar 2019 22:35:46 +0100 Subject: Derive DhallType for structs --- Cargo.lock | 9 ++-- dhall/src/dhall_type.rs | 77 +++++++++++++++++++++++++++++++++++ dhall/src/lib.rs | 2 + dhall/tests/dhall_type.rs | 37 ++++++++++++----- dhall_core/src/dhall_type.rs | 10 ----- dhall_core/src/lib.rs | 2 - dhall_generator/Cargo.toml | 1 + dhall_generator/src/dhall_expr.rs | 4 ++ dhall_generator/src/dhall_type.rs | 86 +++++++++++++++++++++++++++++++++++---- 9 files changed, 194 insertions(+), 34 deletions(-) create mode 100644 dhall/src/dhall_type.rs delete mode 100644 dhall_core/src/dhall_type.rs diff --git a/Cargo.lock b/Cargo.lock index cfbe901..e00159c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,7 +60,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -97,6 +97,7 @@ dependencies = [ "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -223,7 +224,7 @@ dependencies = [ "pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -307,7 +308,7 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.27" +version = "0.15.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", @@ -438,7 +439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560" "checksum serde_cbor 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45cd6d95391b16cd57e88b68be41d504183b7faae22030c0cc3b3f73dd57b2fd" "checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" -"checksum syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)" = "525bd55255f03c816e5d7f615587bd13030c7103354fadb104993dcee6a788ec" +"checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2" "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" "checksum term-painter 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "dcaa948f0e3e38470cd8dc8dcfe561a75c9e43f28075bb183845be2b9b3c08cf" "checksum typed-arena 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c6c06a92aef38bb4dc5b0df00d68496fc31307c5344c867bb61678c6e1671ec5" diff --git a/dhall/src/dhall_type.rs b/dhall/src/dhall_type.rs new file mode 100644 index 0000000..8abef32 --- /dev/null +++ b/dhall/src/dhall_type.rs @@ -0,0 +1,77 @@ +use dhall_core::*; +use dhall_generator::*; + +#[derive(Debug, Clone)] +pub enum DhallConversionError {} + +pub trait DhallType: Sized { + fn dhall_type() -> DhallExpr; + // fn as_dhall(&self) -> DhallExpr; + // fn from_dhall(e: DhallExpr) -> Result; +} + +impl DhallType for bool { + fn dhall_type() -> DhallExpr { + dhall_expr!(Bool) + } +} + +impl DhallType for Natural { + fn dhall_type() -> DhallExpr { + dhall_expr!(Natural) + } +} + +impl DhallType for Integer { + fn dhall_type() -> DhallExpr { + dhall_expr!(Integer) + } +} + +impl DhallType for String { + fn dhall_type() -> DhallExpr { + dhall_expr!(Text) + } +} + +impl DhallType for (A, B) { + fn dhall_type() -> DhallExpr { + let ta = A::dhall_type(); + let tb = B::dhall_type(); + dhall_expr!({ _1: ta, _2: tb }) + } +} + +impl DhallType for Option { + fn dhall_type() -> DhallExpr { + let t = T::dhall_type(); + dhall_expr!(Optional t) + } +} + +impl DhallType for Vec { + fn dhall_type() -> DhallExpr { + let t = T::dhall_type(); + dhall_expr!(List t) + } +} + +impl<'a, T: DhallType> DhallType for &'a T { + fn dhall_type() -> DhallExpr { + T::dhall_type() + } +} + +impl DhallType for std::marker::PhantomData { + fn dhall_type() -> DhallExpr { + dhall_expr!({}) + } +} + +impl DhallType for Result { + fn dhall_type() -> DhallExpr { + let tt = T::dhall_type(); + let te = E::dhall_type(); + dhall_expr!(< Ok: tt | Err: te>) + } +} diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index d9f9edb..c0c1d6f 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -10,8 +10,10 @@ mod normalize; pub use crate::normalize::*; pub mod binary; +mod dhall_type; pub mod imports; pub mod typecheck; +pub use dhall_type::*; pub use crate::imports::*; diff --git a/dhall/tests/dhall_type.rs b/dhall/tests/dhall_type.rs index cbb71a4..0e27ad0 100644 --- a/dhall/tests/dhall_type.rs +++ b/dhall/tests/dhall_type.rs @@ -1,15 +1,32 @@ #![feature(proc_macro_hygiene)] -use dhall_core::*; +use dhall::*; use dhall_generator::*; -#[derive(DhallType)] -struct A { - _field1: bool, - // field2: Option, -} - #[test] -fn test_dhall_type_a() { - assert_eq!(A::dhall_type(), dhall_expr!(False)); - // assert_eq!(A::dhall_type(), dhall_expr!({ field1: Bool })); +fn test_dhall_type() { + assert_eq!(bool::dhall_type(), dhall_expr!(Bool)); + assert_eq!(String::dhall_type(), dhall_expr!(Text)); + assert_eq!( + <(bool, Option)>::dhall_type(), + dhall_expr!({ _1: Bool, _2: Optional Text }) + ); + + #[derive(DhallType)] + #[allow(dead_code)] + struct A { + field1: bool, + field2: Option, + } + assert_eq!( + A::dhall_type(), + dhall_expr!({ field1: Bool, field2: Optional Bool }) + ); + + #[derive(DhallType)] + #[allow(dead_code)] + struct B<'a, T: 'a> { + field1: &'a T, + field2: Option, + } + assert_eq!(>::dhall_type(), A::dhall_type()); } diff --git a/dhall_core/src/dhall_type.rs b/dhall_core/src/dhall_type.rs deleted file mode 100644 index 3635e67..0000000 --- a/dhall_core/src/dhall_type.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::*; - -#[derive(Debug, Clone)] -pub enum DhallConversionError {} - -pub trait DhallType: Sized { - fn dhall_type() -> DhallExpr; - // fn as_dhall(&self) -> DhallExpr; - // fn from_dhall(e: DhallExpr) -> Result; -} diff --git a/dhall_core/src/lib.rs b/dhall_core/src/lib.rs index b15c644..0874b09 100644 --- a/dhall_core/src/lib.rs +++ b/dhall_core/src/lib.rs @@ -18,6 +18,4 @@ mod printer; pub use crate::printer::*; mod parser; pub use crate::parser::*; -mod dhall_type; -pub use dhall_type::*; pub mod context; diff --git a/dhall_generator/Cargo.toml b/dhall_generator/Cargo.toml index ada6707..2a6ca67 100644 --- a/dhall_generator/Cargo.toml +++ b/dhall_generator/Cargo.toml @@ -12,4 +12,5 @@ doctest = false itertools = "0.8.0" quote = "0.6.11" proc-macro2 = "0.4.27" +syn = "0.15.29" dhall_core = { path = "../dhall_core" } diff --git a/dhall_generator/src/dhall_expr.rs b/dhall_generator/src/dhall_expr.rs index 1ee4d1a..bc9da22 100644 --- a/dhall_generator/src/dhall_expr.rs +++ b/dhall_generator/src/dhall_expr.rs @@ -89,6 +89,10 @@ fn dhall_to_tokenstream( let m = map_to_tokenstream(m, ctx); quote! { dhall_core::Expr::RecordLit(#m) } } + UnionType(m) => { + let m = map_to_tokenstream(m, ctx); + quote! { dhall_core::Expr::UnionType(#m) } + } e => unimplemented!("{:?}", e), } } diff --git a/dhall_generator/src/dhall_type.rs b/dhall_generator/src/dhall_type.rs index 7cfd945..b305ca0 100644 --- a/dhall_generator/src/dhall_type.rs +++ b/dhall_generator/src/dhall_type.rs @@ -1,15 +1,85 @@ extern crate proc_macro; // use dhall_core::*; -// use proc_macro2::TokenStream; -use quote::quote; +use proc_macro::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; +use syn::{parse_macro_input, parse_quote, DeriveInput}; -pub fn derive_dhall_type(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { - (quote!{ - impl DhallType for A { +pub fn derive_dhall_type(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + // List of types that must impl DhallType + let mut constraints = vec![]; + + let dhall_type = match &input.data { + syn::Data::Struct(data) => match &data.fields { + syn::Fields::Named(fields) => { + let fields = fields.named.iter() + .map(|f| { + let name = f.ident.as_ref().unwrap().to_string(); + let ty = &f.ty; + constraints.push(ty.clone()); + quote!( m.insert(dhall_core::Label::from(#name), <#ty as dhall::DhallType>::dhall_type()); ) + }); + quote! { dhall_core::rc(dhall_core::Expr::RecordType({ + use std::collections::BTreeMap; + let mut m = BTreeMap::new(); + #(#fields)* + m + })) } + } + _ => quote!(dhall_generator::dhall_expr!(Bool)), + }, + _ => quote!(dhall_generator::dhall_expr!(Bool)), + }; + + let mut generics = input.generics.clone(); + generics.make_where_clause(); + let (impl_generics, ty_generics, orig_where_clause) = + generics.split_for_impl(); + let orig_where_clause = orig_where_clause.unwrap(); + + // Hygienic errors + let assertions = constraints.iter().enumerate().map(|(i, ty)| { + // Ensure that ty: DhallType, with an appropriate span + let assert_name = + syn::Ident::new(&format!("_AssertDhallType{}", i), ty.span()); + let mut local_where_clause = orig_where_clause.clone(); + local_where_clause + .predicates + .push(parse_quote!(#ty: dhall::DhallType)); + let phantoms = generics.params.iter().map(|param| match param { + syn::GenericParam::Type(syn::TypeParam { ident, .. }) => { + quote!(#ident) + } + syn::GenericParam::Lifetime(syn::LifetimeDef { + lifetime, .. + }) => quote!(&#lifetime ()), + _ => unimplemented!(), + }); + quote_spanned! {ty.span()=> + struct #assert_name #impl_generics #local_where_clause { + _phantom: std::marker::PhantomData<(#(#phantoms),*)> + }; + } + }); + + // Ensure that all the fields have a DhallType impl + let mut where_clause = orig_where_clause.clone(); + for ty in constraints.iter() { + where_clause + .predicates + .push(parse_quote!(#ty: dhall::DhallType)); + } + + let ident = &input.ident; + let tokens = quote! { + impl #impl_generics dhall::DhallType for #ident #ty_generics #where_clause { fn dhall_type() -> dhall_core::DhallExpr { - dhall_core::rc(dhall_core::Expr::BoolLit(false)) + #(#assertions)* + #dhall_type } } - }).into() + }; + TokenStream::from(tokens) } - -- cgit v1.2.3