From 42d0f8100462f8a17a3ba1b86664310cdb71dfdc Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 6 Apr 2019 17:55:43 +0200 Subject: Rename some modules --- dhall/src/dhall_type.rs | 77 -------------- dhall/src/lib.rs | 6 +- dhall/src/traits.rs | 77 ++++++++++++++ dhall/tests/dhall_type.rs | 53 ---------- dhall/tests/traits.rs | 53 ++++++++++ dhall_generator/src/derive.rs | 175 ++++++++++++++++++++++++++++++++ dhall_generator/src/dhall_expr.rs | 205 -------------------------------------- dhall_generator/src/dhall_type.rs | 173 -------------------------------- dhall_generator/src/lib.rs | 10 +- dhall_generator/src/quote.rs | 205 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 518 insertions(+), 516 deletions(-) delete mode 100644 dhall/src/dhall_type.rs create mode 100644 dhall/src/traits.rs delete mode 100644 dhall/tests/dhall_type.rs create mode 100644 dhall/tests/traits.rs create mode 100644 dhall_generator/src/derive.rs delete mode 100644 dhall_generator/src/dhall_expr.rs delete mode 100644 dhall_generator/src/dhall_type.rs create mode 100644 dhall_generator/src/quote.rs diff --git a/dhall/src/dhall_type.rs b/dhall/src/dhall_type.rs deleted file mode 100644 index 64e07d9..0000000 --- a/dhall/src/dhall_type.rs +++ /dev/null @@ -1,77 +0,0 @@ -use dhall_core::*; -use dhall_generator::*; - -#[derive(Debug, Clone)] -pub enum ConversionError {} - -pub trait StaticType { - fn get_type() -> DhallExpr; - // fn as_dhall(&self) -> DhallExpr; - // fn from_dhall(e: DhallExpr) -> Result; -} - -impl StaticType for bool { - fn get_type() -> DhallExpr { - dhall_expr!(Bool) - } -} - -impl StaticType for Natural { - fn get_type() -> DhallExpr { - dhall_expr!(Natural) - } -} - -impl StaticType for Integer { - fn get_type() -> DhallExpr { - dhall_expr!(Integer) - } -} - -impl StaticType for String { - fn get_type() -> DhallExpr { - dhall_expr!(Text) - } -} - -impl StaticType for (A, B) { - fn get_type() -> DhallExpr { - let ta = A::get_type(); - let tb = B::get_type(); - dhall_expr!({ _1: ta, _2: tb }) - } -} - -impl StaticType for Option { - fn get_type() -> DhallExpr { - let t = T::get_type(); - dhall_expr!(Optional t) - } -} - -impl StaticType for Vec { - fn get_type() -> DhallExpr { - let t = T::get_type(); - dhall_expr!(List t) - } -} - -impl<'a, T: StaticType> StaticType for &'a T { - fn get_type() -> DhallExpr { - T::get_type() - } -} - -impl StaticType for std::marker::PhantomData { - fn get_type() -> DhallExpr { - dhall_expr!({}) - } -} - -impl StaticType for Result { - fn get_type() -> DhallExpr { - let tt = T::get_type(); - let te = E::get_type(); - dhall_expr!(< Ok: tt | Err: te>) - } -} diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 5a155c8..7439312 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -10,14 +10,14 @@ mod normalize; pub use crate::normalize::*; pub mod binary; -mod dhall_type; pub mod imports; +mod traits; pub mod typecheck; -pub use crate::dhall_type::*; +pub use crate::imports::*; +pub use crate::traits::*; pub use dhall_generator::expr; pub use dhall_generator::subexpr; pub use dhall_generator::StaticType; -pub use crate::imports::*; // pub struct DhallExpr(dhall_core::DhallExpr); diff --git a/dhall/src/traits.rs b/dhall/src/traits.rs new file mode 100644 index 0000000..64e07d9 --- /dev/null +++ b/dhall/src/traits.rs @@ -0,0 +1,77 @@ +use dhall_core::*; +use dhall_generator::*; + +#[derive(Debug, Clone)] +pub enum ConversionError {} + +pub trait StaticType { + fn get_type() -> DhallExpr; + // fn as_dhall(&self) -> DhallExpr; + // fn from_dhall(e: DhallExpr) -> Result; +} + +impl StaticType for bool { + fn get_type() -> DhallExpr { + dhall_expr!(Bool) + } +} + +impl StaticType for Natural { + fn get_type() -> DhallExpr { + dhall_expr!(Natural) + } +} + +impl StaticType for Integer { + fn get_type() -> DhallExpr { + dhall_expr!(Integer) + } +} + +impl StaticType for String { + fn get_type() -> DhallExpr { + dhall_expr!(Text) + } +} + +impl StaticType for (A, B) { + fn get_type() -> DhallExpr { + let ta = A::get_type(); + let tb = B::get_type(); + dhall_expr!({ _1: ta, _2: tb }) + } +} + +impl StaticType for Option { + fn get_type() -> DhallExpr { + let t = T::get_type(); + dhall_expr!(Optional t) + } +} + +impl StaticType for Vec { + fn get_type() -> DhallExpr { + let t = T::get_type(); + dhall_expr!(List t) + } +} + +impl<'a, T: StaticType> StaticType for &'a T { + fn get_type() -> DhallExpr { + T::get_type() + } +} + +impl StaticType for std::marker::PhantomData { + fn get_type() -> DhallExpr { + dhall_expr!({}) + } +} + +impl StaticType for Result { + fn get_type() -> DhallExpr { + let tt = T::get_type(); + let te = E::get_type(); + dhall_expr!(< Ok: tt | Err: te>) + } +} diff --git a/dhall/tests/dhall_type.rs b/dhall/tests/dhall_type.rs deleted file mode 100644 index ac6b5e6..0000000 --- a/dhall/tests/dhall_type.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![feature(proc_macro_hygiene)] -use dhall::StaticType; -use dhall_generator::dhall_expr; - -#[test] -fn test_dhall_type() { - assert_eq!(bool::get_type(), dhall_expr!(Bool)); - assert_eq!(String::get_type(), dhall_expr!(Text)); - assert_eq!( - <(bool, Option)>::get_type(), - dhall_expr!({ _1: Bool, _2: Optional Text }) - ); - - #[derive(dhall::StaticType)] - #[allow(dead_code)] - struct A { - field1: bool, - field2: Option, - } - assert_eq!( - ::get_type(), - dhall_expr!({ field1: Bool, field2: Optional Bool }) - ); - - #[derive(StaticType)] - #[allow(dead_code)] - struct B<'a, T: 'a> { - field1: &'a T, - field2: Option, - } - assert_eq!(>::get_type(), A::get_type()); - - #[derive(StaticType)] - #[allow(dead_code)] - struct C(T, Option); - assert_eq!(>::get_type(), <(bool, Option)>::get_type()); - - #[derive(StaticType)] - #[allow(dead_code)] - struct D(); - assert_eq!( - >::get_type(), - dhall_expr!({ _1: {}, _2: Optional Text }) - ); - - #[derive(StaticType)] - #[allow(dead_code)] - enum E { - A(T), - B(String), - }; - assert_eq!(>::get_type(), dhall_expr!(< A: Bool | B: Text >)); -} diff --git a/dhall/tests/traits.rs b/dhall/tests/traits.rs new file mode 100644 index 0000000..ac6b5e6 --- /dev/null +++ b/dhall/tests/traits.rs @@ -0,0 +1,53 @@ +#![feature(proc_macro_hygiene)] +use dhall::StaticType; +use dhall_generator::dhall_expr; + +#[test] +fn test_dhall_type() { + assert_eq!(bool::get_type(), dhall_expr!(Bool)); + assert_eq!(String::get_type(), dhall_expr!(Text)); + assert_eq!( + <(bool, Option)>::get_type(), + dhall_expr!({ _1: Bool, _2: Optional Text }) + ); + + #[derive(dhall::StaticType)] + #[allow(dead_code)] + struct A { + field1: bool, + field2: Option, + } + assert_eq!( + ::get_type(), + dhall_expr!({ field1: Bool, field2: Optional Bool }) + ); + + #[derive(StaticType)] + #[allow(dead_code)] + struct B<'a, T: 'a> { + field1: &'a T, + field2: Option, + } + assert_eq!(>::get_type(), A::get_type()); + + #[derive(StaticType)] + #[allow(dead_code)] + struct C(T, Option); + assert_eq!(>::get_type(), <(bool, Option)>::get_type()); + + #[derive(StaticType)] + #[allow(dead_code)] + struct D(); + assert_eq!( + >::get_type(), + dhall_expr!({ _1: {}, _2: Optional Text }) + ); + + #[derive(StaticType)] + #[allow(dead_code)] + enum E { + A(T), + B(String), + }; + assert_eq!(>::get_type(), dhall_expr!(< A: Bool | B: Text >)); +} diff --git a/dhall_generator/src/derive.rs b/dhall_generator/src/derive.rs new file mode 100644 index 0000000..159ff5c --- /dev/null +++ b/dhall_generator/src/derive.rs @@ -0,0 +1,175 @@ +extern crate proc_macro; +// use dhall_core::*; +use proc_macro::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; +use syn::Error; +use syn::{parse_quote, DeriveInput}; + +pub fn derive_type(input: TokenStream) -> TokenStream { + TokenStream::from(match derive_type_inner(input) { + Ok(tokens) => tokens, + Err(err) => err.to_compile_error(), + }) +} + +pub 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_core::Label::from(name); + constraints.push(ty.clone()); + (name, quote!(<#ty as dhall::StaticType>::get_type())) + }) + .collect(); + let record = + crate::quote::quote_exprf(dhall_core::ExprF::RecordType(fields)); + Ok(quote! { dhall_core::rc(#record) }) +} + +pub fn derive_for_enum( + data: &syn::DataEnum, + constraints: &mut Vec, +) -> Result { + let variants = data + .variants + .iter() + .map(|v| { + let name = dhall_core::Label::from(v.ident.to_string()); + let ty = match &v.fields { + syn::Fields::Unnamed(fields) if fields.unnamed.is_empty() => { + Err(Error::new( + v.span(), + "Nullary variants are not supported", + )) + } + syn::Fields::Unnamed(fields) if fields.unnamed.len() > 1 => { + Err(Error::new( + v.span(), + "Variants with more than one field are not supported", + )) + } + syn::Fields::Unnamed(fields) => { + Ok(&fields.unnamed.iter().next().unwrap().ty) + } + syn::Fields::Named(_) => Err(Error::new( + v.span(), + "Named variants are not supported", + )), + syn::Fields::Unit => Err(Error::new( + v.span(), + "Nullary variants are not supported", + )), + }; + let ty = ty?; + constraints.push(ty.clone()); + Ok((name, quote!(<#ty as dhall::StaticType>::get_type()))) + }) + .collect::>()?; + + let union = + crate::quote::quote_exprf(dhall_core::ExprF::UnionType(variants)); + Ok(quote! { dhall_core::rc(#union) }) +} + +pub fn derive_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::StaticType)); + 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::StaticType)); + } + + let ident = &input.ident; + let tokens = quote! { + impl #impl_generics dhall::StaticType for #ident #ty_generics + #where_clause { + fn get_type() -> dhall_core::DhallExpr { + #(#assertions)* + #get_type + } + } + }; + Ok(tokens) +} diff --git a/dhall_generator/src/dhall_expr.rs b/dhall_generator/src/dhall_expr.rs deleted file mode 100644 index d0c5733..0000000 --- a/dhall_generator/src/dhall_expr.rs +++ /dev/null @@ -1,205 +0,0 @@ -extern crate proc_macro; -use dhall_core::context::Context; -use dhall_core::*; -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 = parse_expr(&input_str).unwrap(); - let no_import = - |_: &Import| -> X { panic!("Don't use import in dhall::expr!()") }; - let expr = expr.as_ref().map_embed(&no_import); - let output = quote_expr(&expr, &Context::new()); - output.into() -} - -pub fn subexpr(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input_str = input.to_string(); - let expr: SubExpr = parse_expr(&input_str).unwrap(); - let no_import = - |_: &Import| -> X { panic!("Don't use import in dhall::subexpr!()") }; - let expr = expr.as_ref().map_embed(&no_import); - let output = quote_subexpr(&rc(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, -{ - let quote_map = |m: BTreeMap| -> TokenStream { - 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 - } } - }; - - let quote_vec = |e: Vec| -> TokenStream { - quote! { vec![ #(#e),* ] } - }; - - use dhall_core::ExprF::*; - match expr { - Var(_) => unreachable!(), - Pi(x, t, b) => { - let x = quote_label(&x); - quote! { dhall_core::ExprF::Pi(#x, #t, #b) } - } - Lam(x, t, b) => { - let x = quote_label(&x); - quote! { dhall_core::ExprF::Lam(#x, #t, #b) } - } - App(f, a) => { - let a = quote_vec(a); - quote! { dhall_core::ExprF::App(#f, #a) } - } - Const(c) => { - let c = quote_const(c); - quote! { dhall_core::ExprF::Const(#c) } - } - Builtin(b) => { - let b = quote_builtin(b); - quote! { dhall_core::ExprF::Builtin(#b) } - } - BinOp(o, a, b) => { - let o = quote_binop(o); - quote! { dhall_core::ExprF::BinOp(#o, #a, #b) } - } - NaturalLit(n) => { - quote! { dhall_core::ExprF::NaturalLit(#n) } - } - BoolLit(b) => { - quote! { dhall_core::ExprF::BoolLit(#b) } - } - EmptyOptionalLit(x) => { - quote! { dhall_core::ExprF::EmptyOptionalLit(#x) } - } - NEOptionalLit(x) => { - quote! { dhall_core::ExprF::NEOptionalLit(#x) } - } - EmptyListLit(t) => { - quote! { dhall_core::ExprF::EmptyListLit(#t) } - } - NEListLit(es) => { - let es = quote_vec(es); - quote! { dhall_core::ExprF::NEListLit(#es) } - } - RecordType(m) => { - let m = quote_map(m); - quote! { dhall_core::ExprF::RecordType(#m) } - } - RecordLit(m) => { - let m = quote_map(m); - quote! { dhall_core::ExprF::RecordLit(#m) } - } - UnionType(m) => { - let m = quote_map(m); - quote! { dhall_core::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_core::ExprF::*; - match expr.as_ref().map_ref( - |e| quote_subexpr(e, ctx), - |l, e| quote_subexpr(e, &ctx.insert(l.clone(), ())), - |_| unreachable!(), - |_| unreachable!(), - |l| l.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_core::V(#s.into(), #n) }; - bx(quote! { dhall_core::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_core::SubExpr<_, _> = #v.clone(); - x - } } - } - } - } - e => bx(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_core::ExprF::*; - match expr.map_ref( - |e| quote_subexpr(e, ctx), - |l, e| quote_subexpr(e, &ctx.insert(l.clone(), ())), - |_| unreachable!(), - |_| unreachable!(), - |l| l.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_core::V(#s.into(), #n) }; - quote! { dhall_core::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_core::SubExpr<_, _> = #v.clone(); - x.unroll() - } } - } - } - } - e => quote_exprf(e), - } -} - -fn quote_builtin(b: Builtin) -> TokenStream { - format!("dhall_core::Builtin::{:?}", b).parse().unwrap() -} - -fn quote_const(c: Const) -> TokenStream { - format!("dhall_core::Const::{:?}", c).parse().unwrap() -} - -fn quote_binop(b: BinOp) -> TokenStream { - format!("dhall_core::BinOp::{:?}", b).parse().unwrap() -} - -fn quote_label(l: &Label) -> TokenStream { - let l = String::from(l); - quote! { dhall_core::Label::from(#l) } -} - -fn bx(x: TokenStream) -> TokenStream { - quote! { dhall_core::bx(#x) } -} diff --git a/dhall_generator/src/dhall_type.rs b/dhall_generator/src/dhall_type.rs deleted file mode 100644 index 38c871d..0000000 --- a/dhall_generator/src/dhall_type.rs +++ /dev/null @@ -1,173 +0,0 @@ -extern crate proc_macro; -// use dhall_core::*; -use proc_macro::TokenStream; -use quote::{quote, quote_spanned}; -use syn::spanned::Spanned; -use syn::Error; -use syn::{parse_quote, DeriveInput}; - -pub fn derive_type(input: TokenStream) -> TokenStream { - TokenStream::from(match derive_type_inner(input) { - Ok(tokens) => tokens, - Err(err) => err.to_compile_error(), - }) -} - -pub 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_core::Label::from(name); - constraints.push(ty.clone()); - (name, quote!(<#ty as dhall::StaticType>::get_type())) - }) - .collect(); - let record = - crate::dhall_expr::quote_exprf(dhall_core::ExprF::RecordType(fields)); - Ok(quote! { dhall_core::rc(#record) }) -} - -pub fn derive_for_enum( - data: &syn::DataEnum, - constraints: &mut Vec, -) -> Result { - let variants = data - .variants - .iter() - .map(|v| { - let name = dhall_core::Label::from(v.ident.to_string()); - let ty = match &v.fields { - syn::Fields::Unnamed(fields) if fields.unnamed.is_empty() => { - Err(Error::new( - v.span(), - "Nullary variants are not supported", - )) - } - syn::Fields::Unnamed(fields) if fields.unnamed.len() > 1 => { - Err(Error::new( - v.span(), - "Variants with more than one field are not supported", - )) - } - syn::Fields::Unnamed(fields) => { - Ok(&fields.unnamed.iter().next().unwrap().ty) - } - syn::Fields::Named(_) => Err(Error::new( - v.span(), - "Named variants are not supported", - )), - syn::Fields::Unit => Err(Error::new( - v.span(), - "Nullary variants are not supported", - )), - }; - let ty = ty?; - constraints.push(ty.clone()); - Ok((name, quote!(<#ty as dhall::StaticType>::get_type()))) - }) - .collect::>()?; - - let union = - crate::dhall_expr::quote_exprf(dhall_core::ExprF::UnionType(variants)); - Ok(quote! { dhall_core::rc(#union) }) -} - -pub fn derive_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::StaticType)); - 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::StaticType)); - } - - let ident = &input.ident; - let tokens = quote! { - impl #impl_generics dhall::StaticType for #ident #ty_generics - #where_clause { - fn get_type() -> dhall_core::DhallExpr { - #(#assertions)* - #get_type - } - } - }; - Ok(tokens) -} diff --git a/dhall_generator/src/lib.rs b/dhall_generator/src/lib.rs index 08ce21e..b422834 100644 --- a/dhall_generator/src/lib.rs +++ b/dhall_generator/src/lib.rs @@ -1,7 +1,7 @@ extern crate proc_macro; -mod dhall_expr; -mod dhall_type; +mod derive; +mod quote; use proc_macro::TokenStream; @@ -13,15 +13,15 @@ pub fn dhall_expr(input: TokenStream) -> TokenStream { #[proc_macro] pub fn expr(input: TokenStream) -> TokenStream { - dhall_expr::expr(input) + quote::expr(input) } #[proc_macro] pub fn subexpr(input: TokenStream) -> TokenStream { - dhall_expr::subexpr(input) + quote::subexpr(input) } #[proc_macro_derive(StaticType)] pub fn derive_type(input: TokenStream) -> TokenStream { - dhall_type::derive_type(input) + derive::derive_type(input) } diff --git a/dhall_generator/src/quote.rs b/dhall_generator/src/quote.rs new file mode 100644 index 0000000..d0c5733 --- /dev/null +++ b/dhall_generator/src/quote.rs @@ -0,0 +1,205 @@ +extern crate proc_macro; +use dhall_core::context::Context; +use dhall_core::*; +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 = parse_expr(&input_str).unwrap(); + let no_import = + |_: &Import| -> X { panic!("Don't use import in dhall::expr!()") }; + let expr = expr.as_ref().map_embed(&no_import); + let output = quote_expr(&expr, &Context::new()); + output.into() +} + +pub fn subexpr(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input_str = input.to_string(); + let expr: SubExpr = parse_expr(&input_str).unwrap(); + let no_import = + |_: &Import| -> X { panic!("Don't use import in dhall::subexpr!()") }; + let expr = expr.as_ref().map_embed(&no_import); + let output = quote_subexpr(&rc(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, +{ + let quote_map = |m: BTreeMap| -> TokenStream { + 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 + } } + }; + + let quote_vec = |e: Vec| -> TokenStream { + quote! { vec![ #(#e),* ] } + }; + + use dhall_core::ExprF::*; + match expr { + Var(_) => unreachable!(), + Pi(x, t, b) => { + let x = quote_label(&x); + quote! { dhall_core::ExprF::Pi(#x, #t, #b) } + } + Lam(x, t, b) => { + let x = quote_label(&x); + quote! { dhall_core::ExprF::Lam(#x, #t, #b) } + } + App(f, a) => { + let a = quote_vec(a); + quote! { dhall_core::ExprF::App(#f, #a) } + } + Const(c) => { + let c = quote_const(c); + quote! { dhall_core::ExprF::Const(#c) } + } + Builtin(b) => { + let b = quote_builtin(b); + quote! { dhall_core::ExprF::Builtin(#b) } + } + BinOp(o, a, b) => { + let o = quote_binop(o); + quote! { dhall_core::ExprF::BinOp(#o, #a, #b) } + } + NaturalLit(n) => { + quote! { dhall_core::ExprF::NaturalLit(#n) } + } + BoolLit(b) => { + quote! { dhall_core::ExprF::BoolLit(#b) } + } + EmptyOptionalLit(x) => { + quote! { dhall_core::ExprF::EmptyOptionalLit(#x) } + } + NEOptionalLit(x) => { + quote! { dhall_core::ExprF::NEOptionalLit(#x) } + } + EmptyListLit(t) => { + quote! { dhall_core::ExprF::EmptyListLit(#t) } + } + NEListLit(es) => { + let es = quote_vec(es); + quote! { dhall_core::ExprF::NEListLit(#es) } + } + RecordType(m) => { + let m = quote_map(m); + quote! { dhall_core::ExprF::RecordType(#m) } + } + RecordLit(m) => { + let m = quote_map(m); + quote! { dhall_core::ExprF::RecordLit(#m) } + } + UnionType(m) => { + let m = quote_map(m); + quote! { dhall_core::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_core::ExprF::*; + match expr.as_ref().map_ref( + |e| quote_subexpr(e, ctx), + |l, e| quote_subexpr(e, &ctx.insert(l.clone(), ())), + |_| unreachable!(), + |_| unreachable!(), + |l| l.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_core::V(#s.into(), #n) }; + bx(quote! { dhall_core::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_core::SubExpr<_, _> = #v.clone(); + x + } } + } + } + } + e => bx(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_core::ExprF::*; + match expr.map_ref( + |e| quote_subexpr(e, ctx), + |l, e| quote_subexpr(e, &ctx.insert(l.clone(), ())), + |_| unreachable!(), + |_| unreachable!(), + |l| l.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_core::V(#s.into(), #n) }; + quote! { dhall_core::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_core::SubExpr<_, _> = #v.clone(); + x.unroll() + } } + } + } + } + e => quote_exprf(e), + } +} + +fn quote_builtin(b: Builtin) -> TokenStream { + format!("dhall_core::Builtin::{:?}", b).parse().unwrap() +} + +fn quote_const(c: Const) -> TokenStream { + format!("dhall_core::Const::{:?}", c).parse().unwrap() +} + +fn quote_binop(b: BinOp) -> TokenStream { + format!("dhall_core::BinOp::{:?}", b).parse().unwrap() +} + +fn quote_label(l: &Label) -> TokenStream { + let l = String::from(l); + quote! { dhall_core::Label::from(#l) } +} + +fn bx(x: TokenStream) -> TokenStream { + quote! { dhall_core::bx(#x) } +} -- cgit v1.2.3