From e1a30c6f248c0c17c97598290a0d94993dbb0325 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 19:16:11 +0200 Subject: Add SimpleType and SimpeStaticType. Derive the latter --- dhall/src/expr.rs | 21 ++++++++ dhall/src/lib.rs | 3 +- dhall/src/traits.rs | 109 +++++++++++++++++++++++++++--------------- dhall/src/typecheck.rs | 21 ++++++-- dhall/tests/traits.rs | 54 ++++++++++++++------- dhall_generator/src/derive.rs | 35 +++++++++----- dhall_generator/src/lib.rs | 6 +-- 7 files changed, 173 insertions(+), 76 deletions(-) diff --git a/dhall/src/expr.rs b/dhall/src/expr.rs index 7baf628..5ff097b 100644 --- a/dhall/src/expr.rs +++ b/dhall/src/expr.rs @@ -36,6 +36,11 @@ derive_other_traits!(Typed); pub struct Normalized(pub(crate) SubExpr, pub(crate) Type); derive_other_traits!(Normalized); +/// An expression of type `Type` (like `Bool` or `Natural -> Text`, but not `Type`) +#[derive(Debug, Clone, Eq)] +pub struct SimpleType(pub(crate) SubExpr); +derive_other_traits!(SimpleType); + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Type(pub(crate) TypeInternal); @@ -44,3 +49,19 @@ pub(crate) enum TypeInternal { Expr(Box), Untyped, } + +// Exposed for the macros +#[doc(hidden)] +impl From for SubExpr { + fn from(x: SimpleType) -> SubExpr { + x.0 + } +} + +// Exposed for the macros +#[doc(hidden)] +impl From> for SimpleType { + fn from(x: SubExpr) -> SimpleType { + SimpleType(x) + } +} diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 5c5a641..3bfc46f 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -21,6 +21,7 @@ mod imports; mod normalize; mod traits; mod typecheck; +pub use crate::traits::SimpleStaticType; pub use crate::traits::StaticType; -pub use dhall_generator::StaticType; +pub use dhall_generator::SimpleStaticType; pub mod expr; diff --git a/dhall/src/traits.rs b/dhall/src/traits.rs index 64e07d9..4925457 100644 --- a/dhall/src/traits.rs +++ b/dhall/src/traits.rs @@ -1,77 +1,108 @@ +use crate::expr::*; use dhall_core::*; use dhall_generator::*; +use std::borrow::Cow; #[derive(Debug, Clone)] pub enum ConversionError {} pub trait StaticType { - fn get_type() -> DhallExpr; - // fn as_dhall(&self) -> DhallExpr; - // fn from_dhall(e: DhallExpr) -> Result; + fn get_static_type() -> Type; } -impl StaticType for bool { - fn get_type() -> DhallExpr { - dhall_expr!(Bool) +/// Trait for rust types that can be represented in dhall in +/// a single way, independent of the value. A typical example is `Option`, +/// represented by the dhall expression `Optional Bool`. A typical counterexample +/// is `HashMap` because dhall cannot represent records with a +/// variable number of fields. +pub trait SimpleStaticType { + fn get_simple_static_type() -> SimpleType; +} + +fn mktype(x: SubExpr) -> SimpleType { + SimpleType(x) +} + +impl StaticType for T { + fn get_static_type() -> Type { + crate::expr::Normalized( + T::get_simple_static_type().into(), + Type::const_type(), + ) + .into_type() + } +} + +impl StaticType for SimpleType { + fn get_static_type() -> Type { + Type::const_type() + } +} + +impl SimpleStaticType for bool { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Bool)) } } -impl StaticType for Natural { - fn get_type() -> DhallExpr { - dhall_expr!(Natural) +impl SimpleStaticType for Natural { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Natural)) } } -impl StaticType for Integer { - fn get_type() -> DhallExpr { - dhall_expr!(Integer) +impl SimpleStaticType for Integer { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Integer)) } } -impl StaticType for String { - fn get_type() -> DhallExpr { - dhall_expr!(Text) +impl SimpleStaticType for String { + fn get_simple_static_type() -> SimpleType { + mktype(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 SimpleStaticType for (A, B) { + fn get_simple_static_type() -> SimpleType { + let ta: SubExpr<_, _> = A::get_simple_static_type().into(); + let tb: SubExpr<_, _> = B::get_simple_static_type().into(); + mktype(dhall_expr!({ _1: ta, _2: tb })) } } -impl StaticType for Option { - fn get_type() -> DhallExpr { - let t = T::get_type(); - dhall_expr!(Optional t) +impl SimpleStaticType for Option { + fn get_simple_static_type() -> SimpleType { + let t: SubExpr<_, _> = T::get_simple_static_type().into(); + mktype(dhall_expr!(Optional t)) } } -impl StaticType for Vec { - fn get_type() -> DhallExpr { - let t = T::get_type(); - dhall_expr!(List t) +impl SimpleStaticType for Vec { + fn get_simple_static_type() -> SimpleType { + let t: SubExpr<_, _> = T::get_simple_static_type().into(); + mktype(dhall_expr!(List t)) } } -impl<'a, T: StaticType> StaticType for &'a T { - fn get_type() -> DhallExpr { - T::get_type() +impl<'a, T: SimpleStaticType> SimpleStaticType for &'a T { + fn get_simple_static_type() -> SimpleType { + T::get_simple_static_type() } } -impl StaticType for std::marker::PhantomData { - fn get_type() -> DhallExpr { - dhall_expr!({}) +impl SimpleStaticType for std::marker::PhantomData { + fn get_simple_static_type() -> SimpleType { + mktype(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>) +impl SimpleStaticType + for std::result::Result +{ + fn get_simple_static_type() -> SimpleType { + let tt: SubExpr<_, _> = T::get_simple_static_type().into(); + let te: SubExpr<_, _> = E::get_simple_static_type().into(); + mktype(dhall_expr!(< Ok: tt | Err: te>)) } } diff --git a/dhall/src/typecheck.rs b/dhall/src/typecheck.rs index 998d3ca..f51f1b6 100644 --- a/dhall/src/typecheck.rs +++ b/dhall/src/typecheck.rs @@ -42,7 +42,7 @@ impl Normalized { &self.0 } #[inline(always)] - fn into_expr(self) -> SubExpr { + pub(crate) fn into_expr(self) -> SubExpr { self.0 } #[inline(always)] @@ -50,7 +50,7 @@ impl Normalized { &self.1 } #[inline(always)] - fn into_type(self) -> Type { + pub(crate) fn into_type(self) -> Type { crate::expr::Type(TypeInternal::Expr(Box::new(self))) } // Expose the outermost constructor @@ -78,7 +78,7 @@ impl Type { } } #[inline(always)] - fn into_normalized(self) -> Result> { + pub(crate) fn into_normalized(self) -> Result> { use TypeInternal::*; match self.0 { Expr(e) => Ok(*e), @@ -110,6 +110,21 @@ impl Type { Untyped => Untyped, }) } + + #[inline(always)] + pub fn const_sort() -> Self { + Normalized(rc(ExprF::Const(Const::Sort)), UNTYPE).into_type() + } + #[inline(always)] + pub fn const_kind() -> Self { + Normalized(rc(ExprF::Const(Const::Kind)), Type::const_sort()) + .into_type() + } + #[inline(always)] + pub fn const_type() -> Self { + Normalized(rc(ExprF::Const(Const::Type)), Type::const_kind()) + .into_type() + } } const UNTYPE: Type = Type(TypeInternal::Untyped); diff --git a/dhall/tests/traits.rs b/dhall/tests/traits.rs index ac6b5e6..1e9b3c2 100644 --- a/dhall/tests/traits.rs +++ b/dhall/tests/traits.rs @@ -1,53 +1,71 @@ #![feature(proc_macro_hygiene)] -use dhall::StaticType; +use dhall::SimpleStaticType; +use dhall_core::{SubExpr, X}; 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)); +fn test_static_type() { + fn mktype(x: SubExpr) -> dhall::expr::SimpleType { + x.into() + } + + assert_eq!(bool::get_simple_static_type(), mktype(dhall_expr!(Bool))); + assert_eq!(String::get_simple_static_type(), mktype(dhall_expr!(Text))); assert_eq!( - <(bool, Option)>::get_type(), - dhall_expr!({ _1: Bool, _2: Optional Text }) + >::get_simple_static_type(), + mktype(dhall_expr!(Optional Bool)) + ); + assert_eq!( + <(bool, Option)>::get_simple_static_type(), + mktype(dhall_expr!({ _1: Bool, _2: Optional Text })) ); - #[derive(dhall::StaticType)] + #[derive(dhall::SimpleStaticType)] #[allow(dead_code)] struct A { field1: bool, field2: Option, } assert_eq!( - ::get_type(), - dhall_expr!({ field1: Bool, field2: Optional Bool }) + ::get_simple_static_type(), + mktype(dhall_expr!({ field1: Bool, field2: Optional Bool })) ); - #[derive(StaticType)] + #[derive(SimpleStaticType)] #[allow(dead_code)] struct B<'a, T: 'a> { field1: &'a T, field2: Option, } - assert_eq!(>::get_type(), A::get_type()); + assert_eq!( + >::get_simple_static_type(), + A::get_simple_static_type() + ); - #[derive(StaticType)] + #[derive(SimpleStaticType)] #[allow(dead_code)] struct C(T, Option); - assert_eq!(>::get_type(), <(bool, Option)>::get_type()); + assert_eq!( + >::get_simple_static_type(), + <(bool, Option)>::get_simple_static_type() + ); - #[derive(StaticType)] + #[derive(SimpleStaticType)] #[allow(dead_code)] struct D(); assert_eq!( - >::get_type(), - dhall_expr!({ _1: {}, _2: Optional Text }) + >::get_simple_static_type(), + mktype(dhall_expr!({ _1: {}, _2: Optional Text })) ); - #[derive(StaticType)] + #[derive(SimpleStaticType)] #[allow(dead_code)] enum E { A(T), B(String), }; - assert_eq!(>::get_type(), dhall_expr!(< A: Bool | B: Text >)); + assert_eq!( + >::get_simple_static_type(), + mktype(dhall_expr!(< A: Bool | B: Text >)) + ); } diff --git a/dhall_generator/src/derive.rs b/dhall_generator/src/derive.rs index 159ff5c..c911be5 100644 --- a/dhall_generator/src/derive.rs +++ b/dhall_generator/src/derive.rs @@ -6,14 +6,23 @@ 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) { +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(), }) } -pub fn derive_for_struct( +fn get_simple_static_type(ty: T) -> proc_macro2::TokenStream +where + T: quote::ToTokens, +{ + quote!( + <#ty as dhall::SimpleStaticType>::get_simple_static_type() + ) +} + +fn derive_for_struct( data: &syn::DataStruct, constraints: &mut Vec, ) -> Result { @@ -44,7 +53,8 @@ pub fn derive_for_struct( .map(|(name, ty)| { let name = dhall_core::Label::from(name); constraints.push(ty.clone()); - (name, quote!(<#ty as dhall::StaticType>::get_type())) + let ty = get_simple_static_type(ty); + (name, quote!(#ty.into())) }) .collect(); let record = @@ -52,7 +62,7 @@ pub fn derive_for_struct( Ok(quote! { dhall_core::rc(#record) }) } -pub fn derive_for_enum( +fn derive_for_enum( data: &syn::DataEnum, constraints: &mut Vec, ) -> Result { @@ -88,7 +98,8 @@ pub fn derive_for_enum( }; let ty = ty?; constraints.push(ty.clone()); - Ok((name, quote!(<#ty as dhall::StaticType>::get_type()))) + let ty = get_simple_static_type(ty); + Ok((name, quote!(#ty.into()))) }) .collect::>()?; @@ -97,7 +108,7 @@ pub fn derive_for_enum( Ok(quote! { dhall_core::rc(#union) }) } -pub fn derive_type_inner( +pub fn derive_simple_static_type_inner( input: TokenStream, ) -> Result { let input: DeriveInput = syn::parse_macro_input::parse(input)?; @@ -136,7 +147,7 @@ pub fn derive_type_inner( let mut local_where_clause = orig_where_clause.clone(); local_where_clause .predicates - .push(parse_quote!(#ty: dhall::StaticType)); + .push(parse_quote!(#ty: dhall::SimpleStaticType)); let phantoms = generics.params.iter().map(|param| match param { syn::GenericParam::Type(syn::TypeParam { ident, .. }) => { quote!(#ident) @@ -158,16 +169,16 @@ pub fn derive_type_inner( for ty in constraints.iter() { where_clause .predicates - .push(parse_quote!(#ty: dhall::StaticType)); + .push(parse_quote!(#ty: dhall::SimpleStaticType)); } let ident = &input.ident; let tokens = quote! { - impl #impl_generics dhall::StaticType for #ident #ty_generics + impl #impl_generics dhall::SimpleStaticType for #ident #ty_generics #where_clause { - fn get_type() -> dhall_core::DhallExpr { + fn get_simple_static_type() -> dhall::expr::SimpleType { #(#assertions)* - #get_type + dhall::expr::SimpleType::from(#get_type) } } }; diff --git a/dhall_generator/src/lib.rs b/dhall_generator/src/lib.rs index b422834..e67f2aa 100644 --- a/dhall_generator/src/lib.rs +++ b/dhall_generator/src/lib.rs @@ -21,7 +21,7 @@ pub fn subexpr(input: TokenStream) -> TokenStream { quote::subexpr(input) } -#[proc_macro_derive(StaticType)] -pub fn derive_type(input: TokenStream) -> TokenStream { - derive::derive_type(input) +#[proc_macro_derive(SimpleStaticType)] +pub fn derive_simple_static_type(input: TokenStream) -> TokenStream { + derive::derive_simple_static_type(input) } -- cgit v1.2.3 From 58f10a2a274fe858da6cc73d4a33718bfc46d52b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 19:17:34 +0200 Subject: s/load_from/parse/ --- dhall/src/imports.rs | 8 ++++---- dhall/src/main.rs | 2 +- dhall/src/tests.rs | 30 +++++++++++++++--------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/dhall/src/imports.rs b/dhall/src/imports.rs index fdde8c3..a65be4f 100644 --- a/dhall/src/imports.rs +++ b/dhall/src/imports.rs @@ -90,7 +90,7 @@ fn resolve_expr( } impl Parsed { - pub fn load_from_file(f: &Path) -> Result { + pub fn parse_file(f: &Path) -> Result { let mut buffer = String::new(); File::open(f)?.read_to_string(&mut buffer)?; let expr = parse_expr(&*buffer)?; @@ -98,13 +98,13 @@ impl Parsed { Ok(Parsed(expr, root)) } - pub fn load_from_str(s: &str) -> Result { + pub fn parse_str(s: &str) -> Result { let expr = parse_expr(s)?; let root = ImportRoot::LocalDir(std::env::current_dir()?); Ok(Parsed(expr, root)) } - pub fn load_from_binary_file(f: &Path) -> Result { + pub fn parse_binary_file(f: &Path) -> Result { let mut buffer = Vec::new(); File::open(f)?.read_to_end(&mut buffer)?; let expr = crate::binary::decode(&buffer)?; @@ -125,7 +125,7 @@ pub fn load_dhall_file( f: &Path, resolve_imports: bool, ) -> Result, ImportError> { - let expr = Parsed::load_from_file(f)?; + let expr = Parsed::parse_file(f)?; let expr = resolve_expr(expr, resolve_imports)?; Ok(expr.0.unroll()) } diff --git a/dhall/src/main.rs b/dhall/src/main.rs index 2881d5a..3b61a44 100644 --- a/dhall/src/main.rs +++ b/dhall/src/main.rs @@ -55,7 +55,7 @@ fn main() { let mut buffer = String::new(); io::stdin().read_to_string(&mut buffer).unwrap(); - let expr = match dhall::expr::Parsed::load_from_str(&buffer) { + let expr = match dhall::expr::Parsed::parse_str(&buffer) { Ok(expr) => expr, Err(e) => { print_error(&format!("Parse error {}", e), &buffer, 0, 0); diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs index ebb8855..737a8a4 100644 --- a/dhall/src/tests.rs +++ b/dhall/src/tests.rs @@ -51,16 +51,16 @@ fn read_dhall_file<'i>(file_path: &str) -> Result, ImportError> { crate::imports::load_dhall_file(&PathBuf::from(file_path), true) } -fn load_from_file_str<'i>( +fn parse_file_str<'i>( file_path: &str, ) -> Result { - crate::expr::Parsed::load_from_file(&PathBuf::from(file_path)) + crate::expr::Parsed::parse_file(&PathBuf::from(file_path)) } -fn load_from_binary_file_str<'i>( +fn parse_binary_file_str<'i>( file_path: &str, ) -> Result { - crate::expr::Parsed::load_from_binary_file(&PathBuf::from(file_path)) + crate::expr::Parsed::parse_binary_file(&PathBuf::from(file_path)) } pub fn run_test(base_path: &str, feature: Feature) { @@ -80,24 +80,24 @@ pub fn run_test(base_path: &str, feature: Feature) { ParserSuccess => { let expr_file_path = base_path.clone() + "A.dhall"; let expected_file_path = base_path + "B.dhallb"; - let expr = load_from_file_str(&expr_file_path) + let expr = parse_file_str(&expr_file_path) .map_err(|e| println!("{}", e)) .unwrap(); - let expected = load_from_binary_file_str(&expected_file_path) + let expected = parse_binary_file_str(&expected_file_path) .map_err(|e| println!("{}", e)) .unwrap(); assert_eq_pretty!(expr, expected); // Round-trip pretty-printer - let expr = - crate::expr::Parsed::load_from_str(&expr.to_string()).unwrap(); + let expr: crate::expr::Parsed = + crate::from_str(&expr.to_string(), None).unwrap(); assert_eq!(expr, expected); } ParserFailure => { let file_path = base_path + ".dhall"; - let err = load_from_file_str(&file_path).unwrap_err(); + let err = parse_file_str(&file_path).unwrap_err(); match err { ImportError::ParseError(_) => {} e => panic!("Expected parse error, got: {:?}", e), @@ -106,13 +106,13 @@ pub fn run_test(base_path: &str, feature: Feature) { Normalization => { let expr_file_path = base_path.clone() + "A.dhall"; let expected_file_path = base_path + "B.dhall"; - let expr = load_from_file_str(&expr_file_path) + let expr = parse_file_str(&expr_file_path) .unwrap() .resolve() .unwrap() .skip_typecheck() .normalize(); - let expected = load_from_file_str(&expected_file_path) + let expected = parse_file_str(&expected_file_path) .unwrap() .resolve() .unwrap() @@ -123,7 +123,7 @@ pub fn run_test(base_path: &str, feature: Feature) { } TypecheckFailure => { let file_path = base_path + ".dhall"; - load_from_file_str(&file_path) + parse_file_str(&file_path) .unwrap() .skip_resolve() .unwrap() @@ -149,7 +149,7 @@ pub fn run_test(base_path: &str, feature: Feature) { } TypeInferenceFailure => { let file_path = base_path + ".dhall"; - load_from_file_str(&file_path) + parse_file_str(&file_path) .unwrap() .skip_resolve() .unwrap() @@ -159,14 +159,14 @@ pub fn run_test(base_path: &str, feature: Feature) { TypeInferenceSuccess => { let expr_file_path = base_path.clone() + "A.dhall"; let expected_file_path = base_path + "B.dhall"; - let expr = load_from_file_str(&expr_file_path) + let expr = parse_file_str(&expr_file_path) .unwrap() .skip_resolve() .unwrap() .typecheck() .unwrap(); let ty = expr.get_type().as_normalized().unwrap(); - let expected = load_from_file_str(&expected_file_path) + let expected = parse_file_str(&expected_file_path) .unwrap() .skip_resolve() .unwrap() -- cgit v1.2.3 From ff12918696181f1b0f2b8272944044e27c89e319 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 19:18:25 +0200 Subject: Add a new Deserialize trait for reading dhall values --- dhall/src/lib.rs | 14 ++++++++++++++ dhall/src/traits.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 3bfc46f..b49d1c1 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -21,7 +21,21 @@ mod imports; mod normalize; mod traits; mod typecheck; +pub use crate::traits::Deserialize; pub use crate::traits::SimpleStaticType; pub use crate::traits::StaticType; pub use dhall_generator::SimpleStaticType; pub mod expr; + +pub fn from_str<'a, T: Deserialize<'a>>( + s: &'a str, + ty: Option<&crate::expr::Type>, +) -> crate::traits::Result { + T::from_str(s, ty) +} + +pub fn from_str_auto_type<'a, T: Deserialize<'a> + StaticType>( + s: &'a str, +) -> crate::traits::Result { + from_str(s, Some(&::get_static_type())) +} diff --git a/dhall/src/traits.rs b/dhall/src/traits.rs index 4925457..370632e 100644 --- a/dhall/src/traits.rs +++ b/dhall/src/traits.rs @@ -19,6 +19,56 @@ pub trait SimpleStaticType { fn get_simple_static_type() -> SimpleType; } +pub type Error = (); +pub type Result = std::result::Result; + +pub trait Deserialize<'a>: Sized { + fn from_str(s: &'a str, ty: Option<&Type>) -> Result; +} + +impl<'a> Deserialize<'a> for Parsed { + /// Simply parses the provided string. Ignores the + /// provided type. + fn from_str(s: &'a str, _ty: Option<&Type>) -> Result { + Ok(Parsed::parse_str(s).map_err(|_| ())?) + } +} + +impl<'a> Deserialize<'a> for Resolved { + /// Parses and resolves the provided string. Ignores the + /// provided type. + fn from_str(s: &'a str, ty: Option<&Type>) -> Result { + Ok(Parsed::from_str(s, ty)? + .resolve() + .map_err(|_| ())? + ) + } +} + +impl<'a> Deserialize<'a> for Typed { + /// Parses, resolves and typechecks the provided string. + fn from_str(s: &'a str, ty: Option<&Type>) -> Result { + // TODO: compare with provided type + Ok(Resolved::from_str(s, ty)? + .typecheck() + .map_err(|_| ())? + ) + } +} + +impl<'a> Deserialize<'a> for Normalized { + /// Parses, resolves, typechecks and normalizes the provided string. + fn from_str(s: &'a str, ty: Option<&Type>) -> Result { + Ok(Typed::from_str(s, ty)?.normalize()) + } +} + +impl<'a> Deserialize<'a> for Type { + fn from_str(s: &'a str, ty: Option<&Type>) -> Result { + Ok(Normalized::from_str(s, ty)?.into_type()) + } +} + fn mktype(x: SubExpr) -> SimpleType { SimpleType(x) } -- cgit v1.2.3 From 3473b504645a0e1986f32b93b9cae93d5659c861 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 19:25:13 +0200 Subject: Prepare for serde support --- Cargo.lock | 21 ++++++++++++++++++--- dhall/Cargo.toml | 1 + dhall/src/lib.rs | 1 + dhall/src/serde.rs | 9 +++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 dhall/src/serde.rs diff --git a/Cargo.lock b/Cargo.lock index e00159c..781c593 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,7 @@ dependencies = [ "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop-util 0.16.3 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_cbor 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "term-painter 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -282,8 +283,11 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde_cbor" @@ -292,7 +296,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.90" +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)", + "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]] @@ -436,8 +450,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" -"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560" +"checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4" "checksum serde_cbor 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45cd6d95391b16cd57e88b68be41d504183b7faae22030c0cc3b3f73dd57b2fd" +"checksum serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "58fc82bec244f168b23d1963b45c8bf5726e9a15a9d146a067f9081aeed2de79" "checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" "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" diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index e898cde..7aa8b4f 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -16,6 +16,7 @@ bytecount = "0.5.1" itertools = "0.8.0" lalrpop-util = "0.16.3" term-painter = "0.2.3" +serde = { version = "1.0", features = ["derive"] } serde_cbor = "0.9.0" dhall_core = { path = "../dhall_core" } dhall_generator = { path = "../dhall_generator" } diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index b49d1c1..2d44115 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -26,6 +26,7 @@ pub use crate::traits::SimpleStaticType; pub use crate::traits::StaticType; pub use dhall_generator::SimpleStaticType; pub mod expr; +pub mod serde; pub fn from_str<'a, T: Deserialize<'a>>( s: &'a str, diff --git a/dhall/src/serde.rs b/dhall/src/serde.rs new file mode 100644 index 0000000..278ea7a --- /dev/null +++ b/dhall/src/serde.rs @@ -0,0 +1,9 @@ +use crate::expr::Type; +use crate::traits::Deserialize; +use crate::traits::Result; + +impl<'a, T: serde::Deserialize<'a>> Deserialize<'a> for T { + fn from_str(_s: &'a str, _ty: Option<&Type>) -> Result { + unimplemented!() + } +} -- cgit v1.2.3 From 838f6c0a25d4024ee5f32ddde915fdd2f759018d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 19:25:55 +0200 Subject: Write main lib doc --- dhall/src/lib.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 2d44115..ababc5d 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -9,6 +9,112 @@ clippy::many_single_char_names )] +//! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive +//! alternative to JSON and YAML. +//! +//! You can think of Dhall as: JSON/YAML + types + imports + functions +//! +//! For a description of the dhall language, examples, tutorials, and more, see the [language +//! website][dhall]. +//! +//! This crate provides support for consuming dhall files the same way you would consume JSON or +//! YAML. It uses the [Serde][serde] serialization library to provide drop-in support for dhall +//! for any datatype that supports serde (and that's a lot of them !). +//! +//! This library is limited to deserializing (reading) dhall values; serializing (writing) +//! values to dhall is not supported for now. +//! +//! # Examples +//! +//! ### Custom datatype +//! +//! If you have a custom datatype for which you derived [serde::Deserialize], chances are +//! you will be able to derive [SimpleStaticType] for it as well. This gives you access to +//! a dhall representation of your datatype that can be outputted to users, and +//! allows easy type-safe deserializing. +//! +//! ```edition2018 +//! use serde::Deserialize; +//! use dhall::SimpleStaticType; +//! +//! #[derive(Debug, Deserialize, SimpleStaticType)] +//! struct Point { +//! x: usize, +//! y: usize, +//! } +//! +//! fn main() { +//! // Some dhall data +//! // ./Point can be populated using Point::get_type().to_string() +//! let data = "{ x = 1, y = 1 + 1 } : ./Point"; +//! +//! // Convert the dhall string to a Point. +//! let point: Point = +//! dhall::from_str_auto_type(&data) +//! .expect("An error ocurred !"); +//! +//! // Prints "point = Point { x: 1, y: 2 }" +//! println!("point = {:?}", point); +//! } +//! ``` +//! +//! ### Loosely typed +//! +//! If you used to consume JSON or YAML in a loosely typed way, you can continue to do so +//! with dhall. You only need to replace [serde_json::from_str] or [serde_yaml::from_str] +//! with [dhall::from_str][from_str]. +//! More generally, if the [SimpleStaticType] derive doesn't suit your needs, you can still +//! deserialize any valid dhall file that serde can handle. +//! +//! [serde_json::from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html +//! [serde_yaml::from_str]: https://docs.serde.rs/serde_yaml/fn.from_str.html +//! +//! ```edition2018 +//! use std::collections::BTreeMap; +//! +//! let mut map = BTreeMap::new(); +//! map.insert("x".to_string(), 1); +//! map.insert("y".to_string(), 2); +//! +//! // Some dhall data +//! let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; +//! +//! // Deserialize it to a Rust type. +//! let deserialized_map: BTreeMap = +//! dhall::from_str(&data, None) +//! .expect("Failed reading the data !"); +//! assert_eq!(map, deserialized_map); +//! ``` +//! +//! You can of course specify a dhall type that the input should match. +//! +//! ```edition2018 +//! use std::collections::BTreeMap; +//! +//! let mut map = BTreeMap::new(); +//! map.insert("x".to_string(), 1); +//! map.insert("y".to_string(), 2); +//! +//! // Some dhall data +//! let point_data = "{ x = 1, y = 1 + 1 }"; +//! let point_type_data = "{ x: Natural, y: Natural }"; +//! +//! // Construct a type +//! let point_type = +//! dhall::from_str(point_type_data, None) +//! .expect("Could not parse the Point type"); +//! +//! // Deserialize it to a Rust type. +//! let deserialized_map: BTreeMap = +//! dhall::from_str(&point_data, Some(&point_type)) +//! .expect("Failed reading the data !"); +//! assert_eq!(map, deserialized_map); +//! ``` +//! +//! [dhall]: https://dhall-lang.org/ +//! [serde]: https://docs.serde.rs/serde/ +//! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html + #[cfg(test)] #[macro_use] mod tests; -- cgit v1.2.3 From 730d99adf2d4a7f222a71d687ea942545a7038fd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 19:55:50 +0200 Subject: Add error submodule --- dhall/src/error.rs | 3 +++ dhall/src/lib.rs | 5 +++-- dhall/src/serde.rs | 2 +- dhall/src/traits.rs | 6 +----- 4 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 dhall/src/error.rs diff --git a/dhall/src/error.rs b/dhall/src/error.rs new file mode 100644 index 0000000..ef8dd34 --- /dev/null +++ b/dhall/src/error.rs @@ -0,0 +1,3 @@ +// TODO +pub type Error = (); +pub type Result = std::result::Result; diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index ababc5d..c218aeb 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -131,18 +131,19 @@ pub use crate::traits::Deserialize; pub use crate::traits::SimpleStaticType; pub use crate::traits::StaticType; pub use dhall_generator::SimpleStaticType; +pub mod error; pub mod expr; pub mod serde; pub fn from_str<'a, T: Deserialize<'a>>( s: &'a str, ty: Option<&crate::expr::Type>, -) -> crate::traits::Result { +) -> crate::error::Result { T::from_str(s, ty) } pub fn from_str_auto_type<'a, T: Deserialize<'a> + StaticType>( s: &'a str, -) -> crate::traits::Result { +) -> crate::error::Result { from_str(s, Some(&::get_static_type())) } diff --git a/dhall/src/serde.rs b/dhall/src/serde.rs index 278ea7a..4ac4f3d 100644 --- a/dhall/src/serde.rs +++ b/dhall/src/serde.rs @@ -1,6 +1,6 @@ +use crate::error::Result; use crate::expr::Type; use crate::traits::Deserialize; -use crate::traits::Result; impl<'a, T: serde::Deserialize<'a>> Deserialize<'a> for T { fn from_str(_s: &'a str, _ty: Option<&Type>) -> Result { diff --git a/dhall/src/traits.rs b/dhall/src/traits.rs index 370632e..328cbbc 100644 --- a/dhall/src/traits.rs +++ b/dhall/src/traits.rs @@ -1,11 +1,9 @@ +use crate::error::*; use crate::expr::*; use dhall_core::*; use dhall_generator::*; use std::borrow::Cow; -#[derive(Debug, Clone)] -pub enum ConversionError {} - pub trait StaticType { fn get_static_type() -> Type; } @@ -19,8 +17,6 @@ pub trait SimpleStaticType { fn get_simple_static_type() -> SimpleType; } -pub type Error = (); -pub type Result = std::result::Result; pub trait Deserialize<'a>: Sized { fn from_str(s: &'a str, ty: Option<&Type>) -> Result; -- cgit v1.2.3 From 56edb3a50fb4168ed76a3795f0ed774a754b6c32 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 20:02:56 +0200 Subject: Split traits module into submodules --- dhall/src/traits.rs | 154 ---------------------------------------- dhall/src/traits/deserialize.rs | 43 +++++++++++ dhall/src/traits/mod.rs | 4 ++ dhall/src/traits/static_type.rs | 104 +++++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 154 deletions(-) delete mode 100644 dhall/src/traits.rs create mode 100644 dhall/src/traits/deserialize.rs create mode 100644 dhall/src/traits/mod.rs create mode 100644 dhall/src/traits/static_type.rs diff --git a/dhall/src/traits.rs b/dhall/src/traits.rs deleted file mode 100644 index 328cbbc..0000000 --- a/dhall/src/traits.rs +++ /dev/null @@ -1,154 +0,0 @@ -use crate::error::*; -use crate::expr::*; -use dhall_core::*; -use dhall_generator::*; -use std::borrow::Cow; - -pub trait StaticType { - fn get_static_type() -> Type; -} - -/// Trait for rust types that can be represented in dhall in -/// a single way, independent of the value. A typical example is `Option`, -/// represented by the dhall expression `Optional Bool`. A typical counterexample -/// is `HashMap` because dhall cannot represent records with a -/// variable number of fields. -pub trait SimpleStaticType { - fn get_simple_static_type() -> SimpleType; -} - - -pub trait Deserialize<'a>: Sized { - fn from_str(s: &'a str, ty: Option<&Type>) -> Result; -} - -impl<'a> Deserialize<'a> for Parsed { - /// Simply parses the provided string. Ignores the - /// provided type. - fn from_str(s: &'a str, _ty: Option<&Type>) -> Result { - Ok(Parsed::parse_str(s).map_err(|_| ())?) - } -} - -impl<'a> Deserialize<'a> for Resolved { - /// Parses and resolves the provided string. Ignores the - /// provided type. - fn from_str(s: &'a str, ty: Option<&Type>) -> Result { - Ok(Parsed::from_str(s, ty)? - .resolve() - .map_err(|_| ())? - ) - } -} - -impl<'a> Deserialize<'a> for Typed { - /// Parses, resolves and typechecks the provided string. - fn from_str(s: &'a str, ty: Option<&Type>) -> Result { - // TODO: compare with provided type - Ok(Resolved::from_str(s, ty)? - .typecheck() - .map_err(|_| ())? - ) - } -} - -impl<'a> Deserialize<'a> for Normalized { - /// Parses, resolves, typechecks and normalizes the provided string. - fn from_str(s: &'a str, ty: Option<&Type>) -> Result { - Ok(Typed::from_str(s, ty)?.normalize()) - } -} - -impl<'a> Deserialize<'a> for Type { - fn from_str(s: &'a str, ty: Option<&Type>) -> Result { - Ok(Normalized::from_str(s, ty)?.into_type()) - } -} - -fn mktype(x: SubExpr) -> SimpleType { - SimpleType(x) -} - -impl StaticType for T { - fn get_static_type() -> Type { - crate::expr::Normalized( - T::get_simple_static_type().into(), - Type::const_type(), - ) - .into_type() - } -} - -impl StaticType for SimpleType { - fn get_static_type() -> Type { - Type::const_type() - } -} - -impl SimpleStaticType for bool { - fn get_simple_static_type() -> SimpleType { - mktype(dhall_expr!(Bool)) - } -} - -impl SimpleStaticType for Natural { - fn get_simple_static_type() -> SimpleType { - mktype(dhall_expr!(Natural)) - } -} - -impl SimpleStaticType for Integer { - fn get_simple_static_type() -> SimpleType { - mktype(dhall_expr!(Integer)) - } -} - -impl SimpleStaticType for String { - fn get_simple_static_type() -> SimpleType { - mktype(dhall_expr!(Text)) - } -} - -impl SimpleStaticType for (A, B) { - fn get_simple_static_type() -> SimpleType { - let ta: SubExpr<_, _> = A::get_simple_static_type().into(); - let tb: SubExpr<_, _> = B::get_simple_static_type().into(); - mktype(dhall_expr!({ _1: ta, _2: tb })) - } -} - -impl SimpleStaticType for Option { - fn get_simple_static_type() -> SimpleType { - let t: SubExpr<_, _> = T::get_simple_static_type().into(); - mktype(dhall_expr!(Optional t)) - } -} - -impl SimpleStaticType for Vec { - fn get_simple_static_type() -> SimpleType { - let t: SubExpr<_, _> = T::get_simple_static_type().into(); - mktype(dhall_expr!(List t)) - } -} - -impl<'a, T: SimpleStaticType> SimpleStaticType for &'a T { - fn get_simple_static_type() -> SimpleType { - T::get_simple_static_type() - } -} - -impl SimpleStaticType for std::marker::PhantomData { - fn get_simple_static_type() -> SimpleType { - mktype(dhall_expr!({})) - } -} - -impl SimpleStaticType - for std::result::Result -{ - fn get_simple_static_type() -> SimpleType { - let tt: SubExpr<_, _> = T::get_simple_static_type().into(); - let te: SubExpr<_, _> = E::get_simple_static_type().into(); - mktype(dhall_expr!(< Ok: tt | Err: te>)) - } -} diff --git a/dhall/src/traits/deserialize.rs b/dhall/src/traits/deserialize.rs new file mode 100644 index 0000000..8d1f565 --- /dev/null +++ b/dhall/src/traits/deserialize.rs @@ -0,0 +1,43 @@ +use crate::error::*; +use crate::expr::*; + +pub trait Deserialize<'a>: Sized { + fn from_str(s: &'a str, ty: Option<&Type>) -> Result; +} + +impl<'a> Deserialize<'a> for Parsed { + /// Simply parses the provided string. Ignores the + /// provided type. + fn from_str(s: &'a str, _ty: Option<&Type>) -> Result { + Ok(Parsed::parse_str(s).map_err(|_| ())?) + } +} + +impl<'a> Deserialize<'a> for Resolved { + /// Parses and resolves the provided string. Ignores the + /// provided type. + fn from_str(s: &'a str, ty: Option<&Type>) -> Result { + Ok(Parsed::from_str(s, ty)?.resolve().map_err(|_| ())?) + } +} + +impl<'a> Deserialize<'a> for Typed { + /// Parses, resolves and typechecks the provided string. + fn from_str(s: &'a str, ty: Option<&Type>) -> Result { + // TODO: compare with provided type + Ok(Resolved::from_str(s, ty)?.typecheck().map_err(|_| ())?) + } +} + +impl<'a> Deserialize<'a> for Normalized { + /// Parses, resolves, typechecks and normalizes the provided string. + fn from_str(s: &'a str, ty: Option<&Type>) -> Result { + Ok(Typed::from_str(s, ty)?.normalize()) + } +} + +impl<'a> Deserialize<'a> for Type { + fn from_str(s: &'a str, ty: Option<&Type>) -> Result { + Ok(Normalized::from_str(s, ty)?.into_type()) + } +} diff --git a/dhall/src/traits/mod.rs b/dhall/src/traits/mod.rs new file mode 100644 index 0000000..4ce8f97 --- /dev/null +++ b/dhall/src/traits/mod.rs @@ -0,0 +1,4 @@ +mod deserialize; +mod static_type; +pub use deserialize::Deserialize; +pub use static_type::{SimpleStaticType, StaticType}; diff --git a/dhall/src/traits/static_type.rs b/dhall/src/traits/static_type.rs new file mode 100644 index 0000000..b402ca9 --- /dev/null +++ b/dhall/src/traits/static_type.rs @@ -0,0 +1,104 @@ +use crate::expr::*; +use dhall_core::*; +use dhall_generator::*; + +pub trait StaticType { + fn get_static_type() -> Type; +} + +/// Trait for rust types that can be represented in dhall in +/// a single way, independent of the value. A typical example is `Option`, +/// represented by the dhall expression `Optional Bool`. A typical counterexample +/// is `HashMap` because dhall cannot represent records with a +/// variable number of fields. +pub trait SimpleStaticType { + fn get_simple_static_type() -> SimpleType; +} + +fn mktype(x: SubExpr) -> SimpleType { + SimpleType(x) +} + +impl StaticType for T { + fn get_static_type() -> Type { + crate::expr::Normalized( + T::get_simple_static_type().into(), + Type::const_type(), + ) + .into_type() + } +} + +impl StaticType for SimpleType { + fn get_static_type() -> Type { + Type::const_type() + } +} + +impl SimpleStaticType for bool { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Bool)) + } +} + +impl SimpleStaticType for Natural { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Natural)) + } +} + +impl SimpleStaticType for Integer { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Integer)) + } +} + +impl SimpleStaticType for String { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Text)) + } +} + +impl SimpleStaticType for (A, B) { + fn get_simple_static_type() -> SimpleType { + let ta: SubExpr<_, _> = A::get_simple_static_type().into(); + let tb: SubExpr<_, _> = B::get_simple_static_type().into(); + mktype(dhall_expr!({ _1: ta, _2: tb })) + } +} + +impl SimpleStaticType for Option { + fn get_simple_static_type() -> SimpleType { + let t: SubExpr<_, _> = T::get_simple_static_type().into(); + mktype(dhall_expr!(Optional t)) + } +} + +impl SimpleStaticType for Vec { + fn get_simple_static_type() -> SimpleType { + let t: SubExpr<_, _> = T::get_simple_static_type().into(); + mktype(dhall_expr!(List t)) + } +} + +impl<'a, T: SimpleStaticType> SimpleStaticType for &'a T { + fn get_simple_static_type() -> SimpleType { + T::get_simple_static_type() + } +} + +impl SimpleStaticType for std::marker::PhantomData { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!({})) + } +} + +impl SimpleStaticType + for std::result::Result +{ + fn get_simple_static_type() -> SimpleType { + let tt: SubExpr<_, _> = T::get_simple_static_type().into(); + let te: SubExpr<_, _> = E::get_simple_static_type().into(); + mktype(dhall_expr!(< Ok: tt | Err: te>)) + } +} -- cgit v1.2.3 From c7184b841279a55bdfb39bde429896d221aa666c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 21:46:25 +0200 Subject: Cleanup error handling Closes #41 --- Cargo.lock | 7 ++++++ dhall/Cargo.toml | 1 + dhall/src/error.rs | 25 +++++++++++++++++-- dhall/src/imports.rs | 54 ++++++++++++----------------------------- dhall/src/tests.rs | 21 +++++++--------- dhall/src/traits/deserialize.rs | 6 ++--- 6 files changed, 58 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 781c593..e0a55ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,7 @@ dependencies = [ "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop-util 0.16.3 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_cbor 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "term-painter 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -265,6 +266,11 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quick-error" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "quote" version = "0.6.11" @@ -448,6 +454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pretty 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f60c0d9f6fc88ecdd245d90c1920ff76a430ab34303fc778d33b1d0a4c3bf6d3" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4" diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index 7aa8b4f..a7c21dd 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -16,6 +16,7 @@ bytecount = "0.5.1" itertools = "0.8.0" lalrpop-util = "0.16.3" term-painter = "0.2.3" +quick-error = "1.2.2" serde = { version = "1.0", features = ["derive"] } serde_cbor = "0.9.0" dhall_core = { path = "../dhall_core" } diff --git a/dhall/src/error.rs b/dhall/src/error.rs index ef8dd34..eba7ff5 100644 --- a/dhall/src/error.rs +++ b/dhall/src/error.rs @@ -1,3 +1,24 @@ -// TODO -pub type Error = (); +use quick_error::quick_error; + pub type Result = std::result::Result; + +quick_error! { + #[derive(Debug)] + pub enum Error { + IO(err: std::io::Error) { + from() + } + Parse(err: dhall_core::ParseError) { + from() + } + Decode(err: crate::binary::DecodeError) { + from() + } + Resolve(err: crate::imports::ImportError) { + from() + } + Typecheck(err: crate::typecheck::TypeError) { + from() + } + } +} diff --git a/dhall/src/imports.rs b/dhall/src/imports.rs index a65be4f..3c33b4e 100644 --- a/dhall/src/imports.rs +++ b/dhall/src/imports.rs @@ -1,46 +1,20 @@ // use dhall_core::{Expr, FilePrefix, Import, ImportLocation, ImportMode, X}; use dhall_core::{Expr, Import, X}; // use std::path::Path; -use crate::binary::DecodeError; +use crate::error::Error; use crate::expr::*; use dhall_core::*; -use std::fmt; +use quick_error::quick_error; use std::fs::File; use std::io::Read; use std::path::Path; use std::path::PathBuf; -#[derive(Debug)] -pub enum ImportError { - ParseError(ParseError), - BinaryDecodeError(DecodeError), - IOError(std::io::Error), - UnexpectedImportError(Import), -} -impl From for ImportError { - fn from(e: ParseError) -> Self { - ImportError::ParseError(e) - } -} -impl From for ImportError { - fn from(e: DecodeError) -> Self { - ImportError::BinaryDecodeError(e) - } -} -impl From for ImportError { - fn from(e: std::io::Error) -> Self { - ImportError::IOError(e) - } -} -impl fmt::Display for ImportError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - use self::ImportError::*; - match self { - ParseError(e) => e.fmt(f), - BinaryDecodeError(_) => unimplemented!(), - IOError(e) => e.fmt(f), - UnexpectedImportError(e) => e.fmt(f), - } +quick_error! { + #[derive(Debug)] + pub enum ImportError { + Recursive(import: Import, err: Box) {} + UnexpectedImport(import: Import) {} } } @@ -67,7 +41,9 @@ fn resolve_import( Here => cwd.join(path), _ => unimplemented!("{:?}", import), }; - load_dhall_file(&path, true) + Ok(load_dhall_file(&path, true).map_err(|e| { + ImportError::Recursive(import.clone(), Box::new(e)) + })?) } _ => unimplemented!("{:?}", import), } @@ -82,7 +58,7 @@ fn resolve_expr( let expr = resolve_import(import, &root)?; Ok(expr.roll()) } else { - Err(ImportError::UnexpectedImportError(import.clone())) + Err(ImportError::UnexpectedImport(import.clone())) } }; let expr = expr.as_ref().traverse_embed(&resolve)?; @@ -90,7 +66,7 @@ fn resolve_expr( } impl Parsed { - pub fn parse_file(f: &Path) -> Result { + pub fn parse_file(f: &Path) -> Result { let mut buffer = String::new(); File::open(f)?.read_to_string(&mut buffer)?; let expr = parse_expr(&*buffer)?; @@ -98,13 +74,13 @@ impl Parsed { Ok(Parsed(expr, root)) } - pub fn parse_str(s: &str) -> Result { + pub fn parse_str(s: &str) -> Result { let expr = parse_expr(s)?; let root = ImportRoot::LocalDir(std::env::current_dir()?); Ok(Parsed(expr, root)) } - pub fn parse_binary_file(f: &Path) -> Result { + pub fn parse_binary_file(f: &Path) -> Result { let mut buffer = Vec::new(); File::open(f)?.read_to_end(&mut buffer)?; let expr = crate::binary::decode(&buffer)?; @@ -124,7 +100,7 @@ impl Parsed { pub fn load_dhall_file( f: &Path, resolve_imports: bool, -) -> Result, ImportError> { +) -> Result, Error> { let expr = Parsed::parse_file(f)?; let expr = resolve_expr(expr, resolve_imports)?; Ok(expr.0.unroll()) diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs index 737a8a4..9e78c2f 100644 --- a/dhall/src/tests.rs +++ b/dhall/src/tests.rs @@ -29,7 +29,8 @@ macro_rules! make_spec_test { }; } -use crate::imports::ImportError; +use crate::error::{Error, Result}; +use crate::expr::Parsed; use crate::*; use dhall_core::*; use dhall_generator as dhall; @@ -47,20 +48,16 @@ pub enum Feature { } // Deprecated -fn read_dhall_file<'i>(file_path: &str) -> Result, ImportError> { +fn read_dhall_file<'i>(file_path: &str) -> Result> { crate::imports::load_dhall_file(&PathBuf::from(file_path), true) } -fn parse_file_str<'i>( - file_path: &str, -) -> Result { - crate::expr::Parsed::parse_file(&PathBuf::from(file_path)) +fn parse_file_str<'i>(file_path: &str) -> Result { + Parsed::parse_file(&PathBuf::from(file_path)) } -fn parse_binary_file_str<'i>( - file_path: &str, -) -> Result { - crate::expr::Parsed::parse_binary_file(&PathBuf::from(file_path)) +fn parse_binary_file_str<'i>(file_path: &str) -> Result { + Parsed::parse_binary_file(&PathBuf::from(file_path)) } pub fn run_test(base_path: &str, feature: Feature) { @@ -91,7 +88,7 @@ pub fn run_test(base_path: &str, feature: Feature) { assert_eq_pretty!(expr, expected); // Round-trip pretty-printer - let expr: crate::expr::Parsed = + let expr: Parsed = crate::from_str(&expr.to_string(), None).unwrap(); assert_eq!(expr, expected); } @@ -99,7 +96,7 @@ pub fn run_test(base_path: &str, feature: Feature) { let file_path = base_path + ".dhall"; let err = parse_file_str(&file_path).unwrap_err(); match err { - ImportError::ParseError(_) => {} + Error::Parse(_) => {} e => panic!("Expected parse error, got: {:?}", e), } } diff --git a/dhall/src/traits/deserialize.rs b/dhall/src/traits/deserialize.rs index 8d1f565..ad4cde6 100644 --- a/dhall/src/traits/deserialize.rs +++ b/dhall/src/traits/deserialize.rs @@ -9,7 +9,7 @@ impl<'a> Deserialize<'a> for Parsed { /// Simply parses the provided string. Ignores the /// provided type. fn from_str(s: &'a str, _ty: Option<&Type>) -> Result { - Ok(Parsed::parse_str(s).map_err(|_| ())?) + Ok(Parsed::parse_str(s)?) } } @@ -17,7 +17,7 @@ impl<'a> Deserialize<'a> for Resolved { /// Parses and resolves the provided string. Ignores the /// provided type. fn from_str(s: &'a str, ty: Option<&Type>) -> Result { - Ok(Parsed::from_str(s, ty)?.resolve().map_err(|_| ())?) + Ok(Parsed::from_str(s, ty)?.resolve()?) } } @@ -25,7 +25,7 @@ impl<'a> Deserialize<'a> for Typed { /// Parses, resolves and typechecks the provided string. fn from_str(s: &'a str, ty: Option<&Type>) -> Result { // TODO: compare with provided type - Ok(Resolved::from_str(s, ty)?.typecheck().map_err(|_| ())?) + Ok(Resolved::from_str(s, ty)?.typecheck()?) } } -- cgit v1.2.3 From 88ac184e7e967c4a5e257c91598f647b8294d71c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 22:17:13 +0200 Subject: Mild cleanup imports --- dhall/src/imports.rs | 26 ++++++++++++-------------- dhall/src/tests.rs | 8 ++++---- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/dhall/src/imports.rs b/dhall/src/imports.rs index 3c33b4e..cedc072 100644 --- a/dhall/src/imports.rs +++ b/dhall/src/imports.rs @@ -1,6 +1,3 @@ -// use dhall_core::{Expr, FilePrefix, Import, ImportLocation, ImportMode, X}; -use dhall_core::{Expr, Import, X}; -// use std::path::Path; use crate::error::Error; use crate::expr::*; use dhall_core::*; @@ -27,7 +24,7 @@ pub enum ImportRoot { fn resolve_import( import: &Import, root: &ImportRoot, -) -> Result, ImportError> { +) -> Result { use self::ImportRoot::*; use dhall_core::FilePrefix::*; use dhall_core::ImportLocation::*; @@ -37,11 +34,12 @@ fn resolve_import( match &import.location_hashed.location { Local(prefix, path) => { let path = match prefix { + // TODO: fail gracefully Parent => cwd.parent().unwrap().join(path), Here => cwd.join(path), _ => unimplemented!("{:?}", import), }; - Ok(load_dhall_file(&path, true).map_err(|e| { + Ok(load_import(&path).map_err(|e| { ImportError::Recursive(import.clone(), Box::new(e)) })?) } @@ -49,6 +47,10 @@ fn resolve_import( } } +fn load_import(f: &Path) -> Result { + Ok(Parsed::parse_file(f)?.resolve()?) +} + fn resolve_expr( Parsed(expr, root): Parsed, allow_imports: bool, @@ -56,7 +58,7 @@ fn resolve_expr( let resolve = |import: &Import| -> Result, ImportError> { if allow_imports { let expr = resolve_import(import, &root)?; - Ok(expr.roll()) + Ok(expr.0) } else { Err(ImportError::UnexpectedImport(import.clone())) } @@ -96,12 +98,8 @@ impl Parsed { } } -// Deprecated -pub fn load_dhall_file( - f: &Path, - resolve_imports: bool, -) -> Result, Error> { - let expr = Parsed::parse_file(f)?; - let expr = resolve_expr(expr, resolve_imports)?; - Ok(expr.0.unroll()) +// Deprecated, used for tests only +#[allow(dead_code)] +pub fn load_dhall_file(f: &Path) -> Result, Error> { + Ok(load_import(f)?.0) } diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs index 9e78c2f..dede639 100644 --- a/dhall/src/tests.rs +++ b/dhall/src/tests.rs @@ -48,8 +48,8 @@ pub enum Feature { } // Deprecated -fn read_dhall_file<'i>(file_path: &str) -> Result> { - crate::imports::load_dhall_file(&PathBuf::from(file_path), true) +fn read_dhall_file<'i>(file_path: &str) -> Result> { + crate::imports::load_dhall_file(&PathBuf::from(file_path)) } fn parse_file_str<'i>(file_path: &str) -> Result { @@ -134,9 +134,9 @@ pub fn run_test(base_path: &str, feature: Feature) { .spawn(|| { let expr_file_path = base_path.clone() + "A.dhall"; let expected_file_path = base_path + "B.dhall"; - let expr = rc(read_dhall_file(&expr_file_path).unwrap()); + let expr = read_dhall_file(&expr_file_path).unwrap(); let expected = - rc(read_dhall_file(&expected_file_path).unwrap()); + read_dhall_file(&expected_file_path).unwrap(); typecheck::type_of(dhall::subexpr!(expr: expected)) .unwrap(); }) -- cgit v1.2.3 From 7740ec004c6d7e073358bf2be00b6c0006e4dd06 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 22:32:07 +0200 Subject: Allow providing type for typechecking in API --- dhall/src/imports.rs | 6 ------ dhall/src/tests.rs | 23 +++++++++++------------ dhall/src/traits/deserialize.rs | 7 +++++-- dhall/src/typecheck.rs | 5 +++++ 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/dhall/src/imports.rs b/dhall/src/imports.rs index cedc072..7810c55 100644 --- a/dhall/src/imports.rs +++ b/dhall/src/imports.rs @@ -97,9 +97,3 @@ impl Parsed { crate::imports::resolve_expr(self, false) } } - -// Deprecated, used for tests only -#[allow(dead_code)] -pub fn load_dhall_file(f: &Path) -> Result, Error> { - Ok(load_import(f)?.0) -} diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs index dede639..6b1c426 100644 --- a/dhall/src/tests.rs +++ b/dhall/src/tests.rs @@ -31,9 +31,6 @@ macro_rules! make_spec_test { use crate::error::{Error, Result}; use crate::expr::Parsed; -use crate::*; -use dhall_core::*; -use dhall_generator as dhall; use std::path::PathBuf; #[allow(dead_code)] @@ -47,11 +44,6 @@ pub enum Feature { TypeInferenceFailure, } -// Deprecated -fn read_dhall_file<'i>(file_path: &str) -> Result> { - crate::imports::load_dhall_file(&PathBuf::from(file_path)) -} - fn parse_file_str<'i>(file_path: &str) -> Result { Parsed::parse_file(&PathBuf::from(file_path)) } @@ -134,11 +126,18 @@ pub fn run_test(base_path: &str, feature: Feature) { .spawn(|| { let expr_file_path = base_path.clone() + "A.dhall"; let expected_file_path = base_path + "B.dhall"; - let expr = read_dhall_file(&expr_file_path).unwrap(); - let expected = - read_dhall_file(&expected_file_path).unwrap(); - typecheck::type_of(dhall::subexpr!(expr: expected)) + let expr = parse_file_str(&expr_file_path) + .unwrap() + .resolve() .unwrap(); + let expected = parse_file_str(&expected_file_path) + .unwrap() + .resolve() + .unwrap() + .skip_typecheck() + .skip_normalize() + .into_type(); + expr.typecheck_with(&expected).unwrap(); }) .unwrap() .join() diff --git a/dhall/src/traits/deserialize.rs b/dhall/src/traits/deserialize.rs index ad4cde6..5271a65 100644 --- a/dhall/src/traits/deserialize.rs +++ b/dhall/src/traits/deserialize.rs @@ -24,8 +24,11 @@ impl<'a> Deserialize<'a> for Resolved { impl<'a> Deserialize<'a> for Typed { /// Parses, resolves and typechecks the provided string. fn from_str(s: &'a str, ty: Option<&Type>) -> Result { - // TODO: compare with provided type - Ok(Resolved::from_str(s, ty)?.typecheck()?) + let resolved = Resolved::from_str(s, ty)?; + match ty { + None => Ok(resolved.typecheck()?), + Some(t) => Ok(resolved.typecheck_with(t)?), + } } } diff --git a/dhall/src/typecheck.rs b/dhall/src/typecheck.rs index f51f1b6..9741e65 100644 --- a/dhall/src/typecheck.rs +++ b/dhall/src/typecheck.rs @@ -13,6 +13,11 @@ impl Resolved { pub fn typecheck(self) -> Result> { type_of(self.0.clone()) } + pub fn typecheck_with(self, ty: &Type) -> Result> { + let expr: SubExpr<_, _> = self.0.clone(); + let ty: SubExpr<_, _> = ty.as_normalized()?.as_expr().clone(); + type_of(dhall::subexpr!(expr: ty)) + } /// Pretends this expression has been typechecked. Use with care. pub fn skip_typecheck(self) -> Typed { Typed(self.0, UNTYPE) -- cgit v1.2.3 From 7446472833c86fba612fffa436e93514f773f305 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 23:43:14 +0200 Subject: Display error messages --- dhall/src/error.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dhall/src/error.rs b/dhall/src/error.rs index eba7ff5..9e64b7e 100644 --- a/dhall/src/error.rs +++ b/dhall/src/error.rs @@ -7,18 +7,23 @@ quick_error! { pub enum Error { IO(err: std::io::Error) { from() + display("{}", err) } Parse(err: dhall_core::ParseError) { from() + display("{}", err) } Decode(err: crate::binary::DecodeError) { from() + display("{:?}", err) } Resolve(err: crate::imports::ImportError) { from() + display("{}", err) } Typecheck(err: crate::typecheck::TypeError) { from() + display("{:?}", err) } } } -- cgit v1.2.3 From 9a060908aae564e7155259a4e12d63be3fb97d9b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Apr 2019 23:58:12 +0200 Subject: Simplify test harness --- dhall/build.rs | 4 +- dhall/src/normalize.rs | 2 +- dhall/src/tests.rs | 206 ++++++++++++++++++++++--------------------------- dhall/src/typecheck.rs | 8 +- 4 files changed, 99 insertions(+), 121 deletions(-) diff --git a/dhall/build.rs b/dhall/build.rs index 878493d..c258315 100644 --- a/dhall/build.rs +++ b/dhall/build.rs @@ -41,7 +41,7 @@ fn main() -> std::io::Result<()> { } writeln!( file, - r#"make_spec_test!(ParserSuccess, success_{}, "{}");"#, + r#"make_spec_test!(Parser, Success, success_{}, "{}");"#, name, path )?; } @@ -50,7 +50,7 @@ fn main() -> std::io::Result<()> { let name = path.replace("/", "_"); writeln!( file, - r#"make_spec_test!(ParserFailure, failure_{}, "{}");"#, + r#"make_spec_test!(Parser, Failure, failure_{}, "{}");"#, name, path )?; } diff --git a/dhall/src/normalize.rs b/dhall/src/normalize.rs index d7f1848..608e6ee 100644 --- a/dhall/src/normalize.rs +++ b/dhall/src/normalize.rs @@ -337,7 +337,7 @@ mod spec_tests { macro_rules! norm { ($name:ident, $path:expr) => { - make_spec_test!(Normalization, $name, $path); + make_spec_test!(Normalization, Success, $name, $path); }; } diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs index 6b1c426..23ec1f4 100644 --- a/dhall/src/tests.rs +++ b/dhall/src/tests.rs @@ -19,12 +19,22 @@ right: `{}`"#, #[macro_export] macro_rules! make_spec_test { - ($type:ident, $name:ident, $path:expr) => { + ($type:ident, $status:ident, $name:ident, $path:expr) => { #[test] #[allow(non_snake_case)] fn $name() { use crate::tests::*; - run_test($path, Feature::$type); + // Many tests stack overflow in debug mode + std::thread::Builder::new() + .stack_size(4 * 1024 * 1024) + .spawn(|| { + run_test($path, Feature::$type, Status::$status) + .map_err(|e| println!("{}", e)) + .unwrap(); + }) + .unwrap() + .join() + .unwrap(); } }; } @@ -33,15 +43,18 @@ use crate::error::{Error, Result}; use crate::expr::Parsed; use std::path::PathBuf; -#[allow(dead_code)] +#[derive(Copy, Clone)] pub enum Feature { - ParserSuccess, - ParserFailure, + Parser, Normalization, - TypecheckSuccess, - TypecheckFailure, - TypeInferenceSuccess, - TypeInferenceFailure, + Typecheck, + TypeInference, +} + +#[derive(Copy, Clone)] +pub enum Status { + Success, + Failure, } fn parse_file_str<'i>(file_path: &str) -> Result { @@ -52,123 +65,88 @@ fn parse_binary_file_str<'i>(file_path: &str) -> Result { Parsed::parse_binary_file(&PathBuf::from(file_path)) } -pub fn run_test(base_path: &str, feature: Feature) { +pub fn run_test( + base_path: &str, + feature: Feature, + status: Status, +) -> Result<()> { use self::Feature::*; - let base_path_prefix = match feature { - ParserSuccess => "parser/success/", - ParserFailure => "parser/failure/", - Normalization => "normalization/success/", - TypecheckSuccess => "typecheck/success/", - TypecheckFailure => "typecheck/failure/", - TypeInferenceSuccess => "type-inference/success/", - TypeInferenceFailure => "type-inference/failure/", + use self::Status::*; + let feature_prefix = match feature { + Parser => "parser/", + Normalization => "normalization/", + Typecheck => "typecheck/", + TypeInference => "type-inference/", + }; + let status_prefix = match status { + Success => "success/", + Failure => "failure/", }; - let base_path = - "../dhall-lang/tests/".to_owned() + base_path_prefix + base_path; - match feature { - ParserSuccess => { + let base_path = "../dhall-lang/tests/".to_owned() + + feature_prefix + + status_prefix + + base_path; + match status { + Success => { let expr_file_path = base_path.clone() + "A.dhall"; - let expected_file_path = base_path + "B.dhallb"; - let expr = parse_file_str(&expr_file_path) - .map_err(|e| println!("{}", e)) - .unwrap(); + let expr = parse_file_str(&expr_file_path)?; - let expected = parse_binary_file_str(&expected_file_path) - .map_err(|e| println!("{}", e)) - .unwrap(); + if let Parser = feature { + let expected_file_path = base_path + "B.dhallb"; + let expected = parse_binary_file_str(&expected_file_path)?; - assert_eq_pretty!(expr, expected); + assert_eq_pretty!(expr, expected); - // Round-trip pretty-printer - let expr: Parsed = - crate::from_str(&expr.to_string(), None).unwrap(); - assert_eq!(expr, expected); - } - ParserFailure => { - let file_path = base_path + ".dhall"; - let err = parse_file_str(&file_path).unwrap_err(); - match err { - Error::Parse(_) => {} - e => panic!("Expected parse error, got: {:?}", e), + // Round-trip pretty-printer + let expr: Parsed = crate::from_str(&expr.to_string(), None)?; + assert_eq!(expr, expected); + + return Ok(()); } - } - Normalization => { - let expr_file_path = base_path.clone() + "A.dhall"; + + let expr = expr.resolve()?; + let expected_file_path = base_path + "B.dhall"; - let expr = parse_file_str(&expr_file_path) - .unwrap() - .resolve() - .unwrap() + let expected = parse_file_str(&expected_file_path)? + .resolve()? .skip_typecheck() - .normalize(); - let expected = parse_file_str(&expected_file_path) - .unwrap() - .resolve() - .unwrap() - .skip_typecheck() - .normalize(); + .skip_normalize(); - assert_eq_display!(expr, expected); - } - TypecheckFailure => { - let file_path = base_path + ".dhall"; - parse_file_str(&file_path) - .unwrap() - .skip_resolve() - .unwrap() - .typecheck() - .unwrap_err(); - } - TypecheckSuccess => { - // Many tests stack overflow in debug mode - std::thread::Builder::new() - .stack_size(4 * 1024 * 1024) - .spawn(|| { - let expr_file_path = base_path.clone() + "A.dhall"; - let expected_file_path = base_path + "B.dhall"; - let expr = parse_file_str(&expr_file_path) - .unwrap() - .resolve() - .unwrap(); - let expected = parse_file_str(&expected_file_path) - .unwrap() - .resolve() - .unwrap() - .skip_typecheck() - .skip_normalize() - .into_type(); - expr.typecheck_with(&expected).unwrap(); - }) - .unwrap() - .join() - .unwrap(); + match feature { + Parser => unreachable!(), + Typecheck => { + expr.typecheck_with(&expected.into_type())?; + } + TypeInference => { + let expr = expr.typecheck()?; + let ty = expr.get_type().as_normalized()?; + assert_eq_display!(ty, &expected); + } + Normalization => { + let expr = expr.skip_typecheck().normalize(); + assert_eq_display!(expr, expected); + } + } } - TypeInferenceFailure => { + Failure => { let file_path = base_path + ".dhall"; - parse_file_str(&file_path) - .unwrap() - .skip_resolve() - .unwrap() - .typecheck() - .unwrap_err(); - } - TypeInferenceSuccess => { - let expr_file_path = base_path.clone() + "A.dhall"; - let expected_file_path = base_path + "B.dhall"; - let expr = parse_file_str(&expr_file_path) - .unwrap() - .skip_resolve() - .unwrap() - .typecheck() - .unwrap(); - let ty = expr.get_type().as_normalized().unwrap(); - let expected = parse_file_str(&expected_file_path) - .unwrap() - .skip_resolve() - .unwrap() - .skip_typecheck() - .skip_normalize(); - assert_eq_display!(ty, &expected); + match feature { + Parser => { + let err = parse_file_str(&file_path).unwrap_err(); + match err { + Error::Parse(_) => {} + e => panic!("Expected parse error, got: {:?}", e), + } + } + Normalization => unreachable!(), + Typecheck | TypeInference => { + parse_file_str(&file_path)? + .skip_resolve()? + .typecheck() + .unwrap_err(); + } + } } } + Ok(()) } diff --git a/dhall/src/typecheck.rs b/dhall/src/typecheck.rs index 9741e65..91846bc 100644 --- a/dhall/src/typecheck.rs +++ b/dhall/src/typecheck.rs @@ -726,23 +726,23 @@ mod spec_tests { macro_rules! tc_success { ($name:ident, $path:expr) => { - make_spec_test!(TypecheckSuccess, $name, $path); + make_spec_test!(Typecheck, Success, $name, $path); }; } // macro_rules! tc_failure { // ($name:ident, $path:expr) => { - // make_spec_test!(TypecheckFailure, $name, $path); + // make_spec_test!(Typecheck, Failure, $name, $path); // }; // } macro_rules! ti_success { ($name:ident, $path:expr) => { - make_spec_test!(TypeInferenceSuccess, $name, $path); + make_spec_test!(TypeInference, Success, $name, $path); }; } // macro_rules! ti_failure { // ($name:ident, $path:expr) => { - // make_spec_test!(TypeInferenceFailure, $name, $path); + // make_spec_test!(TypeInference, Failure, $name, $path); // }; // } -- cgit v1.2.3 From 82d62c4d7d423817a4fd9d6294d27d18d60bcd22 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 11 Apr 2019 13:11:52 +0200 Subject: Add basic deserialization support Closes #13 --- dhall/Cargo.toml | 3 -- dhall/src/error.rs | 3 ++ dhall/src/lib.rs | 7 ++--- dhall/src/serde.rs | 68 ++++++++++++++++++++++++++++++++++++++--- dhall/src/traits/deserialize.rs | 2 +- dhall/src/traits/static_type.rs | 24 +++++++++++++++ dhall_core/src/core.rs | 1 - dhall_core/src/label.rs | 3 ++ 8 files changed, 98 insertions(+), 13 deletions(-) diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index a7c21dd..7f695d4 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -5,9 +5,6 @@ authors = ["NanoTech ", "Nadrieril > Deserialize<'a> for T { - fn from_str(_s: &'a str, _ty: Option<&Type>) -> Result { - unimplemented!() + fn from_str(s: &'a str, ty: Option<&Type>) -> Result { + let expr = Normalized::from_str(s, ty)?; + T::deserialize(Deserializer(Cow::Owned(expr.0))) + } +} + +struct Deserializer<'a>(Cow<'a, SubExpr>); + +impl serde::de::Error for Error { + fn custom(msg: T) -> Self + where + T: std::fmt::Display, + { + Error::Deserialize(msg.to_string()) + } +} + +impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> { + type Deserializer = Deserializer<'a>; + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { + type Error = Error; + fn deserialize_any(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + use std::convert::TryInto; + use ExprF::*; + match self.0.as_ref().as_ref() { + NaturalLit(n) => match (*n).try_into() { + Ok(n64) => visitor.visit_u64(n64), + Err(_) => match (*n).try_into() { + Ok(n32) => visitor.visit_u32(n32), + Err(_) => unimplemented!(), + }, + }, + IntegerLit(n) => match (*n).try_into() { + Ok(n64) => visitor.visit_i64(n64), + Err(_) => match (*n).try_into() { + Ok(n32) => visitor.visit_i32(n32), + Err(_) => unimplemented!(), + }, + }, + RecordLit(m) => visitor.visit_map( + serde::de::value::MapDeserializer::new(m.iter().map( + |(k, v)| (k.as_ref(), Deserializer(Cow::Borrowed(v))), + )), + ), + _ => unimplemented!(), + } + } + + serde::forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct enum identifier ignored_any } } diff --git a/dhall/src/traits/deserialize.rs b/dhall/src/traits/deserialize.rs index 5271a65..1fbdfe1 100644 --- a/dhall/src/traits/deserialize.rs +++ b/dhall/src/traits/deserialize.rs @@ -8,7 +8,7 @@ pub trait Deserialize<'a>: Sized { impl<'a> Deserialize<'a> for Parsed { /// Simply parses the provided string. Ignores the /// provided type. - fn from_str(s: &'a str, _ty: Option<&Type>) -> Result { + fn from_str(s: &'a str, _: Option<&Type>) -> Result { Ok(Parsed::parse_str(s)?) } } diff --git a/dhall/src/traits/static_type.rs b/dhall/src/traits/static_type.rs index b402ca9..6c41e3f 100644 --- a/dhall/src/traits/static_type.rs +++ b/dhall/src/traits/static_type.rs @@ -47,12 +47,36 @@ impl SimpleStaticType for Natural { } } +impl SimpleStaticType for u32 { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Natural)) + } +} + +impl SimpleStaticType for u64 { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Natural)) + } +} + impl SimpleStaticType for Integer { fn get_simple_static_type() -> SimpleType { mktype(dhall_expr!(Integer)) } } +impl SimpleStaticType for i32 { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Integer)) + } +} + +impl SimpleStaticType for i64 { + fn get_simple_static_type() -> SimpleType { + mktype(dhall_expr!(Integer)) + } +} + impl SimpleStaticType for String { fn get_simple_static_type() -> SimpleType { mktype(dhall_expr!(Text)) diff --git a/dhall_core/src/core.rs b/dhall_core/src/core.rs index 39dea37..03a34f1 100644 --- a/dhall_core/src/core.rs +++ b/dhall_core/src/core.rs @@ -3,7 +3,6 @@ use crate::*; use std::collections::BTreeMap; use std::rc::Rc; -pub type Int = isize; pub type Integer = isize; pub type Natural = usize; pub type Double = NaiveDouble; diff --git a/dhall_core/src/label.rs b/dhall_core/src/label.rs index 9dc2816..43c3f53 100644 --- a/dhall_core/src/label.rs +++ b/dhall_core/src/label.rs @@ -28,4 +28,7 @@ impl Label { pub fn from_str(s: &str) -> Label { Label(s.into()) } + pub fn as_ref(&self) -> &str { + self.0.as_ref() + } } -- cgit v1.2.3