From 4f04d1e6f02113ad0f539b723333f8f1f5734f13 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 4 May 2019 12:40:48 +0200 Subject: Rename dhall_generator to dhall_proc_macros --- Cargo.lock | 4 +- Cargo.toml | 2 +- dhall/Cargo.toml | 2 +- dhall/src/lib.rs | 2 +- dhall/src/normalize.rs | 2 +- dhall/src/traits/static_type.rs | 2 +- dhall/src/typecheck.rs | 2 +- dhall/tests/traits.rs | 16 +-- dhall_generator/Cargo.toml | 17 --- dhall_generator/src/derive.rs | 179 ------------------------------- dhall_generator/src/lib.rs | 26 ----- dhall_generator/src/quote.rs | 227 ---------------------------------------- dhall_proc_macros/Cargo.toml | 17 +++ dhall_proc_macros/src/derive.rs | 179 +++++++++++++++++++++++++++++++ dhall_proc_macros/src/lib.rs | 26 +++++ dhall_proc_macros/src/quote.rs | 227 ++++++++++++++++++++++++++++++++++++++++ 16 files changed, 465 insertions(+), 465 deletions(-) delete mode 100644 dhall_generator/Cargo.toml delete mode 100644 dhall_generator/src/derive.rs delete mode 100644 dhall_generator/src/lib.rs delete mode 100644 dhall_generator/src/quote.rs create mode 100644 dhall_proc_macros/Cargo.toml create mode 100644 dhall_proc_macros/src/derive.rs create mode 100644 dhall_proc_macros/src/lib.rs create mode 100644 dhall_proc_macros/src/quote.rs diff --git a/Cargo.lock b/Cargo.lock index 876e97c..53e019b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,7 +69,7 @@ name = "dhall" version = "0.1.0" dependencies = [ "bytecount 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dhall_generator 0.1.0", + "dhall_proc_macros 0.1.0", "dhall_syntax 0.1.0", "improved_slice_patterns 2.0.0", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -91,7 +91,7 @@ dependencies = [ ] [[package]] -name = "dhall_generator" +name = "dhall_proc_macros" version = "0.1.0" dependencies = [ "dhall_syntax 0.1.0", diff --git a/Cargo.toml b/Cargo.toml index 7153910..f48e33a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ "dhall", "dhall_generated_parser", "dhall_syntax", - "dhall_generator", + "dhall_proc_macros", "improved_slice_patterns", ] diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index 28123e5..d08627d 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -14,7 +14,7 @@ serde = { version = "1.0", features = ["derive"] } serde_cbor = "0.9.0" improved_slice_patterns = { version = "2.0.0", path = "../improved_slice_patterns" } dhall_syntax = { path = "../dhall_syntax" } -dhall_generator = { path = "../dhall_generator" } +dhall_proc_macros = { path = "../dhall_proc_macros" } [dev-dependencies] pretty_assertions = "0.6.1" diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 6e4361f..a531f64 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -140,7 +140,7 @@ mod typecheck; pub mod de { pub use crate::traits::{Deserialize, SimpleStaticType, StaticType}; #[doc(hidden)] - pub use dhall_generator::SimpleStaticType; + pub use dhall_proc_macros::SimpleStaticType; /// Deserialize an instance of type T from a string of Dhall text. /// diff --git a/dhall/src/normalize.rs b/dhall/src/normalize.rs index c64bb4a..c035d93 100644 --- a/dhall/src/normalize.rs +++ b/dhall/src/normalize.rs @@ -7,7 +7,7 @@ use dhall_syntax::{ rc, BinOp, Builtin, Const, ExprF, Integer, InterpolatedText, InterpolatedTextContents, Label, Natural, SubExpr, V, X, }; -use dhall_generator as dhall; +use dhall_proc_macros as dhall; use crate::expr::{Normalized, Type, Typed, TypedInternal}; diff --git a/dhall/src/traits/static_type.rs b/dhall/src/traits/static_type.rs index 6e42da8..60b894c 100644 --- a/dhall/src/traits/static_type.rs +++ b/dhall/src/traits/static_type.rs @@ -1,6 +1,6 @@ use crate::expr::*; use dhall_syntax::*; -use dhall_generator as dhall; +use dhall_proc_macros as dhall; /// A value that has a statically-known Dhall type. /// diff --git a/dhall/src/typecheck.rs b/dhall/src/typecheck.rs index 1683fbf..0b8ea53 100644 --- a/dhall/src/typecheck.rs +++ b/dhall/src/typecheck.rs @@ -10,7 +10,7 @@ use crate::traits::DynamicType; use dhall_syntax; use dhall_syntax::context::Context; use dhall_syntax::*; -use dhall_generator as dhall; +use dhall_proc_macros as dhall; use self::TypeMessage::*; diff --git a/dhall/tests/traits.rs b/dhall/tests/traits.rs index e26a6c7..4b21d42 100644 --- a/dhall/tests/traits.rs +++ b/dhall/tests/traits.rs @@ -1,7 +1,7 @@ #![feature(proc_macro_hygiene)] use dhall::de::SimpleStaticType; use dhall_syntax::{SubExpr, X}; -use dhall_generator; +use dhall_proc_macros; #[test] fn test_static_type() { @@ -11,19 +11,19 @@ fn test_static_type() { assert_eq!( bool::get_simple_static_type(), - mktype(dhall_generator::subexpr!(Bool)) + mktype(dhall_proc_macros::subexpr!(Bool)) ); assert_eq!( String::get_simple_static_type(), - mktype(dhall_generator::subexpr!(Text)) + mktype(dhall_proc_macros::subexpr!(Text)) ); assert_eq!( >::get_simple_static_type(), - mktype(dhall_generator::subexpr!(Optional Bool)) + mktype(dhall_proc_macros::subexpr!(Optional Bool)) ); assert_eq!( <(bool, Option)>::get_simple_static_type(), - mktype(dhall_generator::subexpr!({ _1: Bool, _2: Optional Text })) + mktype(dhall_proc_macros::subexpr!({ _1: Bool, _2: Optional Text })) ); #[derive(dhall::de::SimpleStaticType)] @@ -35,7 +35,7 @@ fn test_static_type() { assert_eq!( ::get_simple_static_type(), mktype( - dhall_generator::subexpr!({ field1: Bool, field2: Optional Bool }) + dhall_proc_macros::subexpr!({ field1: Bool, field2: Optional Bool }) ) ); @@ -63,7 +63,7 @@ fn test_static_type() { struct D(); assert_eq!( >::get_simple_static_type(), - mktype(dhall_generator::subexpr!({ _1: {}, _2: Optional Text })) + mktype(dhall_proc_macros::subexpr!({ _1: {}, _2: Optional Text })) ); #[derive(SimpleStaticType)] @@ -74,6 +74,6 @@ fn test_static_type() { }; assert_eq!( >::get_simple_static_type(), - mktype(dhall_generator::subexpr!(< A: Bool | B: Text >)) + mktype(dhall_proc_macros::subexpr!(< A: Bool | B: Text >)) ); } diff --git a/dhall_generator/Cargo.toml b/dhall_generator/Cargo.toml deleted file mode 100644 index 8a6b6e2..0000000 --- a/dhall_generator/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "dhall_generator" -version = "0.1.0" -authors = ["Nadrieril "] -license = "BSD-2-Clause" -edition = "2018" - -[lib] -proc-macro = true -doctest = false - -[dependencies] -itertools = "0.8.0" -quote = "0.6.11" -proc-macro2 = "0.4.27" -syn = "0.15.29" -dhall_syntax = { path = "../dhall_syntax" } diff --git a/dhall_generator/src/derive.rs b/dhall_generator/src/derive.rs deleted file mode 100644 index bcefb17..0000000 --- a/dhall_generator/src/derive.rs +++ /dev/null @@ -1,179 +0,0 @@ -extern crate proc_macro; -// use dhall_syntax::*; -use proc_macro::TokenStream; -use quote::{quote, quote_spanned}; -use syn::spanned::Spanned; -use syn::Error; -use syn::{parse_quote, DeriveInput}; - -pub fn derive_simple_static_type(input: TokenStream) -> TokenStream { - TokenStream::from(match derive_simple_static_type_inner(input) { - Ok(tokens) => tokens, - Err(err) => err.to_compile_error(), - }) -} - -fn get_simple_static_type(ty: T) -> proc_macro2::TokenStream -where - T: quote::ToTokens, -{ - quote!( - <#ty as ::dhall::de::SimpleStaticType>::get_simple_static_type() - ) -} - -fn derive_for_struct( - data: &syn::DataStruct, - constraints: &mut Vec, -) -> Result { - let fields = match &data.fields { - syn::Fields::Named(fields) => fields - .named - .iter() - .map(|f| { - let name = f.ident.as_ref().unwrap().to_string(); - let ty = &f.ty; - (name, ty) - }) - .collect(), - syn::Fields::Unnamed(fields) => fields - .unnamed - .iter() - .enumerate() - .map(|(i, f)| { - let name = format!("_{}", i + 1); - let ty = &f.ty; - (name, ty) - }) - .collect(), - syn::Fields::Unit => vec![], - }; - let fields = fields - .into_iter() - .map(|(name, ty)| { - let name = dhall_syntax::Label::from(name); - constraints.push(ty.clone()); - let ty = get_simple_static_type(ty); - (name, quote!(#ty.into())) - }) - .collect(); - let record = - crate::quote::quote_exprf(dhall_syntax::ExprF::RecordType(fields)); - Ok(quote! { dhall_syntax::rc(#record) }) -} - -fn derive_for_enum( - data: &syn::DataEnum, - constraints: &mut Vec, -) -> Result { - let variants = data - .variants - .iter() - .map(|v| { - let name = dhall_syntax::Label::from(v.ident.to_string()); - match &v.fields { - syn::Fields::Unit => Ok((name, None)), - syn::Fields::Unnamed(fields) if fields.unnamed.is_empty() => { - Ok((name, None)) - } - syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { - let ty = &fields.unnamed.iter().next().unwrap().ty; - constraints.push(ty.clone()); - let ty = get_simple_static_type(ty); - Ok((name, Some(quote!(#ty.into())))) - } - syn::Fields::Unnamed(_) => Err(Error::new( - v.span(), - "Variants with more than one field are not supported", - )), - syn::Fields::Named(_) => Err(Error::new( - v.span(), - "Named variants are not supported", - )), - } - }) - .collect::>()?; - - let union = - crate::quote::quote_exprf(dhall_syntax::ExprF::UnionType(variants)); - Ok(quote! { dhall_syntax::rc(#union) }) -} - -pub fn derive_simple_static_type_inner( - input: TokenStream, -) -> Result { - let input: DeriveInput = syn::parse_macro_input::parse(input)?; - - // List of types that must impl Type - let mut constraints = vec![]; - - let get_type = match &input.data { - syn::Data::Struct(data) => derive_for_struct(data, &mut constraints)?, - syn::Data::Enum(data) if data.variants.is_empty() => { - return Err(Error::new( - input.span(), - "Empty enums are not supported", - )) - } - syn::Data::Enum(data) => derive_for_enum(data, &mut constraints)?, - syn::Data::Union(x) => { - return Err(Error::new( - x.union_token.span(), - "Unions are not supported", - )) - } - }; - - 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: Type, with an appropriate span - let assert_name = - syn::Ident::new(&format!("_AssertType{}", i), ty.span()); - let mut local_where_clause = orig_where_clause.clone(); - local_where_clause - .predicates - .push(parse_quote!(#ty: ::dhall::de::SimpleStaticType)); - 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 Type impl - let mut where_clause = orig_where_clause.clone(); - for ty in constraints.iter() { - where_clause - .predicates - .push(parse_quote!(#ty: ::dhall::de::SimpleStaticType)); - } - - let ident = &input.ident; - let tokens = quote! { - impl #impl_generics ::dhall::de::SimpleStaticType - for #ident #ty_generics - #where_clause { - fn get_simple_static_type<'get_simple_static_type>() -> - ::dhall::expr::SimpleType<'get_simple_static_type> { - #(#assertions)* - ::dhall::expr::SimpleType::from(#get_type) - } - } - }; - Ok(tokens) -} diff --git a/dhall_generator/src/lib.rs b/dhall_generator/src/lib.rs deleted file mode 100644 index 1124968..0000000 --- a/dhall_generator/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! This crate contains the code-generation primitives for the [dhall-rust][dhall-rust] crate. -//! This is highly unstable and breaks regularly; use at your own risk. -//! -//! [dhall-rust]: https://github.com/Nadrieril/dhall-rust - -extern crate proc_macro; - -mod derive; -mod quote; - -use proc_macro::TokenStream; - -#[proc_macro] -pub fn expr(input: TokenStream) -> TokenStream { - quote::expr(input) -} - -#[proc_macro] -pub fn subexpr(input: TokenStream) -> TokenStream { - quote::subexpr(input) -} - -#[proc_macro_derive(SimpleStaticType)] -pub fn derive_simple_static_type(input: TokenStream) -> TokenStream { - derive::derive_simple_static_type(input) -} diff --git a/dhall_generator/src/quote.rs b/dhall_generator/src/quote.rs deleted file mode 100644 index c2323fa..0000000 --- a/dhall_generator/src/quote.rs +++ /dev/null @@ -1,227 +0,0 @@ -extern crate proc_macro; -use dhall_syntax::context::Context; -use dhall_syntax::*; -use proc_macro2::TokenStream; -use quote::quote; -use std::collections::BTreeMap; - -pub fn expr(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input_str = input.to_string(); - let expr: SubExpr<_, Import> = parse_expr(&input_str).unwrap().unnote(); - let no_import = - |_: &Import| -> X { panic!("Don't use import in dhall::expr!()") }; - let expr = expr.map_embed(no_import); - let output = quote_expr(&expr.unroll(), &Context::new()); - output.into() -} - -pub fn subexpr(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input_str = input.to_string(); - let expr: SubExpr<_, Import> = parse_expr(&input_str).unwrap().unnote(); - let no_import = - |_: &Import| -> X { panic!("Don't use import in dhall::subexpr!()") }; - let expr = expr.map_embed(no_import); - let output = quote_subexpr(&expr, &Context::new()); - output.into() -} - -// Returns an expression of type ExprF, where T is the -// type of the subexpressions after interpolation. -pub fn quote_exprf(expr: ExprF) -> TokenStream -where - TS: quote::ToTokens + std::fmt::Debug, -{ - use dhall_syntax::ExprF::*; - match expr { - Var(_) => unreachable!(), - Pi(x, t, b) => { - let x = quote_label(&x); - quote! { dhall_syntax::ExprF::Pi(#x, #t, #b) } - } - Lam(x, t, b) => { - let x = quote_label(&x); - quote! { dhall_syntax::ExprF::Lam(#x, #t, #b) } - } - App(f, a) => { - quote! { dhall_syntax::ExprF::App(#f, #a) } - } - Annot(x, t) => { - quote! { dhall_syntax::ExprF::Annot(#x, #t) } - } - Const(c) => { - let c = quote_const(c); - quote! { dhall_syntax::ExprF::Const(#c) } - } - Builtin(b) => { - let b = quote_builtin(b); - quote! { dhall_syntax::ExprF::Builtin(#b) } - } - BinOp(o, a, b) => { - let o = quote_binop(o); - quote! { dhall_syntax::ExprF::BinOp(#o, #a, #b) } - } - NaturalLit(n) => { - quote! { dhall_syntax::ExprF::NaturalLit(#n) } - } - BoolLit(b) => { - quote! { dhall_syntax::ExprF::BoolLit(#b) } - } - SomeLit(x) => { - quote! { dhall_syntax::ExprF::SomeLit(#x) } - } - EmptyListLit(t) => { - quote! { dhall_syntax::ExprF::EmptyListLit(#t) } - } - NEListLit(es) => { - let es = quote_vec(es); - quote! { dhall_syntax::ExprF::NEListLit(#es) } - } - RecordType(m) => { - let m = quote_map(m); - quote! { dhall_syntax::ExprF::RecordType(#m) } - } - RecordLit(m) => { - let m = quote_map(m); - quote! { dhall_syntax::ExprF::RecordLit(#m) } - } - UnionType(m) => { - let m = quote_opt_map(m); - quote! { dhall_syntax::ExprF::UnionType(#m) } - } - e => unimplemented!("{:?}", e), - } -} - -// Returns an expression of type SubExpr<_, _>. Expects interpolated variables -// to be of type SubExpr<_, _>. -fn quote_subexpr( - expr: &SubExpr, - ctx: &Context, -) -> TokenStream { - use dhall_syntax::ExprF::*; - match expr.as_ref().map_ref_with_special_handling_of_binders( - |e| quote_subexpr(e, ctx), - |l, e| quote_subexpr(e, &ctx.insert(l.clone(), ())), - |_| unreachable!(), - |_| unreachable!(), - Label::clone, - ) { - Var(V(ref s, n)) => { - match ctx.lookup(s, n) { - // Non-free variable; interpolates as itself - Some(()) => { - let s: String = s.into(); - let var = quote! { dhall_syntax::V(#s.into(), #n) }; - rc(quote! { dhall_syntax::ExprF::Var(#var) }) - } - // Free variable; interpolates as a rust variable - None => { - let s: String = s.into(); - // TODO: insert appropriate shifts ? - let v: TokenStream = s.parse().unwrap(); - quote! { { - let x: dhall_syntax::SubExpr<_, _> = #v.clone(); - x - } } - } - } - } - e => rc(quote_exprf(e)), - } -} - -// Returns an expression of type Expr<_, _>. Expects interpolated variables -// to be of type SubExpr<_, _>. -fn quote_expr(expr: &Expr, ctx: &Context) -> TokenStream { - use dhall_syntax::ExprF::*; - match expr.map_ref_with_special_handling_of_binders( - |e| quote_subexpr(e, ctx), - |l, e| quote_subexpr(e, &ctx.insert(l.clone(), ())), - |_| unreachable!(), - |_| unreachable!(), - Label::clone, - ) { - Var(V(ref s, n)) => { - match ctx.lookup(s, n) { - // Non-free variable; interpolates as itself - Some(()) => { - let s: String = s.into(); - let var = quote! { dhall_syntax::V(#s.into(), #n) }; - quote! { dhall_syntax::ExprF::Var(#var) } - } - // Free variable; interpolates as a rust variable - None => { - let s: String = s.into(); - // TODO: insert appropriate shifts ? - let v: TokenStream = s.parse().unwrap(); - quote! { { - let x: dhall_syntax::SubExpr<_, _> = #v.clone(); - x.unroll() - } } - } - } - } - e => quote_exprf(e), - } -} - -fn quote_builtin(b: Builtin) -> TokenStream { - format!("dhall_syntax::Builtin::{:?}", b).parse().unwrap() -} - -fn quote_const(c: Const) -> TokenStream { - format!("dhall_syntax::Const::{:?}", c).parse().unwrap() -} - -fn quote_binop(b: BinOp) -> TokenStream { - format!("dhall_syntax::BinOp::{:?}", b).parse().unwrap() -} - -fn quote_label(l: &Label) -> TokenStream { - let l = String::from(l); - quote! { dhall_syntax::Label::from(#l) } -} - -fn rc(x: TokenStream) -> TokenStream { - quote! { dhall_syntax::rc(#x) } -} - -fn quote_opt(x: Option) -> TokenStream -where - TS: quote::ToTokens + std::fmt::Debug, -{ - match x { - Some(x) => quote!(Some(#x)), - None => quote!(None), - } -} - -fn quote_vec(e: Vec) -> TokenStream -where - TS: quote::ToTokens + std::fmt::Debug, -{ - quote! { vec![ #(#e),* ] } -} - -fn quote_map(m: BTreeMap) -> TokenStream -where - TS: quote::ToTokens + std::fmt::Debug, -{ - let entries = m.into_iter().map(|(k, v)| { - let k = quote_label(&k); - quote!(m.insert(#k, #v);) - }); - quote! { { - use std::collections::BTreeMap; - let mut m = BTreeMap::new(); - #( #entries )* - m - } } -} - -fn quote_opt_map(m: BTreeMap>) -> TokenStream -where - TS: quote::ToTokens + std::fmt::Debug, -{ - quote_map(m.into_iter().map(|(k, v)| (k, quote_opt(v))).collect()) -} diff --git a/dhall_proc_macros/Cargo.toml b/dhall_proc_macros/Cargo.toml new file mode 100644 index 0000000..76a749f --- /dev/null +++ b/dhall_proc_macros/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "dhall_proc_macros" +version = "0.1.0" +authors = ["Nadrieril "] +license = "BSD-2-Clause" +edition = "2018" + +[lib] +proc-macro = true +doctest = false + +[dependencies] +itertools = "0.8.0" +quote = "0.6.11" +proc-macro2 = "0.4.27" +syn = "0.15.29" +dhall_syntax = { path = "../dhall_syntax" } diff --git a/dhall_proc_macros/src/derive.rs b/dhall_proc_macros/src/derive.rs new file mode 100644 index 0000000..bcefb17 --- /dev/null +++ b/dhall_proc_macros/src/derive.rs @@ -0,0 +1,179 @@ +extern crate proc_macro; +// use dhall_syntax::*; +use proc_macro::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; +use syn::Error; +use syn::{parse_quote, DeriveInput}; + +pub fn derive_simple_static_type(input: TokenStream) -> TokenStream { + TokenStream::from(match derive_simple_static_type_inner(input) { + Ok(tokens) => tokens, + Err(err) => err.to_compile_error(), + }) +} + +fn get_simple_static_type(ty: T) -> proc_macro2::TokenStream +where + T: quote::ToTokens, +{ + quote!( + <#ty as ::dhall::de::SimpleStaticType>::get_simple_static_type() + ) +} + +fn derive_for_struct( + data: &syn::DataStruct, + constraints: &mut Vec, +) -> Result { + let fields = match &data.fields { + syn::Fields::Named(fields) => fields + .named + .iter() + .map(|f| { + let name = f.ident.as_ref().unwrap().to_string(); + let ty = &f.ty; + (name, ty) + }) + .collect(), + syn::Fields::Unnamed(fields) => fields + .unnamed + .iter() + .enumerate() + .map(|(i, f)| { + let name = format!("_{}", i + 1); + let ty = &f.ty; + (name, ty) + }) + .collect(), + syn::Fields::Unit => vec![], + }; + let fields = fields + .into_iter() + .map(|(name, ty)| { + let name = dhall_syntax::Label::from(name); + constraints.push(ty.clone()); + let ty = get_simple_static_type(ty); + (name, quote!(#ty.into())) + }) + .collect(); + let record = + crate::quote::quote_exprf(dhall_syntax::ExprF::RecordType(fields)); + Ok(quote! { dhall_syntax::rc(#record) }) +} + +fn derive_for_enum( + data: &syn::DataEnum, + constraints: &mut Vec, +) -> Result { + let variants = data + .variants + .iter() + .map(|v| { + let name = dhall_syntax::Label::from(v.ident.to_string()); + match &v.fields { + syn::Fields::Unit => Ok((name, None)), + syn::Fields::Unnamed(fields) if fields.unnamed.is_empty() => { + Ok((name, None)) + } + syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { + let ty = &fields.unnamed.iter().next().unwrap().ty; + constraints.push(ty.clone()); + let ty = get_simple_static_type(ty); + Ok((name, Some(quote!(#ty.into())))) + } + syn::Fields::Unnamed(_) => Err(Error::new( + v.span(), + "Variants with more than one field are not supported", + )), + syn::Fields::Named(_) => Err(Error::new( + v.span(), + "Named variants are not supported", + )), + } + }) + .collect::>()?; + + let union = + crate::quote::quote_exprf(dhall_syntax::ExprF::UnionType(variants)); + Ok(quote! { dhall_syntax::rc(#union) }) +} + +pub fn derive_simple_static_type_inner( + input: TokenStream, +) -> Result { + let input: DeriveInput = syn::parse_macro_input::parse(input)?; + + // List of types that must impl Type + let mut constraints = vec![]; + + let get_type = match &input.data { + syn::Data::Struct(data) => derive_for_struct(data, &mut constraints)?, + syn::Data::Enum(data) if data.variants.is_empty() => { + return Err(Error::new( + input.span(), + "Empty enums are not supported", + )) + } + syn::Data::Enum(data) => derive_for_enum(data, &mut constraints)?, + syn::Data::Union(x) => { + return Err(Error::new( + x.union_token.span(), + "Unions are not supported", + )) + } + }; + + 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: Type, with an appropriate span + let assert_name = + syn::Ident::new(&format!("_AssertType{}", i), ty.span()); + let mut local_where_clause = orig_where_clause.clone(); + local_where_clause + .predicates + .push(parse_quote!(#ty: ::dhall::de::SimpleStaticType)); + 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 Type impl + let mut where_clause = orig_where_clause.clone(); + for ty in constraints.iter() { + where_clause + .predicates + .push(parse_quote!(#ty: ::dhall::de::SimpleStaticType)); + } + + let ident = &input.ident; + let tokens = quote! { + impl #impl_generics ::dhall::de::SimpleStaticType + for #ident #ty_generics + #where_clause { + fn get_simple_static_type<'get_simple_static_type>() -> + ::dhall::expr::SimpleType<'get_simple_static_type> { + #(#assertions)* + ::dhall::expr::SimpleType::from(#get_type) + } + } + }; + Ok(tokens) +} diff --git a/dhall_proc_macros/src/lib.rs b/dhall_proc_macros/src/lib.rs new file mode 100644 index 0000000..1124968 --- /dev/null +++ b/dhall_proc_macros/src/lib.rs @@ -0,0 +1,26 @@ +//! This crate contains the code-generation primitives for the [dhall-rust][dhall-rust] crate. +//! This is highly unstable and breaks regularly; use at your own risk. +//! +//! [dhall-rust]: https://github.com/Nadrieril/dhall-rust + +extern crate proc_macro; + +mod derive; +mod quote; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn expr(input: TokenStream) -> TokenStream { + quote::expr(input) +} + +#[proc_macro] +pub fn subexpr(input: TokenStream) -> TokenStream { + quote::subexpr(input) +} + +#[proc_macro_derive(SimpleStaticType)] +pub fn derive_simple_static_type(input: TokenStream) -> TokenStream { + derive::derive_simple_static_type(input) +} diff --git a/dhall_proc_macros/src/quote.rs b/dhall_proc_macros/src/quote.rs new file mode 100644 index 0000000..c2323fa --- /dev/null +++ b/dhall_proc_macros/src/quote.rs @@ -0,0 +1,227 @@ +extern crate proc_macro; +use dhall_syntax::context::Context; +use dhall_syntax::*; +use proc_macro2::TokenStream; +use quote::quote; +use std::collections::BTreeMap; + +pub fn expr(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input_str = input.to_string(); + let expr: SubExpr<_, Import> = parse_expr(&input_str).unwrap().unnote(); + let no_import = + |_: &Import| -> X { panic!("Don't use import in dhall::expr!()") }; + let expr = expr.map_embed(no_import); + let output = quote_expr(&expr.unroll(), &Context::new()); + output.into() +} + +pub fn subexpr(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input_str = input.to_string(); + let expr: SubExpr<_, Import> = parse_expr(&input_str).unwrap().unnote(); + let no_import = + |_: &Import| -> X { panic!("Don't use import in dhall::subexpr!()") }; + let expr = expr.map_embed(no_import); + let output = quote_subexpr(&expr, &Context::new()); + output.into() +} + +// Returns an expression of type ExprF, where T is the +// type of the subexpressions after interpolation. +pub fn quote_exprf(expr: ExprF) -> TokenStream +where + TS: quote::ToTokens + std::fmt::Debug, +{ + use dhall_syntax::ExprF::*; + match expr { + Var(_) => unreachable!(), + Pi(x, t, b) => { + let x = quote_label(&x); + quote! { dhall_syntax::ExprF::Pi(#x, #t, #b) } + } + Lam(x, t, b) => { + let x = quote_label(&x); + quote! { dhall_syntax::ExprF::Lam(#x, #t, #b) } + } + App(f, a) => { + quote! { dhall_syntax::ExprF::App(#f, #a) } + } + Annot(x, t) => { + quote! { dhall_syntax::ExprF::Annot(#x, #t) } + } + Const(c) => { + let c = quote_const(c); + quote! { dhall_syntax::ExprF::Const(#c) } + } + Builtin(b) => { + let b = quote_builtin(b); + quote! { dhall_syntax::ExprF::Builtin(#b) } + } + BinOp(o, a, b) => { + let o = quote_binop(o); + quote! { dhall_syntax::ExprF::BinOp(#o, #a, #b) } + } + NaturalLit(n) => { + quote! { dhall_syntax::ExprF::NaturalLit(#n) } + } + BoolLit(b) => { + quote! { dhall_syntax::ExprF::BoolLit(#b) } + } + SomeLit(x) => { + quote! { dhall_syntax::ExprF::SomeLit(#x) } + } + EmptyListLit(t) => { + quote! { dhall_syntax::ExprF::EmptyListLit(#t) } + } + NEListLit(es) => { + let es = quote_vec(es); + quote! { dhall_syntax::ExprF::NEListLit(#es) } + } + RecordType(m) => { + let m = quote_map(m); + quote! { dhall_syntax::ExprF::RecordType(#m) } + } + RecordLit(m) => { + let m = quote_map(m); + quote! { dhall_syntax::ExprF::RecordLit(#m) } + } + UnionType(m) => { + let m = quote_opt_map(m); + quote! { dhall_syntax::ExprF::UnionType(#m) } + } + e => unimplemented!("{:?}", e), + } +} + +// Returns an expression of type SubExpr<_, _>. Expects interpolated variables +// to be of type SubExpr<_, _>. +fn quote_subexpr( + expr: &SubExpr, + ctx: &Context, +) -> TokenStream { + use dhall_syntax::ExprF::*; + match expr.as_ref().map_ref_with_special_handling_of_binders( + |e| quote_subexpr(e, ctx), + |l, e| quote_subexpr(e, &ctx.insert(l.clone(), ())), + |_| unreachable!(), + |_| unreachable!(), + Label::clone, + ) { + Var(V(ref s, n)) => { + match ctx.lookup(s, n) { + // Non-free variable; interpolates as itself + Some(()) => { + let s: String = s.into(); + let var = quote! { dhall_syntax::V(#s.into(), #n) }; + rc(quote! { dhall_syntax::ExprF::Var(#var) }) + } + // Free variable; interpolates as a rust variable + None => { + let s: String = s.into(); + // TODO: insert appropriate shifts ? + let v: TokenStream = s.parse().unwrap(); + quote! { { + let x: dhall_syntax::SubExpr<_, _> = #v.clone(); + x + } } + } + } + } + e => rc(quote_exprf(e)), + } +} + +// Returns an expression of type Expr<_, _>. Expects interpolated variables +// to be of type SubExpr<_, _>. +fn quote_expr(expr: &Expr, ctx: &Context) -> TokenStream { + use dhall_syntax::ExprF::*; + match expr.map_ref_with_special_handling_of_binders( + |e| quote_subexpr(e, ctx), + |l, e| quote_subexpr(e, &ctx.insert(l.clone(), ())), + |_| unreachable!(), + |_| unreachable!(), + Label::clone, + ) { + Var(V(ref s, n)) => { + match ctx.lookup(s, n) { + // Non-free variable; interpolates as itself + Some(()) => { + let s: String = s.into(); + let var = quote! { dhall_syntax::V(#s.into(), #n) }; + quote! { dhall_syntax::ExprF::Var(#var) } + } + // Free variable; interpolates as a rust variable + None => { + let s: String = s.into(); + // TODO: insert appropriate shifts ? + let v: TokenStream = s.parse().unwrap(); + quote! { { + let x: dhall_syntax::SubExpr<_, _> = #v.clone(); + x.unroll() + } } + } + } + } + e => quote_exprf(e), + } +} + +fn quote_builtin(b: Builtin) -> TokenStream { + format!("dhall_syntax::Builtin::{:?}", b).parse().unwrap() +} + +fn quote_const(c: Const) -> TokenStream { + format!("dhall_syntax::Const::{:?}", c).parse().unwrap() +} + +fn quote_binop(b: BinOp) -> TokenStream { + format!("dhall_syntax::BinOp::{:?}", b).parse().unwrap() +} + +fn quote_label(l: &Label) -> TokenStream { + let l = String::from(l); + quote! { dhall_syntax::Label::from(#l) } +} + +fn rc(x: TokenStream) -> TokenStream { + quote! { dhall_syntax::rc(#x) } +} + +fn quote_opt(x: Option) -> TokenStream +where + TS: quote::ToTokens + std::fmt::Debug, +{ + match x { + Some(x) => quote!(Some(#x)), + None => quote!(None), + } +} + +fn quote_vec(e: Vec) -> TokenStream +where + TS: quote::ToTokens + std::fmt::Debug, +{ + quote! { vec![ #(#e),* ] } +} + +fn quote_map(m: BTreeMap) -> TokenStream +where + TS: quote::ToTokens + std::fmt::Debug, +{ + let entries = m.into_iter().map(|(k, v)| { + let k = quote_label(&k); + quote!(m.insert(#k, #v);) + }); + quote! { { + use std::collections::BTreeMap; + let mut m = BTreeMap::new(); + #( #entries )* + m + } } +} + +fn quote_opt_map(m: BTreeMap>) -> TokenStream +where + TS: quote::ToTokens + std::fmt::Debug, +{ + quote_map(m.into_iter().map(|(k, v)| (k, quote_opt(v))).collect()) +} -- cgit v1.2.3