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 ++++++++-- 4 files changed, 111 insertions(+), 43 deletions(-) (limited to 'dhall/src') 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); -- 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(-) (limited to 'dhall/src') 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(+) (limited to 'dhall/src') 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 --- dhall/src/lib.rs | 1 + dhall/src/serde.rs | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 dhall/src/serde.rs (limited to 'dhall/src') 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(+) (limited to 'dhall/src') 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 (limited to 'dhall/src') 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 (limited to 'dhall/src') 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 --- dhall/src/error.rs | 25 +++++++++++++++++-- dhall/src/imports.rs | 54 ++++++++++++----------------------------- dhall/src/tests.rs | 21 +++++++--------- dhall/src/traits/deserialize.rs | 6 ++--- 4 files changed, 50 insertions(+), 56 deletions(-) (limited to 'dhall/src') 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(-) (limited to 'dhall/src') 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(-) (limited to 'dhall/src') 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(+) (limited to 'dhall/src') 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/src/normalize.rs | 2 +- dhall/src/tests.rs | 206 ++++++++++++++++++++++--------------------------- dhall/src/typecheck.rs | 8 +- 3 files changed, 97 insertions(+), 119 deletions(-) (limited to 'dhall/src') 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/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 +++++++++++++++ 5 files changed, 95 insertions(+), 9 deletions(-) (limited to 'dhall/src') diff --git a/dhall/src/error.rs b/dhall/src/error.rs index 9e64b7e..cfd6f09 100644 --- a/dhall/src/error.rs +++ b/dhall/src/error.rs @@ -25,5 +25,8 @@ quick_error! { from() display("{:?}", err) } + Deserialize(err: String) { + display("{}", err) + } } } diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index c218aeb..8af5af9 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -39,14 +39,13 @@ //! //! #[derive(Debug, Deserialize, SimpleStaticType)] //! struct Point { -//! x: usize, -//! y: usize, +//! x: u64, +//! y: u64, //! } //! //! fn main() { //! // Some dhall data -//! // ./Point can be populated using Point::get_type().to_string() -//! let data = "{ x = 1, y = 1 + 1 } : ./Point"; +//! let data = "{ x = 1, y = 1 + 1 }"; //! //! // Convert the dhall string to a Point. //! let point: Point = diff --git a/dhall/src/serde.rs b/dhall/src/serde.rs index 4ac4f3d..196bda1 100644 --- a/dhall/src/serde.rs +++ b/dhall/src/serde.rs @@ -1,9 +1,69 @@ -use crate::error::Result; -use crate::expr::Type; +use crate::error::{Error, Result}; +use crate::expr::{Normalized, Type}; use crate::traits::Deserialize; +use dhall_core::*; +use std::borrow::Cow; impl<'a, T: serde::Deserialize<'a>> 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)) -- cgit v1.2.3