From 52f9ecfc4dac65d305fd920e8c7f748889a0804f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 12 Aug 2019 23:24:48 +0200 Subject: Move api into its own crate --- Cargo.lock | 11 +- Cargo.toml | 1 + dhall/Cargo.toml | 1 - dhall/src/api/mod.rs | 159 ------------------------ dhall/src/api/serde.rs | 70 ----------- dhall/src/api/static_type.rs | 93 -------------- dhall/src/core/mod.rs | 8 +- dhall/src/error/mod.rs | 10 ++ dhall/src/lib.rs | 112 +---------------- dhall/tests/traits.rs | 68 ----------- dhall_proc_macros/src/derive.rs | 14 +-- serde_dhall/Cargo.toml | 12 ++ serde_dhall/src/lib.rs | 264 ++++++++++++++++++++++++++++++++++++++++ serde_dhall/src/serde.rs | 70 +++++++++++ serde_dhall/src/static_type.rs | 93 ++++++++++++++ serde_dhall/tests/traits.rs | 68 +++++++++++ 16 files changed, 541 insertions(+), 513 deletions(-) delete mode 100644 dhall/src/api/mod.rs delete mode 100644 dhall/src/api/serde.rs delete mode 100644 dhall/src/api/static_type.rs delete mode 100644 dhall/tests/traits.rs create mode 100644 serde_dhall/Cargo.toml create mode 100644 serde_dhall/src/lib.rs create mode 100644 serde_dhall/src/serde.rs create mode 100644 serde_dhall/src/static_type.rs create mode 100644 serde_dhall/tests/traits.rs diff --git a/Cargo.lock b/Cargo.lock index dca96f7..72b5423 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,7 +69,6 @@ name = "dhall" version = "0.1.0" dependencies = [ "bytecount 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dhall_proc_macros 0.1.0", "dhall_syntax 0.1.0", "improved_slice_patterns 2.0.0", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -315,6 +314,16 @@ dependencies = [ "syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "serde_dhall" +version = "0.1.0" +dependencies = [ + "dhall 0.1.0", + "dhall_proc_macros 0.1.0", + "dhall_syntax 0.1.0", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sha-1" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index f48e33a..1e40748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "dhall_syntax", "dhall_proc_macros", "improved_slice_patterns", + "serde_dhall" ] # # Parser is super slow when not optimized diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index d08627d..aa52d58 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -14,7 +14,6 @@ serde = { version = "1.0", features = ["derive"] } serde_cbor = "0.9.0" improved_slice_patterns = { version = "2.0.0", path = "../improved_slice_patterns" } dhall_syntax = { path = "../dhall_syntax" } -dhall_proc_macros = { path = "../dhall_proc_macros" } [dev-dependencies] pretty_assertions = "0.6.1" diff --git a/dhall/src/api/mod.rs b/dhall/src/api/mod.rs deleted file mode 100644 index 188b6c0..0000000 --- a/dhall/src/api/mod.rs +++ /dev/null @@ -1,159 +0,0 @@ -mod serde; -pub(crate) mod static_type; - -pub use value::Value; - -mod value { - use super::Type; - use crate::error::Result; - use crate::phase::{NormalizedSubExpr, Parsed, Typed}; - - // A Dhall value - pub struct Value(Typed); - - impl Value { - pub fn from_str(s: &str, ty: Option<&Type>) -> Result { - let resolved = Parsed::parse_str(s)?.resolve()?; - let typed = match ty { - None => resolved.typecheck()?, - Some(t) => resolved.typecheck_with(&t.to_type())?, - }; - Ok(Value(typed)) - } - pub(crate) fn to_expr(&self) -> NormalizedSubExpr { - self.0.to_expr() - } - pub(crate) fn to_typed(&self) -> Typed { - self.0.clone() - } - } -} - -pub use typ::Type; - -mod typ { - use dhall_syntax::Builtin; - - use crate::core::thunk::{Thunk, TypeThunk}; - use crate::core::value::Value; - use crate::error::Result; - use crate::phase::{NormalizedSubExpr, Typed}; - - /// A Dhall expression representing a type. - /// - /// This captures what is usually simply called a "type", like - /// `Bool`, `{ x: Integer }` or `Natural -> Text`. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Type(Typed); - - impl Type { - pub(crate) fn from_value(v: Value) -> Self { - Type(Typed::from_value_untyped(v)) - } - pub(crate) fn make_builtin_type(b: Builtin) -> Self { - Self::from_value(Value::from_builtin(b)) - } - pub(crate) fn make_optional_type(t: Type) -> Self { - Self::from_value(Value::AppliedBuiltin( - Builtin::Optional, - vec![t.to_thunk()], - )) - } - pub(crate) fn make_list_type(t: Type) -> Self { - Self::from_value(Value::AppliedBuiltin( - Builtin::List, - vec![t.to_thunk()], - )) - } - #[doc(hidden)] - pub fn make_record_type( - kts: impl Iterator, - ) -> Self { - Self::from_value(Value::RecordType( - kts.map(|(k, t)| { - (k.into(), TypeThunk::from_thunk(t.to_thunk())) - }) - .collect(), - )) - } - #[doc(hidden)] - pub fn make_union_type( - kts: impl Iterator)>, - ) -> Self { - Self::from_value(Value::UnionType( - kts.map(|(k, t)| { - (k.into(), t.map(|t| TypeThunk::from_thunk(t.to_thunk()))) - }) - .collect(), - )) - } - - pub(crate) fn to_thunk(&self) -> Thunk { - self.0.to_thunk() - } - #[allow(dead_code)] - pub(crate) fn to_expr(&self) -> NormalizedSubExpr { - self.0.to_expr() - } - pub(crate) fn to_type(&self) -> crate::phase::Type { - self.0.to_type() - } - } - - impl crate::de::Deserialize for Type { - fn from_dhall(v: &crate::api::Value) -> Result { - Ok(Type(v.to_typed())) - } - } -} - -/// Deserialization of Dhall expressions into Rust -pub mod de { - pub use super::static_type::StaticType; - pub use super::{Type, Value}; - use crate::error::Result; - #[doc(hidden)] - pub use dhall_proc_macros::StaticType; - - /// A data structure that can be deserialized from a Dhall expression - /// - /// This is automatically implemented for any type that [serde][serde] - /// can deserialize. - /// - /// This trait cannot be implemented manually. - // TODO: seal trait - pub trait Deserialize: Sized { - /// See [dhall::de::from_str][crate::de::from_str] - fn from_dhall(v: &Value) -> Result; - } - - /// Deserialize an instance of type T from a string of Dhall text. - /// - /// This will recursively resolve all imports in the expression, and - /// typecheck it before deserialization. Relative imports will be resolved relative to the - /// provided file. More control over this process is not yet available - /// but will be in a coming version of this crate. - /// - /// If a type is provided, this additionally checks that the provided - /// expression has that type. - pub fn from_str(s: &str, ty: Option<&Type>) -> Result - where - T: Deserialize, - { - T::from_dhall(&Value::from_str(s, ty)?) - } - - /// Deserialize an instance of type T from a string of Dhall text, - /// additionally checking that it matches the type of T. - /// - /// This will recursively resolve all imports in the expression, and - /// typecheck it before deserialization. Relative imports will be resolved relative to the - /// provided file. More control over this process is not yet available - /// but will be in a coming version of this crate. - pub fn from_str_auto_type(s: &str) -> Result - where - T: Deserialize + StaticType, - { - from_str(s, Some(&::static_type())) - } -} diff --git a/dhall/src/api/serde.rs b/dhall/src/api/serde.rs deleted file mode 100644 index e1c8eef..0000000 --- a/dhall/src/api/serde.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::api::de::{Deserialize, Value}; -use crate::error::{Error, Result}; -use dhall_syntax::{ExprF, SubExpr, X}; -use std::borrow::Cow; - -impl<'a, T> Deserialize for T -where - T: serde::Deserialize<'a>, -{ - fn from_dhall(v: &Value) -> Result { - T::deserialize(Deserializer(Cow::Owned(v.to_expr()))) - } -} - -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/api/static_type.rs b/dhall/src/api/static_type.rs deleted file mode 100644 index 906bcef..0000000 --- a/dhall/src/api/static_type.rs +++ /dev/null @@ -1,93 +0,0 @@ -use dhall_syntax::{Builtin, Integer, Natural}; - -use crate::api::Type; - -/// A Rust type that can be represented as a Dhall type. -/// -/// A typical example is `Option`, -/// represented by the dhall expression `Optional Bool`. -/// -/// This trait can and should be automatically derived. -/// -/// The representation needs to be independent of the value. -/// For this reason, something like `HashMap` cannot implement -/// [StaticType] because each different value would -/// have a different Dhall record type. -pub trait StaticType { - fn static_type() -> Type; -} - -macro_rules! derive_builtin { - ($ty:ty, $builtin:ident) => { - impl StaticType for $ty { - fn static_type() -> Type { - Type::make_builtin_type(Builtin::$builtin) - } - } - }; -} - -derive_builtin!(bool, Bool); -derive_builtin!(Natural, Natural); -derive_builtin!(u64, Natural); -derive_builtin!(Integer, Integer); -derive_builtin!(String, Text); - -impl StaticType for (A, B) -where - A: StaticType, - B: StaticType, -{ - fn static_type() -> Type { - Type::make_record_type( - vec![ - ("_1".to_owned(), A::static_type()), - ("_2".to_owned(), B::static_type()), - ] - .into_iter(), - ) - } -} - -impl StaticType for std::result::Result -where - T: StaticType, - E: StaticType, -{ - fn static_type() -> Type { - Type::make_union_type( - vec![ - ("Ok".to_owned(), Some(T::static_type())), - ("Err".to_owned(), Some(E::static_type())), - ] - .into_iter(), - ) - } -} - -impl StaticType for Option -where - T: StaticType, -{ - fn static_type() -> Type { - Type::make_optional_type(T::static_type()) - } -} - -impl StaticType for Vec -where - T: StaticType, -{ - fn static_type() -> Type { - Type::make_list_type(T::static_type()) - } -} - -impl<'a, T> StaticType for &'a T -where - T: StaticType, -{ - fn static_type() -> Type { - T::static_type() - } -} diff --git a/dhall/src/core/mod.rs b/dhall/src/core/mod.rs index a202e72..0667df8 100644 --- a/dhall/src/core/mod.rs +++ b/dhall/src/core/mod.rs @@ -1,4 +1,4 @@ -pub(crate) mod context; -pub(crate) mod thunk; -pub(crate) mod value; -pub(crate) mod var; +pub mod context; +pub mod thunk; +pub mod value; +pub mod var; diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs index 3c00017..ecf3510 100644 --- a/dhall/src/error/mod.rs +++ b/dhall/src/error/mod.rs @@ -192,3 +192,13 @@ impl From for Error { Error::Typecheck(err) } } + +impl serde::de::Error for Error { + fn custom(msg: T) -> Self + where + T: std::fmt::Display, + { + Error::Deserialize(msg.to_string()) + } +} + diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index ea29869..c360323 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -17,118 +17,10 @@ clippy::ptr_arg )] -//! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive -//! alternative to JSON and YAML. -//! -//! You can think of Dhall as: JSON + 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 [StaticType][de::StaticType] 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::de::StaticType; -//! -//! #[derive(Debug, Deserialize, StaticType)] -//! struct Point { -//! x: u64, -//! y: u64, -//! } -//! -//! fn main() { -//! // Some dhall data -//! let data = "{ x = 1, y = 1 + 1 }"; -//! -//! // Convert the dhall string to a Point. -//! let point: Point = -//! dhall::de::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::de::from_str][de::from_str]. -//! More generally, if the [StaticType][de::StaticType] 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::de::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::de::from_str(point_type_data, None) -//! .expect("Could not parse the Point type"); -//! -//! // Deserialize it to a Rust type. -//! let deserialized_map: BTreeMap = -//! dhall::de::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; -pub(crate) mod api; -pub(crate) mod core; +pub mod core; pub mod error; -pub(crate) mod phase; - -pub use api::*; +pub mod phase; diff --git a/dhall/tests/traits.rs b/dhall/tests/traits.rs deleted file mode 100644 index 0f75553..0000000 --- a/dhall/tests/traits.rs +++ /dev/null @@ -1,68 +0,0 @@ -#![feature(proc_macro_hygiene)] -use dhall::de::{from_str, StaticType, Type}; - -#[test] -fn test_static_type() { - fn parse(s: &str) -> Type { - from_str(s, None).unwrap() - } - - assert_eq!(bool::static_type(), parse("Bool")); - assert_eq!(String::static_type(), parse("Text")); - assert_eq!(>::static_type(), parse("Optional Bool")); - assert_eq!( - <(bool, Vec)>::static_type(), - parse("{ _1: Bool, _2: List Text }") - ); - - #[derive(dhall::de::StaticType)] - #[allow(dead_code)] - struct A { - field1: bool, - field2: Option, - } - assert_eq!( - ::static_type(), - parse("{ field1: Bool, field2: Optional Bool }") - ); - - #[derive(StaticType)] - #[allow(dead_code)] - struct B<'a, T: 'a> { - field1: &'a T, - field2: Option, - } - assert_eq!(>::static_type(), A::static_type()); - - #[derive(StaticType)] - #[allow(dead_code)] - struct C(T, Option); - assert_eq!( - >::static_type(), - <(bool, Option)>::static_type() - ); - - #[derive(StaticType)] - #[allow(dead_code)] - struct D(); - assert_eq!( - >::static_type(), - parse("{ _1: {}, _2: Optional Text }") - ); - - #[derive(StaticType)] - #[allow(dead_code)] - enum E { - A(T), - B(String), - }; - assert_eq!(>::static_type(), parse("< A: Bool | B: Text >")); - - #[derive(StaticType)] - #[allow(dead_code)] - enum F { - A, - B(bool), - }; - assert_eq!(F::static_type(), parse("< A | B: Bool >")); -} diff --git a/dhall_proc_macros/src/derive.rs b/dhall_proc_macros/src/derive.rs index 725cdfb..0ebfe7d 100644 --- a/dhall_proc_macros/src/derive.rs +++ b/dhall_proc_macros/src/derive.rs @@ -18,7 +18,7 @@ where T: quote::ToTokens, { quote!( - <#ty as ::dhall::de::StaticType>::static_type() + <#ty as ::serde_dhall::de::StaticType>::static_type() ) } @@ -53,7 +53,7 @@ fn derive_for_struct( let ty = static_type(ty); quote!( (#name.to_owned(), #ty) ) }); - Ok(quote! { ::dhall::de::Type::make_record_type( + Ok(quote! { ::serde_dhall::de::Type::make_record_type( vec![ #(#entries),* ].into_iter() ) }) } @@ -90,7 +90,7 @@ fn derive_for_enum( }) .collect::>()?; - Ok(quote! { ::dhall::de::Type::make_union_type( + Ok(quote! { ::serde_dhall::de::Type::make_union_type( vec![ #(#entries),* ].into_iter() ) }) } @@ -134,7 +134,7 @@ pub fn derive_static_type_inner( let mut local_where_clause = orig_where_clause.clone(); local_where_clause .predicates - .push(parse_quote!(#ty: ::dhall::de::StaticType)); + .push(parse_quote!(#ty: ::serde_dhall::de::StaticType)); let phantoms = generics.params.iter().map(|param| match param { syn::GenericParam::Type(syn::TypeParam { ident, .. }) => { quote!(#ident) @@ -156,16 +156,16 @@ pub fn derive_static_type_inner( for ty in constraints.iter() { where_clause .predicates - .push(parse_quote!(#ty: ::dhall::de::StaticType)); + .push(parse_quote!(#ty: ::serde_dhall::de::StaticType)); } let ident = &input.ident; let tokens = quote! { - impl #impl_generics ::dhall::de::StaticType + impl #impl_generics ::serde_dhall::de::StaticType for #ident #ty_generics #where_clause { fn static_type() -> - ::dhall::de::Type { + ::serde_dhall::de::Type { #(#assertions)* #get_type } diff --git a/serde_dhall/Cargo.toml b/serde_dhall/Cargo.toml new file mode 100644 index 0000000..c61ddcd --- /dev/null +++ b/serde_dhall/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "serde_dhall" +version = "0.1.0" +authors = ["Nadrieril "] +license = "BSD-2-Clause" +edition = "2018" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +dhall = { path = "../dhall" } +dhall_syntax = { path = "../dhall_syntax" } +dhall_proc_macros = { path = "../dhall_proc_macros" } diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs new file mode 100644 index 0000000..1dbbf99 --- /dev/null +++ b/serde_dhall/src/lib.rs @@ -0,0 +1,264 @@ +//! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive +//! alternative to JSON and YAML. +//! +//! You can think of Dhall as: JSON + 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 [StaticType][de::StaticType] 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 serde_dhall::de::StaticType; +//! +//! #[derive(Debug, Deserialize, StaticType)] +//! struct Point { +//! x: u64, +//! y: u64, +//! } +//! +//! fn main() { +//! // Some dhall data +//! let data = "{ x = 1, y = 1 + 1 }"; +//! +//! // Convert the dhall string to a Point. +//! let point: Point = +//! serde_dhall::de::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 [serde_dhall::de::from_str][de::from_str]. +//! More generally, if the [StaticType][de::StaticType] 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 = +//! serde_dhall::de::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 = +//! serde_dhall::de::from_str(point_type_data, None) +//! .expect("Could not parse the Point type"); +//! +//! // Deserialize it to a Rust type. +//! let deserialized_map: BTreeMap = +//! serde_dhall::de::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 + +mod serde; +pub(crate) mod static_type; + +pub use value::Value; + +mod value { + use super::Type; + use dhall::error::Result; + use dhall::phase::{NormalizedSubExpr, Parsed, Typed}; + + // A Dhall value + pub struct Value(Typed); + + impl Value { + pub fn from_str(s: &str, ty: Option<&Type>) -> Result { + let resolved = Parsed::parse_str(s)?.resolve()?; + let typed = match ty { + None => resolved.typecheck()?, + Some(t) => resolved.typecheck_with(&t.to_type())?, + }; + Ok(Value(typed)) + } + pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + self.0.to_expr() + } + pub(crate) fn to_typed(&self) -> Typed { + self.0.clone() + } + } +} + +pub use typ::Type; + +mod typ { + use dhall_syntax::Builtin; + + use dhall::core::thunk::{Thunk, TypeThunk}; + use dhall::core::value::Value; + use dhall::error::Result; + use dhall::phase::{NormalizedSubExpr, Typed}; + + /// A Dhall expression representing a type. + /// + /// This captures what is usually simply called a "type", like + /// `Bool`, `{ x: Integer }` or `Natural -> Text`. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Type(Typed); + + impl Type { + pub(crate) fn from_value(v: Value) -> Self { + Type(Typed::from_value_untyped(v)) + } + pub(crate) fn make_builtin_type(b: Builtin) -> Self { + Self::from_value(Value::from_builtin(b)) + } + pub(crate) fn make_optional_type(t: Type) -> Self { + Self::from_value(Value::AppliedBuiltin( + Builtin::Optional, + vec![t.to_thunk()], + )) + } + pub(crate) fn make_list_type(t: Type) -> Self { + Self::from_value(Value::AppliedBuiltin( + Builtin::List, + vec![t.to_thunk()], + )) + } + #[doc(hidden)] + pub fn make_record_type( + kts: impl Iterator, + ) -> Self { + Self::from_value(Value::RecordType( + kts.map(|(k, t)| { + (k.into(), TypeThunk::from_thunk(t.to_thunk())) + }) + .collect(), + )) + } + #[doc(hidden)] + pub fn make_union_type( + kts: impl Iterator)>, + ) -> Self { + Self::from_value(Value::UnionType( + kts.map(|(k, t)| { + (k.into(), t.map(|t| TypeThunk::from_thunk(t.to_thunk()))) + }) + .collect(), + )) + } + + pub(crate) fn to_thunk(&self) -> Thunk { + self.0.to_thunk() + } + #[allow(dead_code)] + pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + self.0.to_expr() + } + pub(crate) fn to_type(&self) -> dhall::phase::Type { + self.0.to_type() + } + } + + impl crate::de::Deserialize for Type { + fn from_dhall(v: &super::Value) -> Result { + Ok(Type(v.to_typed())) + } + } +} + +/// Deserialization of Dhall expressions into Rust +pub mod de { + pub use super::static_type::StaticType; + pub use super::{Type, Value}; + use dhall::error::Result; + #[doc(hidden)] + pub use dhall_proc_macros::StaticType; + + /// A data structure that can be deserialized from a Dhall expression + /// + /// This is automatically implemented for any type that [serde][serde] + /// can deserialize. + /// + /// This trait cannot be implemented manually. + // TODO: seal trait + pub trait Deserialize: Sized { + /// See [dhall::de::from_str][crate::de::from_str] + fn from_dhall(v: &Value) -> Result; + } + + /// Deserialize an instance of type T from a string of Dhall text. + /// + /// This will recursively resolve all imports in the expression, and + /// typecheck it before deserialization. Relative imports will be resolved relative to the + /// provided file. More control over this process is not yet available + /// but will be in a coming version of this crate. + /// + /// If a type is provided, this additionally checks that the provided + /// expression has that type. + pub fn from_str(s: &str, ty: Option<&Type>) -> Result + where + T: Deserialize, + { + T::from_dhall(&Value::from_str(s, ty)?) + } + + /// Deserialize an instance of type T from a string of Dhall text, + /// additionally checking that it matches the type of T. + /// + /// This will recursively resolve all imports in the expression, and + /// typecheck it before deserialization. Relative imports will be resolved relative to the + /// provided file. More control over this process is not yet available + /// but will be in a coming version of this crate. + pub fn from_str_auto_type(s: &str) -> Result + where + T: Deserialize + StaticType, + { + from_str(s, Some(&::static_type())) + } +} diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs new file mode 100644 index 0000000..3dad2d8 --- /dev/null +++ b/serde_dhall/src/serde.rs @@ -0,0 +1,70 @@ +use crate::de::{Deserialize, Value}; +use dhall::error::{Error, Result}; +use dhall_syntax::{ExprF, SubExpr, X}; +use std::borrow::Cow; + +impl<'a, T> Deserialize for T +where + T: serde::Deserialize<'a>, +{ + fn from_dhall(v: &Value) -> Result { + T::deserialize(Deserializer(Cow::Owned(v.to_expr()))) + } +} + +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/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs new file mode 100644 index 0000000..13d5d70 --- /dev/null +++ b/serde_dhall/src/static_type.rs @@ -0,0 +1,93 @@ +use dhall_syntax::{Builtin, Integer, Natural}; + +use crate::Type; + +/// A Rust type that can be represented as a Dhall type. +/// +/// A typical example is `Option`, +/// represented by the dhall expression `Optional Bool`. +/// +/// This trait can and should be automatically derived. +/// +/// The representation needs to be independent of the value. +/// For this reason, something like `HashMap` cannot implement +/// [StaticType] because each different value would +/// have a different Dhall record type. +pub trait StaticType { + fn static_type() -> Type; +} + +macro_rules! derive_builtin { + ($ty:ty, $builtin:ident) => { + impl StaticType for $ty { + fn static_type() -> Type { + Type::make_builtin_type(Builtin::$builtin) + } + } + }; +} + +derive_builtin!(bool, Bool); +derive_builtin!(Natural, Natural); +derive_builtin!(u64, Natural); +derive_builtin!(Integer, Integer); +derive_builtin!(String, Text); + +impl StaticType for (A, B) +where + A: StaticType, + B: StaticType, +{ + fn static_type() -> Type { + Type::make_record_type( + vec![ + ("_1".to_owned(), A::static_type()), + ("_2".to_owned(), B::static_type()), + ] + .into_iter(), + ) + } +} + +impl StaticType for std::result::Result +where + T: StaticType, + E: StaticType, +{ + fn static_type() -> Type { + Type::make_union_type( + vec![ + ("Ok".to_owned(), Some(T::static_type())), + ("Err".to_owned(), Some(E::static_type())), + ] + .into_iter(), + ) + } +} + +impl StaticType for Option +where + T: StaticType, +{ + fn static_type() -> Type { + Type::make_optional_type(T::static_type()) + } +} + +impl StaticType for Vec +where + T: StaticType, +{ + fn static_type() -> Type { + Type::make_list_type(T::static_type()) + } +} + +impl<'a, T> StaticType for &'a T +where + T: StaticType, +{ + fn static_type() -> Type { + T::static_type() + } +} diff --git a/serde_dhall/tests/traits.rs b/serde_dhall/tests/traits.rs new file mode 100644 index 0000000..99f1109 --- /dev/null +++ b/serde_dhall/tests/traits.rs @@ -0,0 +1,68 @@ +#![feature(proc_macro_hygiene)] +use serde_dhall::de::{from_str, StaticType, Type}; + +#[test] +fn test_static_type() { + fn parse(s: &str) -> Type { + from_str(s, None).unwrap() + } + + assert_eq!(bool::static_type(), parse("Bool")); + assert_eq!(String::static_type(), parse("Text")); + assert_eq!(>::static_type(), parse("Optional Bool")); + assert_eq!( + <(bool, Vec)>::static_type(), + parse("{ _1: Bool, _2: List Text }") + ); + + #[derive(serde_dhall::de::StaticType)] + #[allow(dead_code)] + struct A { + field1: bool, + field2: Option, + } + assert_eq!( + ::static_type(), + parse("{ field1: Bool, field2: Optional Bool }") + ); + + #[derive(StaticType)] + #[allow(dead_code)] + struct B<'a, T: 'a> { + field1: &'a T, + field2: Option, + } + assert_eq!(>::static_type(), A::static_type()); + + #[derive(StaticType)] + #[allow(dead_code)] + struct C(T, Option); + assert_eq!( + >::static_type(), + <(bool, Option)>::static_type() + ); + + #[derive(StaticType)] + #[allow(dead_code)] + struct D(); + assert_eq!( + >::static_type(), + parse("{ _1: {}, _2: Optional Text }") + ); + + #[derive(StaticType)] + #[allow(dead_code)] + enum E { + A(T), + B(String), + }; + assert_eq!(>::static_type(), parse("< A: Bool | B: Text >")); + + #[derive(StaticType)] + #[allow(dead_code)] + enum F { + A, + B(bool), + }; + assert_eq!(F::static_type(), parse("< A | B: Bool >")); +} -- cgit v1.2.3 From 74bb40e88c71b80ab785f07fd19da22404ab6e95 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 15:11:31 +0200 Subject: Add new error type for serde_dhall --- dhall/Cargo.toml | 2 +- dhall/src/error/mod.rs | 12 ---------- serde_dhall/src/lib.rs | 58 +++++++++++++++++++++++++++++++++++++++++------- serde_dhall/src/serde.rs | 12 +--------- 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index aa52d58..0a257e4 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -10,7 +10,7 @@ build = "build.rs" bytecount = "0.5.1" itertools = "0.8.0" term-painter = "0.2.3" -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0" } serde_cbor = "0.9.0" improved_slice_patterns = { version = "2.0.0", path = "../improved_slice_patterns" } dhall_syntax = { path = "../dhall_syntax" } diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs index ecf3510..3626d96 100644 --- a/dhall/src/error/mod.rs +++ b/dhall/src/error/mod.rs @@ -17,7 +17,6 @@ pub enum Error { Encode(EncodeError), Resolve(ImportError), Typecheck(TypeError), - Deserialize(String), } #[derive(Debug)] @@ -156,7 +155,6 @@ impl std::fmt::Display for Error { Error::Encode(err) => write!(f, "{:?}", err), Error::Resolve(err) => write!(f, "{:?}", err), Error::Typecheck(err) => write!(f, "{:?}", err), - Error::Deserialize(err) => write!(f, "{}", err), } } } @@ -192,13 +190,3 @@ impl From for Error { Error::Typecheck(err) } } - -impl serde::de::Error for Error { - fn custom(msg: T) -> Self - where - T: std::fmt::Display, - { - Error::Deserialize(msg.to_string()) - } -} - diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 1dbbf99..f8f9ae9 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(non_exhaustive)] + //! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive //! alternative to JSON and YAML. //! @@ -109,8 +111,8 @@ pub(crate) mod static_type; pub use value::Value; mod value { + use super::de::{Error, Result}; use super::Type; - use dhall::error::Result; use dhall::phase::{NormalizedSubExpr, Parsed, Typed}; // A Dhall value @@ -118,6 +120,12 @@ mod value { impl Value { pub fn from_str(s: &str, ty: Option<&Type>) -> Result { + Value::from_str_using_dhall_error_type(s, ty).map_err(Error::Dhall) + } + fn from_str_using_dhall_error_type( + s: &str, + ty: Option<&Type>, + ) -> dhall::error::Result { let resolved = Parsed::parse_str(s)?.resolve()?; let typed = match ty { None => resolved.typecheck()?, @@ -137,12 +145,12 @@ mod value { pub use typ::Type; mod typ { - use dhall_syntax::Builtin; - use dhall::core::thunk::{Thunk, TypeThunk}; use dhall::core::value::Value; - use dhall::error::Result; use dhall::phase::{NormalizedSubExpr, Typed}; + use dhall_syntax::Builtin; + + use super::de::Result; /// A Dhall expression representing a type. /// @@ -205,21 +213,55 @@ mod typ { } } - impl crate::de::Deserialize for Type { + impl super::de::Deserialize for Type { fn from_dhall(v: &super::Value) -> Result { Ok(Type(v.to_typed())) } } } +mod error { + use dhall::error::Error as DhallError; + + pub type Result = std::result::Result; + + #[derive(Debug)] + #[non_exhaustive] + pub enum Error { + Dhall(DhallError), + Deserialize(String), + } + + impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Error::Dhall(err) => write!(f, "{}", err), + Error::Deserialize(err) => write!(f, "{}", err), + } + } + } + + impl std::error::Error for Error {} + + impl serde::de::Error for Error { + fn custom(msg: T) -> Self + where + T: std::fmt::Display, + { + Error::Deserialize(msg.to_string()) + } + } +} + /// Deserialization of Dhall expressions into Rust pub mod de { - pub use super::static_type::StaticType; - pub use super::{Type, Value}; - use dhall::error::Result; #[doc(hidden)] pub use dhall_proc_macros::StaticType; + pub use super::error::{Error, Result}; + pub use super::static_type::StaticType; + pub use super::{Type, Value}; + /// A data structure that can be deserialized from a Dhall expression /// /// This is automatically implemented for any type that [serde][serde] diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 3dad2d8..1f639e6 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -1,5 +1,4 @@ -use crate::de::{Deserialize, Value}; -use dhall::error::{Error, Result}; +use crate::de::{Deserialize, Error, Result, Value}; use dhall_syntax::{ExprF, SubExpr, X}; use std::borrow::Cow; @@ -14,15 +13,6 @@ where 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 { -- cgit v1.2.3 From 1ea8b10051d29c634399304273d6ee565d039bc2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 15:25:53 +0200 Subject: Merge `Type` and `Value` in serde_dhall There was no point in separating them --- dhall_proc_macros/src/derive.rs | 6 +-- serde_dhall/src/lib.rs | 87 +++++++++++++++-------------------------- serde_dhall/src/static_type.rs | 26 ++++++------ serde_dhall/tests/traits.rs | 4 +- 4 files changed, 50 insertions(+), 73 deletions(-) diff --git a/dhall_proc_macros/src/derive.rs b/dhall_proc_macros/src/derive.rs index 0ebfe7d..ea78766 100644 --- a/dhall_proc_macros/src/derive.rs +++ b/dhall_proc_macros/src/derive.rs @@ -53,7 +53,7 @@ fn derive_for_struct( let ty = static_type(ty); quote!( (#name.to_owned(), #ty) ) }); - Ok(quote! { ::serde_dhall::de::Type::make_record_type( + Ok(quote! { ::serde_dhall::de::Value::make_record_type( vec![ #(#entries),* ].into_iter() ) }) } @@ -90,7 +90,7 @@ fn derive_for_enum( }) .collect::>()?; - Ok(quote! { ::serde_dhall::de::Type::make_union_type( + Ok(quote! { ::serde_dhall::de::Value::make_union_type( vec![ #(#entries),* ].into_iter() ) }) } @@ -165,7 +165,7 @@ pub fn derive_static_type_inner( for #ident #ty_generics #where_clause { fn static_type() -> - ::serde_dhall::de::Type { + ::serde_dhall::de::Value { #(#assertions)* #get_type } diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index f8f9ae9..8e1af82 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -106,25 +106,29 @@ //! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html mod serde; -pub(crate) mod static_type; +mod static_type; pub use value::Value; mod value { + use dhall::core::thunk::{Thunk, TypeThunk}; + use dhall::core::value::Value as DhallValue; + use dhall::phase::{NormalizedSubExpr, Parsed, Type, Typed}; + use dhall_syntax::Builtin; + use super::de::{Error, Result}; - use super::Type; - use dhall::phase::{NormalizedSubExpr, Parsed, Typed}; - // A Dhall value + /// A Dhall value + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Value(Typed); impl Value { - pub fn from_str(s: &str, ty: Option<&Type>) -> Result { + pub fn from_str(s: &str, ty: Option<&Value>) -> Result { Value::from_str_using_dhall_error_type(s, ty).map_err(Error::Dhall) } fn from_str_using_dhall_error_type( s: &str, - ty: Option<&Type>, + ty: Option<&Value>, ) -> dhall::error::Result { let resolved = Parsed::parse_str(s)?.resolve()?; let typed = match ty { @@ -136,53 +140,37 @@ mod value { pub(crate) fn to_expr(&self) -> NormalizedSubExpr { self.0.to_expr() } - pub(crate) fn to_typed(&self) -> Typed { - self.0.clone() + pub(crate) fn to_thunk(&self) -> Thunk { + self.0.to_thunk() + } + pub(crate) fn to_type(&self) -> Type { + self.0.to_type() } - } -} - -pub use typ::Type; - -mod typ { - use dhall::core::thunk::{Thunk, TypeThunk}; - use dhall::core::value::Value; - use dhall::phase::{NormalizedSubExpr, Typed}; - use dhall_syntax::Builtin; - - use super::de::Result; - - /// A Dhall expression representing a type. - /// - /// This captures what is usually simply called a "type", like - /// `Bool`, `{ x: Integer }` or `Natural -> Text`. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Type(Typed); - impl Type { - pub(crate) fn from_value(v: Value) -> Self { - Type(Typed::from_value_untyped(v)) + pub(crate) fn from_dhall_value(v: DhallValue) -> Self { + Value(Typed::from_value_untyped(v)) } pub(crate) fn make_builtin_type(b: Builtin) -> Self { - Self::from_value(Value::from_builtin(b)) + Self::from_dhall_value(DhallValue::from_builtin(b)) } - pub(crate) fn make_optional_type(t: Type) -> Self { - Self::from_value(Value::AppliedBuiltin( + pub(crate) fn make_optional_type(t: Value) -> Self { + Self::from_dhall_value(DhallValue::AppliedBuiltin( Builtin::Optional, vec![t.to_thunk()], )) } - pub(crate) fn make_list_type(t: Type) -> Self { - Self::from_value(Value::AppliedBuiltin( + pub(crate) fn make_list_type(t: Value) -> Self { + Self::from_dhall_value(DhallValue::AppliedBuiltin( Builtin::List, vec![t.to_thunk()], )) } + // Made public for the StaticType derive macro #[doc(hidden)] pub fn make_record_type( - kts: impl Iterator, + kts: impl Iterator, ) -> Self { - Self::from_value(Value::RecordType( + Self::from_dhall_value(DhallValue::RecordType( kts.map(|(k, t)| { (k.into(), TypeThunk::from_thunk(t.to_thunk())) }) @@ -191,31 +179,20 @@ mod typ { } #[doc(hidden)] pub fn make_union_type( - kts: impl Iterator)>, + kts: impl Iterator)>, ) -> Self { - Self::from_value(Value::UnionType( + Self::from_dhall_value(DhallValue::UnionType( kts.map(|(k, t)| { (k.into(), t.map(|t| TypeThunk::from_thunk(t.to_thunk()))) }) .collect(), )) } - - pub(crate) fn to_thunk(&self) -> Thunk { - self.0.to_thunk() - } - #[allow(dead_code)] - pub(crate) fn to_expr(&self) -> NormalizedSubExpr { - self.0.to_expr() - } - pub(crate) fn to_type(&self) -> dhall::phase::Type { - self.0.to_type() - } } - impl super::de::Deserialize for Type { - fn from_dhall(v: &super::Value) -> Result { - Ok(Type(v.to_typed())) + impl super::de::Deserialize for Value { + fn from_dhall(v: &Value) -> Result { + Ok(v.clone()) } } } @@ -260,7 +237,7 @@ pub mod de { pub use super::error::{Error, Result}; pub use super::static_type::StaticType; - pub use super::{Type, Value}; + pub use super::Value; /// A data structure that can be deserialized from a Dhall expression /// @@ -283,7 +260,7 @@ pub mod de { /// /// If a type is provided, this additionally checks that the provided /// expression has that type. - pub fn from_str(s: &str, ty: Option<&Type>) -> Result + pub fn from_str(s: &str, ty: Option<&Value>) -> Result where T: Deserialize, { diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index 13d5d70..67a7bc4 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -1,6 +1,6 @@ use dhall_syntax::{Builtin, Integer, Natural}; -use crate::Type; +use crate::Value; /// A Rust type that can be represented as a Dhall type. /// @@ -14,14 +14,14 @@ use crate::Type; /// [StaticType] because each different value would /// have a different Dhall record type. pub trait StaticType { - fn static_type() -> Type; + fn static_type() -> Value; } macro_rules! derive_builtin { ($ty:ty, $builtin:ident) => { impl StaticType for $ty { - fn static_type() -> Type { - Type::make_builtin_type(Builtin::$builtin) + fn static_type() -> Value { + Value::make_builtin_type(Builtin::$builtin) } } }; @@ -38,8 +38,8 @@ where A: StaticType, B: StaticType, { - fn static_type() -> Type { - Type::make_record_type( + fn static_type() -> Value { + Value::make_record_type( vec![ ("_1".to_owned(), A::static_type()), ("_2".to_owned(), B::static_type()), @@ -54,8 +54,8 @@ where T: StaticType, E: StaticType, { - fn static_type() -> Type { - Type::make_union_type( + fn static_type() -> Value { + Value::make_union_type( vec![ ("Ok".to_owned(), Some(T::static_type())), ("Err".to_owned(), Some(E::static_type())), @@ -69,8 +69,8 @@ impl StaticType for Option where T: StaticType, { - fn static_type() -> Type { - Type::make_optional_type(T::static_type()) + fn static_type() -> Value { + Value::make_optional_type(T::static_type()) } } @@ -78,8 +78,8 @@ impl StaticType for Vec where T: StaticType, { - fn static_type() -> Type { - Type::make_list_type(T::static_type()) + fn static_type() -> Value { + Value::make_list_type(T::static_type()) } } @@ -87,7 +87,7 @@ impl<'a, T> StaticType for &'a T where T: StaticType, { - fn static_type() -> Type { + fn static_type() -> Value { T::static_type() } } diff --git a/serde_dhall/tests/traits.rs b/serde_dhall/tests/traits.rs index 99f1109..40ac1d7 100644 --- a/serde_dhall/tests/traits.rs +++ b/serde_dhall/tests/traits.rs @@ -1,9 +1,9 @@ #![feature(proc_macro_hygiene)] -use serde_dhall::de::{from_str, StaticType, Type}; +use serde_dhall::de::{from_str, StaticType, Value}; #[test] fn test_static_type() { - fn parse(s: &str) -> Type { + fn parse(s: &str) -> Value { from_str(s, None).unwrap() } -- cgit v1.2.3 From bf5d33f3ba991afe398d58fb4fed38ec72d6f4c7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 16:31:41 +0200 Subject: Rework API to resemble that of serde_json --- dhall_proc_macros/src/derive.rs | 14 +-- serde_dhall/src/lib.rs | 200 ++++++++++++++++++++++------------------ serde_dhall/src/serde.rs | 3 +- serde_dhall/tests/traits.rs | 8 +- 4 files changed, 122 insertions(+), 103 deletions(-) diff --git a/dhall_proc_macros/src/derive.rs b/dhall_proc_macros/src/derive.rs index ea78766..79b553b 100644 --- a/dhall_proc_macros/src/derive.rs +++ b/dhall_proc_macros/src/derive.rs @@ -18,7 +18,7 @@ where T: quote::ToTokens, { quote!( - <#ty as ::serde_dhall::de::StaticType>::static_type() + <#ty as ::serde_dhall::StaticType>::static_type() ) } @@ -53,7 +53,7 @@ fn derive_for_struct( let ty = static_type(ty); quote!( (#name.to_owned(), #ty) ) }); - Ok(quote! { ::serde_dhall::de::Value::make_record_type( + Ok(quote! { ::serde_dhall::value::Value::make_record_type( vec![ #(#entries),* ].into_iter() ) }) } @@ -90,7 +90,7 @@ fn derive_for_enum( }) .collect::>()?; - Ok(quote! { ::serde_dhall::de::Value::make_union_type( + Ok(quote! { ::serde_dhall::value::Value::make_union_type( vec![ #(#entries),* ].into_iter() ) }) } @@ -134,7 +134,7 @@ pub fn derive_static_type_inner( let mut local_where_clause = orig_where_clause.clone(); local_where_clause .predicates - .push(parse_quote!(#ty: ::serde_dhall::de::StaticType)); + .push(parse_quote!(#ty: ::serde_dhall::StaticType)); let phantoms = generics.params.iter().map(|param| match param { syn::GenericParam::Type(syn::TypeParam { ident, .. }) => { quote!(#ident) @@ -156,16 +156,16 @@ pub fn derive_static_type_inner( for ty in constraints.iter() { where_clause .predicates - .push(parse_quote!(#ty: ::serde_dhall::de::StaticType)); + .push(parse_quote!(#ty: ::serde_dhall::StaticType)); } let ident = &input.ident; let tokens = quote! { - impl #impl_generics ::serde_dhall::de::StaticType + impl #impl_generics ::serde_dhall::StaticType for #ident #ty_generics #where_clause { fn static_type() -> - ::serde_dhall::de::Value { + ::serde_dhall::value::Value { #(#assertions)* #get_type } diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 8e1af82..19aabec 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -5,28 +5,27 @@ //! //! You can think of Dhall as: JSON + types + imports + functions //! -//! For a description of the dhall language, examples, tutorials, and more, see the [language +//! 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 +//! 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. +//! 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 [StaticType][de::StaticType] 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. +//! you will be able to derive [StaticType] for it as well. +//! This allows easy type-safe deserializing. //! //! ```edition2018 //! use serde::Deserialize; -//! use serde_dhall::de::StaticType; +//! use serde_dhall::{de::Error, StaticType}; //! //! #[derive(Debug, Deserialize, StaticType)] //! struct Point { @@ -34,71 +33,78 @@ //! y: u64, //! } //! -//! fn main() { -//! // Some dhall data +//! fn main() -> Result<(), Error> { +//! // Some Dhall data //! let data = "{ x = 1, y = 1 + 1 }"; //! -//! // Convert the dhall string to a Point. -//! let point: Point = -//! serde_dhall::de::from_str_auto_type(&data) -//! .expect("An error ocurred !"); +//! // Convert the Dhall string to a Point. +//! let point: Point = serde_dhall::from_str_auto_type(data)?; +//! assert_eq!(point.x, 1); +//! assert_eq!(point.y, 2); //! -//! // Prints "point = Point { x: 1, y: 2 }" -//! println!("point = {:?}", point); +//! // Invalid data fails the type validation +//! let invalid_data = "{ x = 1, z = 0.3 }"; +//! assert!(serde_dhall::from_str_auto_type::(invalid_data).is_err()); +//! +//! Ok(()) //! } //! ``` //! //! ### 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 [serde_dhall::de::from_str][de::from_str]. -//! More generally, if the [StaticType][de::StaticType] derive doesn't suit your -//! needs, you can still deserialize any valid dhall file that serde can handle. +//! with Dhall. You only need to replace [serde_json::from_str] or [serde_yaml::from_str] +//! with [serde_dhall::from_str][from_str]. +//! More generally, if the [StaticType] 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 +//! # fn main() -> serde_dhall::de::Result<()> { //! use std::collections::BTreeMap; //! -//! let mut map = BTreeMap::new(); -//! map.insert("x".to_string(), 1); -//! map.insert("y".to_string(), 2); -//! -//! // Some dhall data +//! // Some Dhall data //! let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; //! //! // Deserialize it to a Rust type. -//! let deserialized_map: BTreeMap = -//! serde_dhall::de::from_str(&data, None) -//! .expect("Failed reading the data !"); -//! assert_eq!(map, deserialized_map); +//! let deserialized_map: BTreeMap = serde_dhall::from_str(data)?; +//! +//! let mut expected_map = BTreeMap::new(); +//! expected_map.insert("x".to_string(), 1); +//! expected_map.insert("y".to_string(), 2); +//! +//! assert_eq!(deserialized_map, expected_map); +//! # Ok(()) +//! # } //! ``` //! -//! You can of course specify a dhall type that the input should match. +//! You can alternatively specify a Dhall type that the input should match. //! //! ```edition2018 +//! # fn main() -> serde_dhall::de::Result<()> { //! use std::collections::BTreeMap; //! -//! let mut map = BTreeMap::new(); -//! map.insert("x".to_string(), 1); -//! map.insert("y".to_string(), 2); +//! // Parse a Dhall type +//! let point_type_str = "{ x: Natural, y: Natural }"; +//! let point_type = serde_dhall::from_str(point_type_str)?; //! -//! // Some dhall data +//! // 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 = -//! serde_dhall::de::from_str(point_type_data, None) -//! .expect("Could not parse the Point type"); //! -//! // Deserialize it to a Rust type. +//! // Deserialize the data to a Rust type. This ensures that +//! // the data matches the point type. //! let deserialized_map: BTreeMap = -//! serde_dhall::de::from_str(&point_data, Some(&point_type)) -//! .expect("Failed reading the data !"); -//! assert_eq!(map, deserialized_map); +//! serde_dhall::from_str_check_type(point_data, &point_type)?; +//! +//! let mut expected_map = BTreeMap::new(); +//! expected_map.insert("x".to_string(), 1); +//! expected_map.insert("y".to_string(), 2); +//! +//! assert_eq!(deserialized_map, expected_map); +//! # Ok(()) +//! # } //! ``` //! //! [dhall]: https://dhall-lang.org/ @@ -108,9 +114,16 @@ mod serde; mod static_type; +#[doc(inline)] +pub use de::{from_str, from_str_auto_type, from_str_check_type}; +#[doc(hidden)] +pub use dhall_proc_macros::StaticType; +pub use static_type::StaticType; +#[doc(inline)] pub use value::Value; -mod value { +// A Dhall value. +pub mod value { use dhall::core::thunk::{Thunk, TypeThunk}; use dhall::core::value::Value as DhallValue; use dhall::phase::{NormalizedSubExpr, Parsed, Type, Typed}; @@ -197,47 +210,44 @@ mod value { } } -mod error { - use dhall::error::Error as DhallError; +/// Deserialize Dhall data to a Rust data structure. +pub mod de { + use super::StaticType; + use super::Value; + pub use error::{Error, Result}; - pub type Result = std::result::Result; + mod error { + use dhall::error::Error as DhallError; - #[derive(Debug)] - #[non_exhaustive] - pub enum Error { - Dhall(DhallError), - Deserialize(String), - } + pub type Result = std::result::Result; - impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::Dhall(err) => write!(f, "{}", err), - Error::Deserialize(err) => write!(f, "{}", err), + #[derive(Debug)] + #[non_exhaustive] + pub enum Error { + Dhall(DhallError), + Deserialize(String), + } + + impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Error::Dhall(err) => write!(f, "{}", err), + Error::Deserialize(err) => write!(f, "{}", err), + } } } - } - impl std::error::Error for Error {} + impl std::error::Error for Error {} - impl serde::de::Error for Error { - fn custom(msg: T) -> Self - where - T: std::fmt::Display, - { - Error::Deserialize(msg.to_string()) + impl serde::de::Error for Error { + fn custom(msg: T) -> Self + where + T: std::fmt::Display, + { + Error::Deserialize(msg.to_string()) + } } } -} - -/// Deserialization of Dhall expressions into Rust -pub mod de { - #[doc(hidden)] - pub use dhall_proc_macros::StaticType; - - pub use super::error::{Error, Result}; - pub use super::static_type::StaticType; - pub use super::Value; /// A data structure that can be deserialized from a Dhall expression /// @@ -247,37 +257,45 @@ pub mod de { /// This trait cannot be implemented manually. // TODO: seal trait pub trait Deserialize: Sized { - /// See [dhall::de::from_str][crate::de::from_str] + /// See [serde_dhall::from_str][crate::from_str] fn from_dhall(v: &Value) -> Result; } - /// Deserialize an instance of type T from a string of Dhall text. + /// Deserialize an instance of type `T` from a string of Dhall text. /// /// This will recursively resolve all imports in the expression, and /// typecheck it before deserialization. Relative imports will be resolved relative to the /// provided file. More control over this process is not yet available /// but will be in a coming version of this crate. + pub fn from_str(s: &str) -> Result + where + T: Deserialize, + { + T::from_dhall(&Value::from_str(s, None)?) + } + + /// Deserialize an instance of type `T` from a string of Dhall text, + /// additionally checking that it matches the supplied type. /// - /// If a type is provided, this additionally checks that the provided - /// expression has that type. - pub fn from_str(s: &str, ty: Option<&Value>) -> Result + /// Like [from_str], but this additionally checks that + /// the type of the provided expression matches the supplied type. + pub fn from_str_check_type(s: &str, ty: &Value) -> Result where T: Deserialize, { - T::from_dhall(&Value::from_str(s, ty)?) + T::from_dhall(&Value::from_str(s, Some(ty))?) } - /// Deserialize an instance of type T from a string of Dhall text, - /// additionally checking that it matches the type of T. + /// Deserialize an instance of type `T` from a string of Dhall text, + /// additionally checking that it matches the type of `T`. /// - /// This will recursively resolve all imports in the expression, and - /// typecheck it before deserialization. Relative imports will be resolved relative to the - /// provided file. More control over this process is not yet available - /// but will be in a coming version of this crate. + /// Like [from_str], but this additionally checks that + /// the type of the provided expression matches the output type `T`. The [StaticType] trait + /// captures Rust types that are valid Dhall types. pub fn from_str_auto_type(s: &str) -> Result where T: Deserialize + StaticType, { - from_str(s, Some(&::static_type())) + from_str_check_type(s, &::static_type()) } } diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 1f639e6..10d5a17 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -1,4 +1,5 @@ -use crate::de::{Deserialize, Error, Result, Value}; +use crate::de::{Deserialize, Error, Result}; +use crate::Value; use dhall_syntax::{ExprF, SubExpr, X}; use std::borrow::Cow; diff --git a/serde_dhall/tests/traits.rs b/serde_dhall/tests/traits.rs index 40ac1d7..55be63b 100644 --- a/serde_dhall/tests/traits.rs +++ b/serde_dhall/tests/traits.rs @@ -1,10 +1,10 @@ #![feature(proc_macro_hygiene)] -use serde_dhall::de::{from_str, StaticType, Value}; +use serde_dhall::{from_str, StaticType, Value}; #[test] fn test_static_type() { fn parse(s: &str) -> Value { - from_str(s, None).unwrap() + from_str(s).unwrap() } assert_eq!(bool::static_type(), parse("Bool")); @@ -15,14 +15,14 @@ fn test_static_type() { parse("{ _1: Bool, _2: List Text }") ); - #[derive(serde_dhall::de::StaticType)] + #[derive(serde_dhall::StaticType)] #[allow(dead_code)] struct A { field1: bool, field2: Option, } assert_eq!( - ::static_type(), + ::static_type(), parse("{ field1: Bool, field2: Optional Bool }") ); -- cgit v1.2.3 From 1ed3123aeb3c9272b6810605a7ee781c42095f09 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 17:20:35 +0200 Subject: Swap Typed and TypeThunk --- dhall/src/core/thunk.rs | 125 ++++++++++++++++++++++++++++++++++++++++-------- dhall/src/phase/mod.rs | 95 ++++++++++-------------------------- 2 files changed, 129 insertions(+), 91 deletions(-) diff --git a/dhall/src/core/thunk.rs b/dhall/src/core/thunk.rs index f41579c..740ecbc 100644 --- a/dhall/src/core/thunk.rs +++ b/dhall/src/core/thunk.rs @@ -1,15 +1,18 @@ +use std::borrow::Cow; use std::cell::{Ref, RefCell}; use std::rc::Rc; -use dhall_syntax::{ExprF, X}; +use dhall_syntax::{Const, ExprF, X}; -use crate::core::context::NormalizationContext; +use crate::core::context::{NormalizationContext, TypecheckContext}; use crate::core::value::Value; use crate::core::var::{AlphaVar, Shift, Subst}; +use crate::error::{TypeError, TypeMessage}; use crate::phase::normalize::{ apply_any, normalize_one_layer, normalize_whnf, InputSubExpr, OutputSubExpr, }; -use crate::phase::{Type, Typed}; +use crate::phase::typecheck::type_of_const; +use crate::phase::{NormalizedSubExpr, Type, Typed}; #[derive(Debug, Clone, Copy)] enum Marker { @@ -42,7 +45,16 @@ pub struct Thunk(Rc>); /// A thunk in type position. Can optionally store a Type from the typechecking phase to preserve /// type information through the normalization phase. #[derive(Debug, Clone)] -pub struct TypeThunk(Typed); +pub enum TypeThunk { + // Any value, along with (optionally) its type + Untyped(Thunk), + Typed(Thunk, Box), + // One of the base higher-kinded typed. + // Used to avoid storing the same tower ot Type->Kind->Sort + // over and over again. Also enables having Sort as a type + // even though it doesn't itself have a type. + Const(Const), +} impl ThunkInternal { fn into_thunk(self) -> Thunk { @@ -190,39 +202,94 @@ impl TypeThunk { } pub fn from_thunk(th: Thunk) -> TypeThunk { - TypeThunk(Typed::from_thunk_untyped(th)) + TypeThunk::from_thunk_untyped(th) } pub fn from_type(t: Type) -> TypeThunk { - TypeThunk(t) + t.into_typethunk() } - pub fn normalize_mut(&mut self) { - self.0.normalize_mut() + pub fn normalize_nf(&self) -> Value { + match self { + TypeThunk::Const(c) => Value::Const(*c), + TypeThunk::Untyped(thunk) | TypeThunk::Typed(thunk, _) => { + thunk.normalize_nf().clone() + } + } } - pub fn normalize_nf(&self) -> Value { - self.0.to_value().normalize() + pub fn to_typed(&self) -> Typed { + self.clone().into_typed() } - pub fn to_value(&self) -> Value { - self.0.to_value() + pub fn normalize_to_expr_maybe_alpha(&self, alpha: bool) -> OutputSubExpr { + self.normalize_nf().normalize_to_expr_maybe_alpha(alpha) } - pub fn to_thunk(&self) -> Thunk { - self.0.to_thunk() + pub fn from_thunk_and_type(th: Thunk, t: Type) -> Self { + TypeThunk::Typed(th, Box::new(t)) + } + pub fn from_thunk_untyped(th: Thunk) -> Self { + TypeThunk::Untyped(th) + } + pub fn from_const(c: Const) -> Self { + TypeThunk::Const(c) + } + pub fn from_value_untyped(v: Value) -> Self { + TypeThunk::from_thunk_untyped(Thunk::from_value(v)) } + // TODO: Avoid cloning if possible + pub fn to_value(&self) -> Value { + match self { + TypeThunk::Untyped(th) | TypeThunk::Typed(th, _) => th.to_value(), + TypeThunk::Const(c) => Value::Const(*c), + } + } + pub fn to_expr(&self) -> NormalizedSubExpr { + self.to_value().normalize_to_expr() + } + pub fn to_expr_alpha(&self) -> NormalizedSubExpr { + self.to_value().normalize_to_expr_maybe_alpha(true) + } + pub fn to_thunk(&self) -> Thunk { + match self { + TypeThunk::Untyped(th) | TypeThunk::Typed(th, _) => th.clone(), + TypeThunk::Const(c) => Thunk::from_value(Value::Const(*c)), + } + } pub fn to_type(&self) -> Type { - self.0.to_type() + self.clone().into_typed().into_type() + } + pub fn into_typed(self) -> Typed { + Typed::from_typethunk(self) + } + pub fn as_const(&self) -> Option { + // TODO: avoid clone + match &self.to_value() { + Value::Const(c) => Some(*c), + _ => None, + } } - pub fn to_typed(&self) -> Typed { - self.0.clone() + pub fn normalize_mut(&mut self) { + match self { + TypeThunk::Untyped(th) | TypeThunk::Typed(th, _) => { + th.normalize_mut() + } + TypeThunk::Const(_) => {} + } } - pub fn normalize_to_expr_maybe_alpha(&self, alpha: bool) -> OutputSubExpr { - self.normalize_nf().normalize_to_expr_maybe_alpha(alpha) + pub fn get_type(&self) -> Result, TypeError> { + match self { + TypeThunk::Untyped(_) => Err(TypeError::new( + &TypecheckContext::new(), + TypeMessage::Untyped, + )), + TypeThunk::Typed(_, t) => Ok(Cow::Borrowed(t)), + TypeThunk::Const(c) => Ok(Cow::Owned(type_of_const(*c)?)), + } } } @@ -254,7 +321,14 @@ impl Shift for ThunkInternal { impl Shift for TypeThunk { fn shift(&self, delta: isize, var: &AlphaVar) -> Option { - Some(TypeThunk(self.0.shift(delta, var)?)) + Some(match self { + TypeThunk::Untyped(th) => TypeThunk::Untyped(th.shift(delta, var)?), + TypeThunk::Typed(th, t) => TypeThunk::Typed( + th.shift(delta, var)?, + Box::new(t.shift(delta, var)?), + ), + TypeThunk::Const(c) => TypeThunk::Const(*c), + }) } } @@ -293,7 +367,16 @@ impl Subst for ThunkInternal { impl Subst for TypeThunk { fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { - TypeThunk(self.0.subst_shift(var, val)) + match self { + TypeThunk::Untyped(th) => { + TypeThunk::Untyped(th.subst_shift(var, val)) + } + TypeThunk::Typed(th, t) => TypeThunk::Typed( + th.subst_shift(var, val), + Box::new(t.subst_shift(var, val)), + ), + TypeThunk::Const(c) => TypeThunk::Const(*c), + } } } diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs index ccedff2..0b05227 100644 --- a/dhall/src/phase/mod.rs +++ b/dhall/src/phase/mod.rs @@ -4,14 +4,12 @@ use std::path::Path; use dhall_syntax::{Const, Import, Span, SubExpr, X}; -use crate::core::context::TypecheckContext; -use crate::core::thunk::Thunk; +use crate::core::thunk::{Thunk, TypeThunk}; use crate::core::value::Value; use crate::core::var::{AlphaVar, Shift, Subst}; -use crate::error::{EncodeError, Error, ImportError, TypeError, TypeMessage}; +use crate::error::{EncodeError, Error, ImportError, TypeError}; use resolve::ImportRoot; -use typecheck::type_of_const; pub(crate) mod binary; pub(crate) mod normalize; @@ -33,16 +31,7 @@ pub struct Resolved(ResolvedSubExpr); /// A typed expression #[derive(Debug, Clone)] -pub enum Typed { - // Any value, along with (optionally) its type - Untyped(Thunk), - Typed(Thunk, Box), - // One of the base higher-kinded typed. - // Used to avoid storing the same tower ot Type->Kind->Sort - // over and over again. Also enables having Sort as a type - // even though it doesn't itself have a type. - Const(Const), -} +pub struct Typed(TypeThunk); /// A normalized expression. /// @@ -105,83 +94,63 @@ impl Typed { /// /// However, `normalize` will not fail if the expression is ill-typed and will /// leave ill-typed sub-expressions unevaluated. - pub fn normalize(self) -> Normalized { - match &self { - Typed::Const(_) => {} - Typed::Untyped(thunk) | Typed::Typed(thunk, _) => { - thunk.normalize_nf(); - } - } + pub fn normalize(mut self) -> Normalized { + self.normalize_mut(); Normalized(self) } pub fn from_thunk_and_type(th: Thunk, t: Type) -> Self { - Typed::Typed(th, Box::new(t)) + Typed(TypeThunk::from_thunk_and_type(th, t)) } pub fn from_thunk_untyped(th: Thunk) -> Self { - Typed::Untyped(th) + Typed(TypeThunk::from_thunk_untyped(th)) } pub fn from_const(c: Const) -> Self { - Typed::Const(c) + Typed(TypeThunk::from_const(c)) } pub fn from_value_untyped(v: Value) -> Self { - Typed::from_thunk_untyped(Thunk::from_value(v)) + Typed(TypeThunk::from_value_untyped(v)) + } + pub fn from_typethunk(th: TypeThunk) -> Self { + Typed(th) } - // TODO: Avoid cloning if possible pub fn to_value(&self) -> Value { - match self { - Typed::Untyped(th) | Typed::Typed(th, _) => th.to_value(), - Typed::Const(c) => Value::Const(*c), - } + self.0.to_value() } pub fn to_expr(&self) -> NormalizedSubExpr { - self.to_value().normalize_to_expr() + self.0.to_expr() } pub fn to_expr_alpha(&self) -> NormalizedSubExpr { - self.to_value().normalize_to_expr_maybe_alpha(true) + self.0.to_expr_alpha() } pub fn to_thunk(&self) -> Thunk { - match self { - Typed::Untyped(th) | Typed::Typed(th, _) => th.clone(), - Typed::Const(c) => Thunk::from_value(Value::Const(*c)), - } + self.0.to_thunk() } // Deprecated pub fn to_type(&self) -> Type { - self.clone().into_type() + self.clone() } // Deprecated pub fn into_type(self) -> Type { self } + pub fn into_typethunk(self) -> TypeThunk { + self.0 + } pub fn to_normalized(&self) -> Normalized { self.clone().normalize() } pub fn as_const(&self) -> Option { - // TODO: avoid clone - match &self.to_value() { - Value::Const(c) => Some(*c), - _ => None, - } + self.0.as_const() } pub fn normalize_mut(&mut self) { - match self { - Typed::Untyped(th) | Typed::Typed(th, _) => th.normalize_mut(), - Typed::Const(_) => {} - } + self.0.normalize_mut() } pub fn get_type(&self) -> Result, TypeError> { - match self { - Typed::Untyped(_) => Err(TypeError::new( - &TypecheckContext::new(), - TypeMessage::Untyped, - )), - Typed::Typed(_, t) => Ok(Cow::Borrowed(t)), - Typed::Const(c) => Ok(Cow::Owned(type_of_const(*c)?)), - } + self.0.get_type() } } @@ -208,14 +177,7 @@ impl Normalized { impl Shift for Typed { fn shift(&self, delta: isize, var: &AlphaVar) -> Option { - Some(match self { - Typed::Untyped(th) => Typed::Untyped(th.shift(delta, var)?), - Typed::Typed(th, t) => Typed::Typed( - th.shift(delta, var)?, - Box::new(t.shift(delta, var)?), - ), - Typed::Const(c) => Typed::Const(*c), - }) + Some(Typed(self.0.shift(delta, var)?)) } } @@ -227,14 +189,7 @@ impl Shift for Normalized { impl Subst for Typed { fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { - match self { - Typed::Untyped(th) => Typed::Untyped(th.subst_shift(var, val)), - Typed::Typed(th, t) => Typed::Typed( - th.subst_shift(var, val), - Box::new(t.subst_shift(var, val)), - ), - Typed::Const(c) => Typed::Const(*c), - } + Typed(self.0.subst_shift(var, val)) } } -- cgit v1.2.3 From fb0120dffe8e9552c3da7b994ad850f66dc612a3 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 17:21:54 +0200 Subject: s/TypeThunk/TypedThunk/g --- dhall/src/core/thunk.rs | 68 +++++++++++++++++++++++--------------------- dhall/src/core/value.rs | 24 ++++++++-------- dhall/src/phase/mod.rs | 16 +++++------ dhall/src/phase/normalize.rs | 34 +++++++++++----------- dhall/src/phase/typecheck.rs | 40 +++++++++++++++----------- serde_dhall/src/lib.rs | 6 ++-- 6 files changed, 99 insertions(+), 89 deletions(-) diff --git a/dhall/src/core/thunk.rs b/dhall/src/core/thunk.rs index 740ecbc..38c54f7 100644 --- a/dhall/src/core/thunk.rs +++ b/dhall/src/core/thunk.rs @@ -45,7 +45,7 @@ pub struct Thunk(Rc>); /// A thunk in type position. Can optionally store a Type from the typechecking phase to preserve /// type information through the normalization phase. #[derive(Debug, Clone)] -pub enum TypeThunk { +pub enum TypedThunk { // Any value, along with (optionally) its type Untyped(Thunk), Typed(Thunk, Box), @@ -196,23 +196,23 @@ impl Thunk { } } -impl TypeThunk { - pub fn from_value(v: Value) -> TypeThunk { - TypeThunk::from_thunk(Thunk::from_value(v)) +impl TypedThunk { + pub fn from_value(v: Value) -> TypedThunk { + TypedThunk::from_thunk(Thunk::from_value(v)) } - pub fn from_thunk(th: Thunk) -> TypeThunk { - TypeThunk::from_thunk_untyped(th) + pub fn from_thunk(th: Thunk) -> TypedThunk { + TypedThunk::from_thunk_untyped(th) } - pub fn from_type(t: Type) -> TypeThunk { + pub fn from_type(t: Type) -> TypedThunk { t.into_typethunk() } pub fn normalize_nf(&self) -> Value { match self { - TypeThunk::Const(c) => Value::Const(*c), - TypeThunk::Untyped(thunk) | TypeThunk::Typed(thunk, _) => { + TypedThunk::Const(c) => Value::Const(*c), + TypedThunk::Untyped(thunk) | TypedThunk::Typed(thunk, _) => { thunk.normalize_nf().clone() } } @@ -227,23 +227,23 @@ impl TypeThunk { } pub fn from_thunk_and_type(th: Thunk, t: Type) -> Self { - TypeThunk::Typed(th, Box::new(t)) + TypedThunk::Typed(th, Box::new(t)) } pub fn from_thunk_untyped(th: Thunk) -> Self { - TypeThunk::Untyped(th) + TypedThunk::Untyped(th) } pub fn from_const(c: Const) -> Self { - TypeThunk::Const(c) + TypedThunk::Const(c) } pub fn from_value_untyped(v: Value) -> Self { - TypeThunk::from_thunk_untyped(Thunk::from_value(v)) + TypedThunk::from_thunk_untyped(Thunk::from_value(v)) } // TODO: Avoid cloning if possible pub fn to_value(&self) -> Value { match self { - TypeThunk::Untyped(th) | TypeThunk::Typed(th, _) => th.to_value(), - TypeThunk::Const(c) => Value::Const(*c), + TypedThunk::Untyped(th) | TypedThunk::Typed(th, _) => th.to_value(), + TypedThunk::Const(c) => Value::Const(*c), } } pub fn to_expr(&self) -> NormalizedSubExpr { @@ -254,8 +254,8 @@ impl TypeThunk { } pub fn to_thunk(&self) -> Thunk { match self { - TypeThunk::Untyped(th) | TypeThunk::Typed(th, _) => th.clone(), - TypeThunk::Const(c) => Thunk::from_value(Value::Const(*c)), + TypedThunk::Untyped(th) | TypedThunk::Typed(th, _) => th.clone(), + TypedThunk::Const(c) => Thunk::from_value(Value::Const(*c)), } } pub fn to_type(&self) -> Type { @@ -274,21 +274,21 @@ impl TypeThunk { pub fn normalize_mut(&mut self) { match self { - TypeThunk::Untyped(th) | TypeThunk::Typed(th, _) => { + TypedThunk::Untyped(th) | TypedThunk::Typed(th, _) => { th.normalize_mut() } - TypeThunk::Const(_) => {} + TypedThunk::Const(_) => {} } } pub fn get_type(&self) -> Result, TypeError> { match self { - TypeThunk::Untyped(_) => Err(TypeError::new( + TypedThunk::Untyped(_) => Err(TypeError::new( &TypecheckContext::new(), TypeMessage::Untyped, )), - TypeThunk::Typed(_, t) => Ok(Cow::Borrowed(t)), - TypeThunk::Const(c) => Ok(Cow::Owned(type_of_const(*c)?)), + TypedThunk::Typed(_, t) => Ok(Cow::Borrowed(t)), + TypedThunk::Const(c) => Ok(Cow::Owned(type_of_const(*c)?)), } } } @@ -319,15 +319,17 @@ impl Shift for ThunkInternal { } } -impl Shift for TypeThunk { +impl Shift for TypedThunk { fn shift(&self, delta: isize, var: &AlphaVar) -> Option { Some(match self { - TypeThunk::Untyped(th) => TypeThunk::Untyped(th.shift(delta, var)?), - TypeThunk::Typed(th, t) => TypeThunk::Typed( + TypedThunk::Untyped(th) => { + TypedThunk::Untyped(th.shift(delta, var)?) + } + TypedThunk::Typed(th, t) => TypedThunk::Typed( th.shift(delta, var)?, Box::new(t.shift(delta, var)?), ), - TypeThunk::Const(c) => TypeThunk::Const(*c), + TypedThunk::Const(c) => TypedThunk::Const(*c), }) } } @@ -365,17 +367,17 @@ impl Subst for ThunkInternal { } } -impl Subst for TypeThunk { +impl Subst for TypedThunk { fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { match self { - TypeThunk::Untyped(th) => { - TypeThunk::Untyped(th.subst_shift(var, val)) + TypedThunk::Untyped(th) => { + TypedThunk::Untyped(th.subst_shift(var, val)) } - TypeThunk::Typed(th, t) => TypeThunk::Typed( + TypedThunk::Typed(th, t) => TypedThunk::Typed( th.subst_shift(var, val), Box::new(t.subst_shift(var, val)), ), - TypeThunk::Const(c) => TypeThunk::Const(*c), + TypedThunk::Const(c) => TypedThunk::Const(*c), } } } @@ -387,9 +389,9 @@ impl std::cmp::PartialEq for Thunk { } impl std::cmp::Eq for Thunk {} -impl std::cmp::PartialEq for TypeThunk { +impl std::cmp::PartialEq for TypedThunk { fn eq(&self, other: &Self) -> bool { self.to_value() == other.to_value() } } -impl std::cmp::Eq for TypeThunk {} +impl std::cmp::Eq for TypedThunk {} diff --git a/dhall/src/core/value.rs b/dhall/src/core/value.rs index 0b68bf6..8486d6e 100644 --- a/dhall/src/core/value.rs +++ b/dhall/src/core/value.rs @@ -5,7 +5,7 @@ use dhall_syntax::{ NaiveDouble, Natural, X, }; -use crate::core::thunk::{Thunk, TypeThunk}; +use crate::core::thunk::{Thunk, TypedThunk}; use crate::core::var::{AlphaLabel, AlphaVar, Shift, Subst}; use crate::phase::normalize::{ apply_builtin, normalize_one_layer, squash_textlit, OutputSubExpr, @@ -26,15 +26,15 @@ use crate::phase::Typed; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Value { /// Closures - Lam(AlphaLabel, TypeThunk, Thunk), - Pi(AlphaLabel, TypeThunk, TypeThunk), + Lam(AlphaLabel, TypedThunk, Thunk), + Pi(AlphaLabel, TypedThunk, TypedThunk), // Invariant: the evaluation must not be able to progress further. AppliedBuiltin(Builtin, Vec), /// `λ(x: a) -> Some x` - OptionalSomeClosure(TypeThunk), + OptionalSomeClosure(TypedThunk), /// `λ(x : a) -> λ(xs : List a) -> [ x ] # xs` /// `λ(xs : List a) -> [ x ] # xs` - ListConsClosure(TypeThunk, Option), + ListConsClosure(TypedThunk, Option), /// `λ(x : Natural) -> x + 1` NaturalSuccClosure, @@ -44,20 +44,20 @@ pub enum Value { NaturalLit(Natural), IntegerLit(Integer), DoubleLit(NaiveDouble), - EmptyOptionalLit(TypeThunk), + EmptyOptionalLit(TypedThunk), NEOptionalLit(Thunk), // EmptyListLit(t) means `[] : List t`, not `[] : t` - EmptyListLit(TypeThunk), + EmptyListLit(TypedThunk), NEListLit(Vec), RecordLit(HashMap), - RecordType(HashMap), - UnionType(HashMap>), - UnionConstructor(Label, HashMap>), - UnionLit(Label, Thunk, HashMap>), + RecordType(HashMap), + UnionType(HashMap>), + UnionConstructor(Label, HashMap>), + UnionLit(Label, Thunk, HashMap>), // Invariant: this must not contain interpolations that are themselves TextLits, and // contiguous text values must be merged. TextLit(Vec>), - Equivalence(TypeThunk, TypeThunk), + Equivalence(TypedThunk, TypedThunk), // Invariant: this must not contain a value captured by one of the variants above. PartialExpr(ExprF), } diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs index 0b05227..27a6901 100644 --- a/dhall/src/phase/mod.rs +++ b/dhall/src/phase/mod.rs @@ -4,7 +4,7 @@ use std::path::Path; use dhall_syntax::{Const, Import, Span, SubExpr, X}; -use crate::core::thunk::{Thunk, TypeThunk}; +use crate::core::thunk::{Thunk, TypedThunk}; use crate::core::value::Value; use crate::core::var::{AlphaVar, Shift, Subst}; use crate::error::{EncodeError, Error, ImportError, TypeError}; @@ -31,7 +31,7 @@ pub struct Resolved(ResolvedSubExpr); /// A typed expression #[derive(Debug, Clone)] -pub struct Typed(TypeThunk); +pub struct Typed(TypedThunk); /// A normalized expression. /// @@ -100,18 +100,18 @@ impl Typed { } pub fn from_thunk_and_type(th: Thunk, t: Type) -> Self { - Typed(TypeThunk::from_thunk_and_type(th, t)) + Typed(TypedThunk::from_thunk_and_type(th, t)) } pub fn from_thunk_untyped(th: Thunk) -> Self { - Typed(TypeThunk::from_thunk_untyped(th)) + Typed(TypedThunk::from_thunk_untyped(th)) } pub fn from_const(c: Const) -> Self { - Typed(TypeThunk::from_const(c)) + Typed(TypedThunk::from_const(c)) } pub fn from_value_untyped(v: Value) -> Self { - Typed(TypeThunk::from_value_untyped(v)) + Typed(TypedThunk::from_value_untyped(v)) } - pub fn from_typethunk(th: TypeThunk) -> Self { + pub fn from_typethunk(th: TypedThunk) -> Self { Typed(th) } @@ -135,7 +135,7 @@ impl Typed { pub fn into_type(self) -> Type { self } - pub fn into_typethunk(self) -> TypeThunk { + pub fn into_typethunk(self) -> TypedThunk { self.0 } pub fn to_normalized(&self) -> Normalized { diff --git a/dhall/src/phase/normalize.rs b/dhall/src/phase/normalize.rs index 405677a..644f98c 100644 --- a/dhall/src/phase/normalize.rs +++ b/dhall/src/phase/normalize.rs @@ -6,7 +6,7 @@ use dhall_syntax::{ }; use crate::core::context::NormalizationContext; -use crate::core::thunk::{Thunk, TypeThunk}; +use crate::core::thunk::{Thunk, TypedThunk}; use crate::core::value::Value; use crate::core::var::Subst; use crate::phase::{NormalizedSubExpr, ResolvedSubExpr, Typed}; @@ -22,7 +22,7 @@ pub fn apply_builtin(b: Builtin, args: Vec) -> Value { // Return Ok((unconsumed args, returned value)), or Err(()) if value could not be produced. let ret = match (b, args.as_slice()) { (OptionalNone, [t, r..]) => { - Ok((r, EmptyOptionalLit(TypeThunk::from_thunk(t.clone())))) + Ok((r, EmptyOptionalLit(TypedThunk::from_thunk(t.clone())))) } (NaturalIsZero, [n, r..]) => match &*n.as_value() { NaturalLit(n) => Ok((r, BoolLit(*n == 0))), @@ -144,10 +144,10 @@ pub fn apply_builtin(b: Builtin, args: Vec) -> Value { let mut kts = HashMap::new(); kts.insert( "index".into(), - TypeThunk::from_value(Value::from_builtin(Natural)), + TypedThunk::from_value(Value::from_builtin(Natural)), ); kts.insert("value".into(), t.clone()); - Ok((r, EmptyListLit(TypeThunk::from_value(RecordType(kts))))) + Ok((r, EmptyListLit(TypedThunk::from_value(RecordType(kts))))) } NEListLit(xs) => { let xs = xs @@ -179,10 +179,10 @@ pub fn apply_builtin(b: Builtin, args: Vec) -> Value { r, f.app_val(Value::from_builtin(List).app_thunk(t.clone())) .app_val(ListConsClosure( - TypeThunk::from_thunk(t.clone()), + TypedThunk::from_thunk(t.clone()), None, )) - .app_val(EmptyListLit(TypeThunk::from_thunk(t.clone()))), + .app_val(EmptyListLit(TypedThunk::from_thunk(t.clone()))), )), }, (ListFold, [_, l, _, cons, nil, r..]) => match &*l.as_value() { @@ -213,10 +213,10 @@ pub fn apply_builtin(b: Builtin, args: Vec) -> Value { _ => Ok(( r, f.app_val(Value::from_builtin(Optional).app_thunk(t.clone())) - .app_val(OptionalSomeClosure(TypeThunk::from_thunk( + .app_val(OptionalSomeClosure(TypedThunk::from_thunk( t.clone(), ))) - .app_val(EmptyOptionalLit(TypeThunk::from_thunk( + .app_val(EmptyOptionalLit(TypedThunk::from_thunk( t.clone(), ))), )), @@ -618,7 +618,7 @@ fn apply_binop<'a>(o: BinOp, x: &'a Thunk, y: &'a Thunk) -> Option> { } (RecursiveRecordTypeMerge, RecordType(kvs1), RecordType(kvs2)) => { let kvs = merge_maps(kvs1, kvs2, |v1, v2| { - TypeThunk::from_thunk(Thunk::from_partial_expr(ExprF::BinOp( + TypedThunk::from_thunk(Thunk::from_partial_expr(ExprF::BinOp( RecursiveRecordTypeMerge, v1.to_thunk(), v2.to_thunk(), @@ -628,8 +628,8 @@ fn apply_binop<'a>(o: BinOp, x: &'a Thunk, y: &'a Thunk) -> Option> { } (Equivalence, _, _) => Ret::Value(Value::Equivalence( - TypeThunk::from_thunk(x.clone()), - TypeThunk::from_thunk(y.clone()), + TypedThunk::from_thunk(x.clone()), + TypedThunk::from_thunk(y.clone()), )), _ => return None, @@ -649,12 +649,12 @@ pub fn normalize_one_layer(expr: ExprF) -> Value { ExprF::Annot(x, _) => Ret::Thunk(x), ExprF::Assert(_) => Ret::Expr(expr), ExprF::Lam(x, t, e) => { - Ret::Value(Lam(x.into(), TypeThunk::from_thunk(t), e)) + Ret::Value(Lam(x.into(), TypedThunk::from_thunk(t), e)) } ExprF::Pi(x, t, e) => Ret::Value(Pi( x.into(), - TypeThunk::from_thunk(t), - TypeThunk::from_thunk(e), + TypedThunk::from_thunk(t), + TypedThunk::from_thunk(e), )), ExprF::Let(x, _, v, b) => { let v = Typed::from_thunk_untyped(v); @@ -673,7 +673,7 @@ pub fn normalize_one_layer(expr: ExprF) -> Value { let t_borrow = t.as_value(); match &*t_borrow { AppliedBuiltin(Builtin::List, args) if args.len() == 1 => { - Ret::Value(EmptyListLit(TypeThunk::from_thunk( + Ret::Value(EmptyListLit(TypedThunk::from_thunk( args[0].clone(), ))) } @@ -691,12 +691,12 @@ pub fn normalize_one_layer(expr: ExprF) -> Value { } ExprF::RecordType(kts) => Ret::Value(RecordType( kts.into_iter() - .map(|(k, t)| (k, TypeThunk::from_thunk(t))) + .map(|(k, t)| (k, TypedThunk::from_thunk(t))) .collect(), )), ExprF::UnionType(kts) => Ret::Value(UnionType( kts.into_iter() - .map(|(k, t)| (k, t.map(|t| TypeThunk::from_thunk(t)))) + .map(|(k, t)| (k, t.map(|t| TypedThunk::from_thunk(t)))) .collect(), )), ExprF::TextLit(elts) => { diff --git a/dhall/src/phase/typecheck.rs b/dhall/src/phase/typecheck.rs index 299997a..a19e1ca 100644 --- a/dhall/src/phase/typecheck.rs +++ b/dhall/src/phase/typecheck.rs @@ -6,7 +6,7 @@ use dhall_syntax::{ }; use crate::core::context::{NormalizationContext, TypecheckContext}; -use crate::core::thunk::{Thunk, TypeThunk}; +use crate::core::thunk::{Thunk, TypedThunk}; use crate::core::value::Value; use crate::core::var::{Shift, Subst}; use crate::error::{TypeError, TypeMessage}; @@ -44,8 +44,12 @@ fn tck_pi_type( let k = function_check(ka, kb); Ok(Typed::from_thunk_and_type( - Value::Pi(x.into(), TypeThunk::from_type(tx), TypeThunk::from_type(te)) - .into_thunk(), + Value::Pi( + x.into(), + TypedThunk::from_type(tx), + TypedThunk::from_type(te), + ) + .into_thunk(), Type::from_const(k), )) } @@ -77,7 +81,7 @@ fn tck_record_type( return Err(TypeError::new(ctx, RecordTypeDuplicateField)) } Entry::Vacant(_) => { - entry.or_insert_with(|| TypeThunk::from_type(t.clone())) + entry.or_insert_with(|| TypedThunk::from_type(t.clone())) } }; } @@ -119,7 +123,7 @@ fn tck_union_type( return Err(TypeError::new(ctx, UnionTypeDuplicateField)) } Entry::Vacant(_) => entry.or_insert_with(|| { - t.as_ref().map(|t| TypeThunk::from_type(t.clone())) + t.as_ref().map(|t| TypedThunk::from_type(t.clone())) }), }; } @@ -334,7 +338,7 @@ fn type_with( let b = type_with(&ctx2, b.clone())?; let v = Value::Lam( x.clone().into(), - TypeThunk::from_type(tx.clone()), + TypedThunk::from_type(tx.clone()), b.to_thunk(), ); let tb = b.get_type()?.into_owned(); @@ -658,8 +662,8 @@ fn type_last_layer( // records of records when merging. fn combine_record_types( ctx: &TypecheckContext, - kts_l: HashMap, - kts_r: HashMap, + kts_l: HashMap, + kts_r: HashMap, ) -> Result { use crate::phase::normalize::outer_join; @@ -667,8 +671,8 @@ fn type_last_layer( // are records themselves, then we hit the recursive case. // Otherwise we have a field collision. let combine = |k: &Label, - inner_l: &TypeThunk, - inner_r: &TypeThunk| + inner_l: &TypedThunk, + inner_r: &TypedThunk| -> Result { match (inner_l.to_value(), inner_r.to_value()) { ( @@ -686,7 +690,9 @@ fn type_last_layer( let kts: HashMap> = outer_join( |l| Ok(l.to_type()), |r| Ok(r.to_type()), - |k: &Label, l: &TypeThunk, r: &TypeThunk| combine(k, l, r), + |k: &Label, l: &TypedThunk, r: &TypedThunk| { + combine(k, l, r) + }, &kts_l, &kts_r, ); @@ -729,8 +735,8 @@ fn type_last_layer( // records of records when merging. fn combine_record_types( ctx: &TypecheckContext, - kts_l: HashMap, - kts_r: HashMap, + kts_l: HashMap, + kts_r: HashMap, ) -> Result { use crate::phase::normalize::intersection_with_key; @@ -738,8 +744,8 @@ fn type_last_layer( // are records themselves, then we hit the recursive case. // Otherwise we have a field collision. let combine = |k: &Label, - kts_l_inner: &TypeThunk, - kts_r_inner: &TypeThunk| + kts_l_inner: &TypedThunk, + kts_r_inner: &TypedThunk| -> Result { match (kts_l_inner.to_value(), kts_r_inner.to_value()) { ( @@ -755,7 +761,9 @@ fn type_last_layer( }; let kts = intersection_with_key( - |k: &Label, l: &TypeThunk, r: &TypeThunk| combine(k, l, r), + |k: &Label, l: &TypedThunk, r: &TypedThunk| { + combine(k, l, r) + }, &kts_l, &kts_r, ); diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 19aabec..14d8b47 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -124,7 +124,7 @@ pub use value::Value; // A Dhall value. pub mod value { - use dhall::core::thunk::{Thunk, TypeThunk}; + use dhall::core::thunk::{Thunk, TypedThunk}; use dhall::core::value::Value as DhallValue; use dhall::phase::{NormalizedSubExpr, Parsed, Type, Typed}; use dhall_syntax::Builtin; @@ -185,7 +185,7 @@ pub mod value { ) -> Self { Self::from_dhall_value(DhallValue::RecordType( kts.map(|(k, t)| { - (k.into(), TypeThunk::from_thunk(t.to_thunk())) + (k.into(), TypedThunk::from_thunk(t.to_thunk())) }) .collect(), )) @@ -196,7 +196,7 @@ pub mod value { ) -> Self { Self::from_dhall_value(DhallValue::UnionType( kts.map(|(k, t)| { - (k.into(), t.map(|t| TypeThunk::from_thunk(t.to_thunk()))) + (k.into(), t.map(|t| TypedThunk::from_thunk(t.to_thunk()))) }) .collect(), )) -- cgit v1.2.3 From cbd62bb57bcc94e0133c57437488a5af22a0b1c2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 17:34:38 +0200 Subject: Typos --- dhall/src/core/thunk.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dhall/src/core/thunk.rs b/dhall/src/core/thunk.rs index 38c54f7..c18014e 100644 --- a/dhall/src/core/thunk.rs +++ b/dhall/src/core/thunk.rs @@ -49,9 +49,9 @@ pub enum TypedThunk { // Any value, along with (optionally) its type Untyped(Thunk), Typed(Thunk, Box), - // One of the base higher-kinded typed. + // One of the base higher-kinded types. // Used to avoid storing the same tower ot Type->Kind->Sort - // over and over again. Also enables having Sort as a type + // over and over again. Also enables having Sort as a value // even though it doesn't itself have a type. Const(Const), } -- cgit v1.2.3 From 5895c3aa6552f75d7e5202be561f9734fe8945e7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 19:31:23 +0200 Subject: No need to track the absence of `Span`s at the type level --- dhall/src/phase/binary.rs | 45 +++++++------- dhall/src/phase/mod.rs | 10 +-- dhall/src/phase/parse.rs | 4 +- dhall/src/phase/typecheck.rs | 16 ++--- dhall_syntax/src/core/expr.rs | 131 +++++++++++++++++++-------------------- dhall_syntax/src/core/visitor.rs | 40 ++++-------- dhall_syntax/src/parser.rs | 39 ++---------- dhall_syntax/src/printer.rs | 12 ++-- serde_dhall/src/serde.rs | 9 ++- 9 files changed, 126 insertions(+), 180 deletions(-) diff --git a/dhall/src/phase/binary.rs b/dhall/src/phase/binary.rs index f4a9cc1..3292617 100644 --- a/dhall/src/phase/binary.rs +++ b/dhall/src/phase/binary.rs @@ -631,14 +631,12 @@ where ImportLocation::Remote(url) => { match &url.headers { None => ser_seq.serialize_element(&Null)?, - Some(location_hashed) => { - ser_seq.serialize_element(&self::Serialize::Expr( - &SubExpr::from_expr_no_note(ExprF::Embed(Import { - mode: ImportMode::Code, - location_hashed: location_hashed.as_ref().clone(), - })), - ))? - } + Some(location_hashed) => ser_seq.serialize_element( + &self::Serialize::Expr(&rc(ExprF::Embed(Import { + mode: ImportMode::Code, + location_hashed: location_hashed.as_ref().clone(), + }))), + )?, }; ser_seq.serialize_element(&url.authority)?; for p in &url.path { @@ -689,13 +687,13 @@ impl<'a> serde::ser::Serialize for Serialize<'a> { } } -fn collect_nested_applications<'a, N, E>( - e: &'a SubExpr, -) -> (&'a SubExpr, Vec<&'a SubExpr>) { - fn go<'a, N, E>( - e: &'a SubExpr, - vec: &mut Vec<&'a SubExpr>, - ) -> &'a SubExpr { +fn collect_nested_applications<'a, E>( + e: &'a SubExpr, +) -> (&'a SubExpr, Vec<&'a SubExpr>) { + fn go<'a, E>( + e: &'a SubExpr, + vec: &mut Vec<&'a SubExpr>, + ) -> &'a SubExpr { match e.as_ref() { ExprF::App(f, a) => { vec.push(a); @@ -709,16 +707,15 @@ fn collect_nested_applications<'a, N, E>( (e, vec) } -type LetBinding<'a, N, E> = - (&'a Label, &'a Option>, &'a SubExpr); +type LetBinding<'a, E> = (&'a Label, &'a Option>, &'a SubExpr); -fn collect_nested_lets<'a, N, E>( - e: &'a SubExpr, -) -> (&'a SubExpr, Vec>) { - fn go<'a, N, E>( - e: &'a SubExpr, - vec: &mut Vec>, - ) -> &'a SubExpr { +fn collect_nested_lets<'a, E>( + e: &'a SubExpr, +) -> (&'a SubExpr, Vec>) { + fn go<'a, E>( + e: &'a SubExpr, + vec: &mut Vec>, + ) -> &'a SubExpr { match e.as_ref() { ExprF::Let(l, t, v, e) => { vec.push((l, t, v)); diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs index 27a6901..8c93889 100644 --- a/dhall/src/phase/mod.rs +++ b/dhall/src/phase/mod.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::fmt::Display; use std::path::Path; -use dhall_syntax::{Const, Import, Span, SubExpr, X}; +use dhall_syntax::{Const, Import, SubExpr, X}; use crate::core::thunk::{Thunk, TypedThunk}; use crate::core::value::Value; @@ -17,10 +17,10 @@ pub(crate) mod parse; pub(crate) mod resolve; pub(crate) mod typecheck; -pub type ParsedSubExpr = SubExpr; -pub type DecodedSubExpr = SubExpr; -pub type ResolvedSubExpr = SubExpr; -pub type NormalizedSubExpr = SubExpr; +pub type ParsedSubExpr = SubExpr; +pub type DecodedSubExpr = SubExpr; +pub type ResolvedSubExpr = SubExpr; +pub type NormalizedSubExpr = SubExpr; #[derive(Debug, Clone)] pub struct Parsed(ParsedSubExpr, ImportRoot); diff --git a/dhall/src/phase/parse.rs b/dhall/src/phase/parse.rs index 734f6e1..9f3f2f4 100644 --- a/dhall/src/phase/parse.rs +++ b/dhall/src/phase/parse.rs @@ -25,7 +25,7 @@ pub fn parse_str(s: &str) -> Result { pub fn parse_binary(data: &[u8]) -> Result { let expr = crate::phase::binary::decode(data)?; let root = ImportRoot::LocalDir(std::env::current_dir()?); - Ok(Parsed(expr.note_absurd(), root)) + Ok(Parsed(expr, root)) } pub fn parse_binary_file(f: &Path) -> Result { @@ -33,5 +33,5 @@ pub fn parse_binary_file(f: &Path) -> Result { File::open(f)?.read_to_end(&mut buffer)?; let expr = crate::phase::binary::decode(&buffer)?; let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); - Ok(Parsed(expr.note_absurd(), root)) + Ok(Parsed(expr, root)) } diff --git a/dhall/src/phase/typecheck.rs b/dhall/src/phase/typecheck.rs index a19e1ca..ecf9793 100644 --- a/dhall/src/phase/typecheck.rs +++ b/dhall/src/phase/typecheck.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use dhall_syntax::{ - rc, Builtin, Const, Expr, ExprF, InterpolatedTextContents, Label, Span, - SubExpr, X, + rc, Builtin, Const, Expr, ExprF, InterpolatedTextContents, Label, SubExpr, + X, }; use crate::core::context::{NormalizationContext, TypecheckContext}; @@ -214,7 +214,7 @@ macro_rules! make_type { }; } -fn type_of_builtin(b: Builtin) -> Expr { +fn type_of_builtin(b: Builtin) -> Expr { use dhall_syntax::Builtin::*; match b { Bool | Natural | Integer | Double | Text => make_type!(Type), @@ -303,7 +303,7 @@ fn type_of_builtin(b: Builtin) -> Expr { /// and turn it into a type, typechecking it along the way. pub fn mktype( ctx: &TypecheckContext, - e: SubExpr, + e: SubExpr, ) -> Result { Ok(type_with(ctx, e)?.to_type()) } @@ -326,7 +326,7 @@ enum Ret { /// normalized as well. fn type_with( ctx: &TypecheckContext, - e: SubExpr, + e: SubExpr, ) -> Result { use dhall_syntax::ExprF::{Annot, Embed, Lam, Let, Pi, Var}; @@ -1002,7 +1002,7 @@ fn type_last_layer( /// `typeOf` is the same as `type_with` with an empty context, meaning that the /// expression must be closed (i.e. no free variables), otherwise type-checking /// will fail. -fn type_of(e: SubExpr) -> Result { +fn type_of(e: SubExpr) -> Result { let ctx = TypecheckContext::new(); let e = type_with(&ctx, e)?; // Ensure `e` has a type (i.e. `e` is not `Sort`) @@ -1015,8 +1015,8 @@ pub fn typecheck(e: Resolved) -> Result { } pub fn typecheck_with(e: Resolved, ty: &Type) -> Result { - let expr: SubExpr<_, _> = e.0; - let ty: SubExpr<_, _> = ty.to_expr().absurd(); + let expr: SubExpr<_> = e.0; + let ty: SubExpr<_> = ty.to_expr().absurd(); type_of(expr.rewrap(ExprF::Annot(expr.clone(), ty))) } pub fn skip_typecheck(e: Resolved) -> Typed { diff --git a/dhall_syntax/src/core/expr.rs b/dhall_syntax/src/core/expr.rs index 6522cb1..0cbece3 100644 --- a/dhall_syntax/src/core/expr.rs +++ b/dhall_syntax/src/core/expr.rs @@ -19,6 +19,30 @@ pub fn trivial_result(x: Result) -> T { } } +/// A location in the source text +#[derive(Debug, Clone)] +pub struct Span { + input: Rc, + /// # Safety + /// + /// Must be a valid character boundary index into `input`. + start: usize, + /// # Safety + /// + /// Must be a valid character boundary index into `input`. + end: usize, +} + +impl Span { + pub(crate) fn make(input: Rc, sp: pest::Span) -> Self { + Span { + input, + start: sp.start(), + end: sp.end(), + } + } +} + /// Double with bitwise equality #[derive(Debug, Copy, Clone)] pub struct NaiveDouble(f64); @@ -137,23 +161,19 @@ pub enum Builtin { TextShow, } -pub type ParsedExpr = SubExpr; -pub type ResolvedExpr = SubExpr; -pub type DhallExpr = ResolvedExpr; - // Each node carries an annotation. In practice it's either X (no annotation) or a Span. #[derive(Debug)] -pub struct SubExpr(Rc<(Expr, Option)>); +pub struct SubExpr(Rc<(Expr, Option)>); -impl std::cmp::PartialEq for SubExpr { +impl std::cmp::PartialEq for SubExpr { fn eq(&self, other: &Self) -> bool { self.0.as_ref().0 == other.0.as_ref().0 } } -impl std::cmp::Eq for SubExpr {} +impl std::cmp::Eq for SubExpr {} -pub type Expr = ExprF, Embed>; +pub type Expr = ExprF, Embed>; /// Syntax tree for expressions // Having the recursion out of the enum definition enables writing @@ -293,31 +313,22 @@ impl ExprF { } } -impl Expr { +impl Expr { fn traverse_embed( &self, visit_embed: impl FnMut(&E) -> Result, - ) -> Result, Err> - where - N: Clone, - { + ) -> Result, Err> { self.visit(&mut visitor::TraverseEmbedVisitor(visit_embed)) } - fn map_embed(&self, mut map_embed: impl FnMut(&E) -> E2) -> Expr - where - N: Clone, - { + fn map_embed(&self, mut map_embed: impl FnMut(&E) -> E2) -> Expr { trivial_result(self.traverse_embed(|x| Ok(map_embed(x)))) } pub fn traverse_resolve( &self, visit_embed: impl FnMut(&E) -> Result, - ) -> Result, Err> - where - N: Clone, - { + ) -> Result, Err> { self.traverse_resolve_with_visitor(&mut visitor::ResolveVisitor( visit_embed, )) @@ -326,9 +337,8 @@ impl Expr { pub(crate) fn traverse_resolve_with_visitor( &self, visitor: &mut visitor::ResolveVisitor, - ) -> Result, Err> + ) -> Result, Err> where - N: Clone, F1: FnMut(&E) -> Result, { match self { @@ -341,59 +351,44 @@ impl Expr { } } -impl Expr { - pub fn absurd(&self) -> Expr { +impl Expr { + pub fn absurd(&self) -> Expr { self.visit(&mut visitor::AbsurdVisitor) } } -impl Expr { - pub fn note_absurd(&self) -> Expr { - self.visit(&mut visitor::NoteAbsurdVisitor) - } -} - -impl SubExpr { - pub fn as_ref(&self) -> &Expr { +impl SubExpr { + pub fn as_ref(&self) -> &Expr { &self.0.as_ref().0 } - pub fn new(x: Expr, n: N) -> Self { + pub fn new(x: Expr, n: Span) -> Self { SubExpr(Rc::new((x, Some(n)))) } - pub fn from_expr_no_note(x: Expr) -> Self { + pub fn from_expr_no_span(x: Expr) -> Self { SubExpr(Rc::new((x, None))) } pub fn from_builtin(b: Builtin) -> Self { - SubExpr::from_expr_no_note(ExprF::Builtin(b)) + SubExpr::from_expr_no_span(ExprF::Builtin(b)) } - pub fn rewrap(&self, x: Expr) -> SubExpr - where - N: Clone, - { + pub fn rewrap(&self, x: Expr) -> SubExpr { SubExpr(Rc::new((x, (self.0).1.clone()))) } pub fn traverse_embed( &self, visit_embed: impl FnMut(&E) -> Result, - ) -> Result, Err> - where - N: Clone, - { + ) -> Result, Err> { Ok(self.rewrap(self.as_ref().traverse_embed(visit_embed)?)) } pub fn map_embed( &self, map_embed: impl FnMut(&E) -> E2, - ) -> SubExpr - where - N: Clone, - { + ) -> SubExpr { self.rewrap(self.as_ref().map_embed(map_embed)) } @@ -401,10 +396,7 @@ impl SubExpr { &'a self, map_expr: impl FnMut(&'a Self) -> Self, map_under_binder: impl FnMut(&'a Label, &'a Self) -> Self, - ) -> Self - where - N: Clone, - { + ) -> Self { match self.as_ref() { ExprF::Embed(_) => SubExpr::clone(self), // This calls ExprF::map_ref @@ -419,35 +411,38 @@ impl SubExpr { pub fn traverse_resolve( &self, visit_embed: impl FnMut(&E) -> Result, - ) -> Result, Err> - where - N: Clone, - { + ) -> Result, Err> { Ok(self.rewrap(self.as_ref().traverse_resolve(visit_embed)?)) } } -impl SubExpr { - pub fn absurd(&self) -> SubExpr { - SubExpr::from_expr_no_note(self.as_ref().absurd()) - } -} - -impl SubExpr { - pub fn note_absurd(&self) -> SubExpr { - SubExpr::from_expr_no_note(self.as_ref().note_absurd()) +impl SubExpr { + pub fn absurd(&self) -> SubExpr { + SubExpr::from_expr_no_span(self.as_ref().absurd()) } } -impl Clone for SubExpr { +impl Clone for SubExpr { fn clone(&self) -> Self { SubExpr(Rc::clone(&self.0)) } } // Should probably rename this -pub fn rc(x: Expr) -> SubExpr { - SubExpr::from_expr_no_note(x) +pub fn rc(x: Expr) -> SubExpr { + SubExpr::from_expr_no_span(x) +} + +pub(crate) fn spanned( + span: Span, + x: crate::parser::ParsedExpr, +) -> crate::parser::ParsedSubExpr { + SubExpr::new(x, span) +} +pub(crate) fn unspanned( + x: crate::parser::ParsedExpr, +) -> crate::parser::ParsedSubExpr { + SubExpr::from_expr_no_span(x) } /// Add an isize to an usize diff --git a/dhall_syntax/src/core/visitor.rs b/dhall_syntax/src/core/visitor.rs index 18f76d9..50ec68a 100644 --- a/dhall_syntax/src/core/visitor.rs +++ b/dhall_syntax/src/core/visitor.rs @@ -336,19 +336,18 @@ where pub struct TraverseEmbedVisitor(pub F1); -impl<'a, 'b, N, E, E2, Err, F1> - ExprFFallibleVisitor<'a, SubExpr, SubExpr, E, E2> +impl<'a, 'b, E, E2, Err, F1> + ExprFFallibleVisitor<'a, SubExpr, SubExpr, E, E2> for &'b mut TraverseEmbedVisitor where - N: Clone + 'a, F1: FnMut(&E) -> Result, { type Error = Err; fn visit_subexpr( &mut self, - subexpr: &'a SubExpr, - ) -> Result, Self::Error> { + subexpr: &'a SubExpr, + ) -> Result, Self::Error> { Ok(subexpr.rewrap(subexpr.as_ref().visit(&mut **self)?)) } fn visit_embed(self, embed: &'a E) -> Result { @@ -358,19 +357,18 @@ where pub struct ResolveVisitor(pub F1); -impl<'a, 'b, N, E, E2, Err, F1> - ExprFFallibleVisitor<'a, SubExpr, SubExpr, E, E2> +impl<'a, 'b, E, E2, Err, F1> + ExprFFallibleVisitor<'a, SubExpr, SubExpr, E, E2> for &'b mut ResolveVisitor where - N: Clone + 'a, F1: FnMut(&E) -> Result, { type Error = Err; fn visit_subexpr( &mut self, - subexpr: &'a SubExpr, - ) -> Result, Self::Error> { + subexpr: &'a SubExpr, + ) -> Result, Self::Error> { Ok(subexpr.rewrap( subexpr .as_ref() @@ -381,30 +379,14 @@ where (self.0)(embed) } } -pub struct NoteAbsurdVisitor; - -impl<'a, 'b, N, E> - ExprFInFallibleVisitor<'a, SubExpr, SubExpr, E, E> - for &'b mut NoteAbsurdVisitor -where - E: Clone + 'a, -{ - fn visit_subexpr(&mut self, subexpr: &'a SubExpr) -> SubExpr { - SubExpr::from_expr_no_note(subexpr.as_ref().visit(&mut **self)) - } - fn visit_embed(self, embed: &'a E) -> E { - E::clone(embed) - } -} pub struct AbsurdVisitor; -impl<'a, 'b, N, E> - ExprFInFallibleVisitor<'a, SubExpr, SubExpr, X, E> +impl<'a, 'b, E> ExprFInFallibleVisitor<'a, SubExpr, SubExpr, X, E> for &'b mut AbsurdVisitor { - fn visit_subexpr(&mut self, subexpr: &'a SubExpr) -> SubExpr { - SubExpr::from_expr_no_note(subexpr.as_ref().visit(&mut **self)) + fn visit_subexpr(&mut self, subexpr: &'a SubExpr) -> SubExpr { + SubExpr::from_expr_no_span(subexpr.as_ref().visit(&mut **self)) } fn visit_embed(self, embed: &'a X) -> E { match *embed {} diff --git a/dhall_syntax/src/parser.rs b/dhall_syntax/src/parser.rs index 9ae6dc8..1ab6e6d 100644 --- a/dhall_syntax/src/parser.rs +++ b/dhall_syntax/src/parser.rs @@ -15,46 +15,15 @@ use crate::*; // their own crate because they are quite general and useful. For now they // are here and hopefully you can figure out how they work. -type ParsedExpr = Expr; -type ParsedSubExpr = SubExpr; -type ParsedText = InterpolatedText>; -type ParsedTextContents = InterpolatedTextContents>; +pub(crate) type ParsedExpr = Expr; +pub(crate) type ParsedSubExpr = SubExpr; +type ParsedText = InterpolatedText>; +type ParsedTextContents = InterpolatedTextContents>; pub type ParseError = pest::error::Error; pub type ParseResult = Result; -fn unspanned(x: ParsedExpr) -> ParsedSubExpr { - SubExpr::from_expr_no_note(x) -} - -#[derive(Debug, Clone)] -pub struct Span { - input: Rc, - /// # Safety - /// - /// Must be a valid character boundary index into `input`. - start: usize, - /// # Safety - /// - /// Must be a valid character boundary index into `input`. - end: usize, -} - -impl Span { - fn make(input: Rc, sp: pest::Span) -> Self { - Span { - input, - start: sp.start(), - end: sp.end(), - } - } -} - -fn spanned(span: Span, x: ParsedExpr) -> ParsedSubExpr { - SubExpr::new(x, span) -} - #[derive(Debug)] enum Either { Left(A), diff --git a/dhall_syntax/src/printer.rs b/dhall_syntax/src/printer.rs index 70b224e..95eafe5 100644 --- a/dhall_syntax/src/printer.rs +++ b/dhall_syntax/src/printer.rs @@ -111,21 +111,21 @@ enum PrintPhase { // Wraps an Expr with a phase, so that phase selsction can be done // separate from the actual printing #[derive(Clone)] -struct PhasedExpr<'a, S, A>(&'a SubExpr, PrintPhase); +struct PhasedExpr<'a, A>(&'a SubExpr, PrintPhase); -impl<'a, S: Clone, A: Display + Clone> Display for PhasedExpr<'a, S, A> { +impl<'a, A: Display + Clone> Display for PhasedExpr<'a, A> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { self.0.as_ref().fmt_phase(f, self.1) } } -impl<'a, S: Clone, A: Display + Clone> PhasedExpr<'a, S, A> { - fn phase(self, phase: PrintPhase) -> PhasedExpr<'a, S, A> { +impl<'a, A: Display + Clone> PhasedExpr<'a, A> { + fn phase(self, phase: PrintPhase) -> PhasedExpr<'a, A> { PhasedExpr(self.0, phase) } } -impl Expr { +impl Expr { fn fmt_phase( &self, f: &mut fmt::Formatter, @@ -196,7 +196,7 @@ impl Expr { } } -impl Display for SubExpr { +impl Display for SubExpr { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { self.as_ref().fmt_phase(f, PrintPhase::Base) } diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 10d5a17..d891127 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -1,7 +1,10 @@ +use std::borrow::Cow; + +use dhall::phase::NormalizedSubExpr; +use dhall_syntax::ExprF; + use crate::de::{Deserialize, Error, Result}; use crate::Value; -use dhall_syntax::{ExprF, SubExpr, X}; -use std::borrow::Cow; impl<'a, T> Deserialize for T where @@ -12,7 +15,7 @@ where } } -struct Deserializer<'a>(Cow<'a, SubExpr>); +struct Deserializer<'a>(Cow<'a, NormalizedSubExpr>); impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> { type Deserializer = Deserializer<'a>; -- cgit v1.2.3 From 77af0bbc171618f48531cc6b1d77e18089928885 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 20:54:15 +0200 Subject: Stop tracking the absence of Embed values at the type level --- dhall/src/core/thunk.rs | 12 ++++++------ dhall/src/core/value.rs | 10 +++++----- dhall/src/phase/mod.rs | 4 ++-- dhall/src/phase/normalize.rs | 21 +++++++++++---------- dhall/src/phase/typecheck.rs | 11 ++++------- dhall_syntax/src/core/context.rs | 2 +- dhall_syntax/src/core/expr.rs | 16 ++-------------- dhall_syntax/src/core/visitor.rs | 15 +-------------- dhall_syntax/src/printer.rs | 6 ------ 9 files changed, 32 insertions(+), 65 deletions(-) diff --git a/dhall/src/core/thunk.rs b/dhall/src/core/thunk.rs index c18014e..4d193f9 100644 --- a/dhall/src/core/thunk.rs +++ b/dhall/src/core/thunk.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::cell::{Ref, RefCell}; use std::rc::Rc; -use dhall_syntax::{Const, ExprF, X}; +use dhall_syntax::{Const, ExprF}; use crate::core::context::{NormalizationContext, TypecheckContext}; use crate::core::value::Value; @@ -12,7 +12,7 @@ use crate::phase::normalize::{ apply_any, normalize_one_layer, normalize_whnf, InputSubExpr, OutputSubExpr, }; use crate::phase::typecheck::type_of_const; -use crate::phase::{NormalizedSubExpr, Type, Typed}; +use crate::phase::{Normalized, NormalizedSubExpr, Type, Typed}; #[derive(Debug, Clone, Copy)] enum Marker { @@ -30,7 +30,7 @@ enum ThunkInternal { /// Partially normalized value whose subexpressions have been thunked (this is returned from /// typechecking). Note that this is different from `Value::PartialExpr` because there is no /// requirement of WHNF here. - PartialExpr(ExprF), + PartialExpr(ExprF), /// Partially normalized value. /// Invariant: if the marker is `NF`, the value must be fully normalized Value(Marker, Value), @@ -123,7 +123,7 @@ impl Thunk { ThunkInternal::Value(WHNF, v).into_thunk() } - pub fn from_partial_expr(e: ExprF) -> Thunk { + pub fn from_partial_expr(e: ExprF) -> Thunk { ThunkInternal::PartialExpr(e).into_thunk() } @@ -309,7 +309,7 @@ impl Shift for ThunkInternal { e.traverse_ref_with_special_handling_of_binders( |v| Ok(v.shift(delta, var)?), |x, v| Ok(v.shift(delta, &var.under_binder(x))?), - |x| Ok(X::clone(x)), + |x| Ok(Normalized::clone(x)), )?, ), ThunkInternal::Value(m, v) => { @@ -356,7 +356,7 @@ impl Subst for ThunkInternal { &val.under_binder(x), ) }, - X::clone, + Normalized::clone, ), ), ThunkInternal::Value(_, v) => { diff --git a/dhall/src/core/value.rs b/dhall/src/core/value.rs index 8486d6e..88d7a20 100644 --- a/dhall/src/core/value.rs +++ b/dhall/src/core/value.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use dhall_syntax::{ rc, Builtin, Const, ExprF, Integer, InterpolatedTextContents, Label, - NaiveDouble, Natural, X, + NaiveDouble, Natural, }; use crate::core::thunk::{Thunk, TypedThunk}; @@ -10,7 +10,7 @@ use crate::core::var::{AlphaLabel, AlphaVar, Shift, Subst}; use crate::phase::normalize::{ apply_builtin, normalize_one_layer, squash_textlit, OutputSubExpr, }; -use crate::phase::Typed; +use crate::phase::{Normalized, Typed}; /// A semantic value. The invariants ensure this value represents a Weak-Head /// Normal Form (WHNF). This means that this first constructor is the first constructor of the @@ -59,7 +59,7 @@ pub enum Value { TextLit(Vec>), Equivalence(TypedThunk, TypedThunk), // Invariant: this must not contain a value captured by one of the variants above. - PartialExpr(ExprF), + PartialExpr(ExprF), } impl Value { @@ -475,7 +475,7 @@ impl Shift for Value { e.traverse_ref_with_special_handling_of_binders( |v| Ok(v.shift(delta, var)?), |x, v| Ok(v.shift(delta, &var.under_binder(x))?), - |x| Ok(X::clone(x)), + |x| Ok(Normalized::clone(x)), )?, ), }) @@ -500,7 +500,7 @@ impl Subst for Value { &val.under_binder(x), ) }, - X::clone, + Normalized::clone, )) } // Retry normalizing since substituting may allow progress diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs index 8c93889..7364949 100644 --- a/dhall/src/phase/mod.rs +++ b/dhall/src/phase/mod.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::fmt::Display; use std::path::Path; -use dhall_syntax::{Const, Import, SubExpr, X}; +use dhall_syntax::{Const, Import, SubExpr}; use crate::core::thunk::{Thunk, TypedThunk}; use crate::core::value::Value; @@ -20,7 +20,7 @@ pub(crate) mod typecheck; pub type ParsedSubExpr = SubExpr; pub type DecodedSubExpr = SubExpr; pub type ResolvedSubExpr = SubExpr; -pub type NormalizedSubExpr = SubExpr; +pub type NormalizedSubExpr = SubExpr; #[derive(Debug, Clone)] pub struct Parsed(ParsedSubExpr, ImportRoot); diff --git a/dhall/src/phase/normalize.rs b/dhall/src/phase/normalize.rs index 644f98c..f1045a5 100644 --- a/dhall/src/phase/normalize.rs +++ b/dhall/src/phase/normalize.rs @@ -2,14 +2,14 @@ use std::collections::HashMap; use dhall_syntax::{ BinOp, Builtin, ExprF, InterpolatedText, InterpolatedTextContents, - NaiveDouble, X, + NaiveDouble, }; use crate::core::context::NormalizationContext; use crate::core::thunk::{Thunk, TypedThunk}; use crate::core::value::Value; use crate::core::var::Subst; -use crate::phase::{NormalizedSubExpr, ResolvedSubExpr, Typed}; +use crate::phase::{Normalized, NormalizedSubExpr, ResolvedSubExpr, Typed}; pub type InputSubExpr = ResolvedSubExpr; pub type OutputSubExpr = NormalizedSubExpr; @@ -86,7 +86,7 @@ pub fn apply_builtin(b: Builtin, args: Vec) -> Value { // Empty string literal. [] => { // Printing InterpolatedText takes care of all the escaping - let txt: InterpolatedText = + let txt: InterpolatedText = std::iter::empty().collect(); let s = txt.to_string(); Ok(( @@ -98,10 +98,11 @@ pub fn apply_builtin(b: Builtin, args: Vec) -> Value { // interpolations, there is a single Text item) in the literal. [InterpolatedTextContents::Text(s)] => { // Printing InterpolatedText takes care of all the escaping - let txt: InterpolatedText = std::iter::once( - InterpolatedTextContents::Text(s.clone()), - ) - .collect(); + let txt: InterpolatedText = + std::iter::once(InterpolatedTextContents::Text( + s.clone(), + )) + .collect(); let s = txt.to_string(); Ok(( r, @@ -376,7 +377,7 @@ pub fn normalize_whnf(ctx: NormalizationContext, expr: InputSubExpr) -> Value { } // Thunk subexpressions - let expr: ExprF = + let expr: ExprF = expr.as_ref().map_ref_with_special_handling_of_binders( |e| Thunk::new(ctx.clone(), e.clone()), |x, e| Thunk::new(ctx.skip(x), e.clone()), @@ -391,7 +392,7 @@ enum Ret<'a> { Value(Value), Thunk(Thunk), ThunkRef(&'a Thunk), - Expr(ExprF), + Expr(ExprF), } /// Performs an intersection of two HashMaps. @@ -636,7 +637,7 @@ fn apply_binop<'a>(o: BinOp, x: &'a Thunk, y: &'a Thunk) -> Option> { }) } -pub fn normalize_one_layer(expr: ExprF) -> Value { +pub fn normalize_one_layer(expr: ExprF) -> Value { use Value::{ AppliedBuiltin, BoolLit, DoubleLit, EmptyListLit, IntegerLit, Lam, NEListLit, NEOptionalLit, NaturalLit, Pi, RecordLit, RecordType, diff --git a/dhall/src/phase/typecheck.rs b/dhall/src/phase/typecheck.rs index ecf9793..96ff246 100644 --- a/dhall/src/phase/typecheck.rs +++ b/dhall/src/phase/typecheck.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use dhall_syntax::{ rc, Builtin, Const, Expr, ExprF, InterpolatedTextContents, Label, SubExpr, - X, }; use crate::core::context::{NormalizationContext, TypecheckContext}; @@ -214,7 +213,7 @@ macro_rules! make_type { }; } -fn type_of_builtin(b: Builtin) -> Expr { +fn type_of_builtin(b: Builtin) -> Expr { use dhall_syntax::Builtin::*; match b { Bool | Natural | Integer | Double | Text => make_type!(Type), @@ -398,7 +397,7 @@ fn type_with( /// layer. fn type_last_layer( ctx: &TypecheckContext, - e: &ExprF, + e: &ExprF, ) -> Result { use crate::error::TypeMessage::*; use dhall_syntax::BinOp::*; @@ -598,9 +597,7 @@ fn type_last_layer( } } Const(c) => Ok(RetWhole(Typed::from_const(*c))), - Builtin(b) => { - Ok(RetTypeOnly(mktype(ctx, rc(type_of_builtin(*b)).absurd())?)) - } + Builtin(b) => Ok(RetTypeOnly(mktype(ctx, rc(type_of_builtin(*b)))?)), BoolLit(_) => Ok(RetTypeOnly(builtin_to_type(Bool)?)), NaturalLit(_) => Ok(RetTypeOnly(builtin_to_type(Natural)?)), IntegerLit(_) => Ok(RetTypeOnly(builtin_to_type(Integer)?)), @@ -1016,7 +1013,7 @@ pub fn typecheck(e: Resolved) -> Result { pub fn typecheck_with(e: Resolved, ty: &Type) -> Result { let expr: SubExpr<_> = e.0; - let ty: SubExpr<_> = ty.to_expr().absurd(); + let ty: SubExpr<_> = ty.to_expr(); type_of(expr.rewrap(ExprF::Annot(expr.clone(), ty))) } pub fn skip_typecheck(e: Resolved) -> Typed { diff --git a/dhall_syntax/src/core/context.rs b/dhall_syntax/src/core/context.rs index eeec121..6844baa 100644 --- a/dhall_syntax/src/core/context.rs +++ b/dhall_syntax/src/core/context.rs @@ -4,7 +4,7 @@ use std::hash::Hash; /// A `(Context a)` associates `Text` labels with values of type `a` /// -/// The `Context` is used for type-checking when `(a = Expr X)` +/// The `Context` is used for type-checking when `(a = Expr)` /// /// * You create a `Context` using `empty` and `insert` /// * You transform a `Context` using `fmap` diff --git a/dhall_syntax/src/core/expr.rs b/dhall_syntax/src/core/expr.rs index 0cbece3..e08d816 100644 --- a/dhall_syntax/src/core/expr.rs +++ b/dhall_syntax/src/core/expr.rs @@ -10,9 +10,9 @@ pub type Double = NaiveDouble; /// An empty type #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum X {} +pub enum Void {} -pub fn trivial_result(x: Result) -> T { +pub fn trivial_result(x: Result) -> T { match x { Ok(x) => x, Err(e) => match e {}, @@ -351,12 +351,6 @@ impl Expr { } } -impl Expr { - pub fn absurd(&self) -> Expr { - self.visit(&mut visitor::AbsurdVisitor) - } -} - impl SubExpr { pub fn as_ref(&self) -> &Expr { &self.0.as_ref().0 @@ -416,12 +410,6 @@ impl SubExpr { } } -impl SubExpr { - pub fn absurd(&self) -> SubExpr { - SubExpr::from_expr_no_span(self.as_ref().absurd()) - } -} - impl Clone for SubExpr { fn clone(&self) -> Self { SubExpr(Rc::clone(&self.0)) diff --git a/dhall_syntax/src/core/visitor.rs b/dhall_syntax/src/core/visitor.rs index 50ec68a..e39e95b 100644 --- a/dhall_syntax/src/core/visitor.rs +++ b/dhall_syntax/src/core/visitor.rs @@ -243,7 +243,7 @@ impl<'a, T, SE1, SE2, E1, E2> ExprFFallibleVisitor<'a, SE1, SE2, E1, E2> where T: ExprFInFallibleVisitor<'a, SE1, SE2, E1, E2>, { - type Error = X; + type Error = Void; fn visit_subexpr(&mut self, subexpr: &'a SE1) -> Result { Ok(self.0.visit_subexpr(subexpr)) @@ -379,16 +379,3 @@ where (self.0)(embed) } } - -pub struct AbsurdVisitor; - -impl<'a, 'b, E> ExprFInFallibleVisitor<'a, SubExpr, SubExpr, X, E> - for &'b mut AbsurdVisitor -{ - fn visit_subexpr(&mut self, subexpr: &'a SubExpr) -> SubExpr { - SubExpr::from_expr_no_span(subexpr.as_ref().visit(&mut **self)) - } - fn visit_embed(self, embed: &'a X) -> E { - match *embed {} - } -} diff --git a/dhall_syntax/src/printer.rs b/dhall_syntax/src/printer.rs index 95eafe5..25d4ca8 100644 --- a/dhall_syntax/src/printer.rs +++ b/dhall_syntax/src/printer.rs @@ -491,9 +491,3 @@ impl Display for V { }; // Annotate subexpressions with the appropriate phase, defaulting to Base - let phased_self = match self.map_ref_simple(|e| PhasedExpr(e, Base)) { + let phased_self = match self.map_ref(|e| PhasedExpr(e, Base)) { Pi(a, b, c) => { if &String::from(&a) == "_" { Pi(a, b.phase(Operator), c) -- cgit v1.2.3 From 07956ccb1daf4a6819f64776f70b6f5f26869184 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 21:38:17 +0200 Subject: Cleanup visitor code --- dhall_syntax/src/core/visitor.rs | 193 +++++++++++---------------------------- 1 file changed, 54 insertions(+), 139 deletions(-) diff --git a/dhall_syntax/src/core/visitor.rs b/dhall_syntax/src/core/visitor.rs index 647f76f..f2f3b32 100644 --- a/dhall_syntax/src/core/visitor.rs +++ b/dhall_syntax/src/core/visitor.rs @@ -2,52 +2,56 @@ use crate::*; use std::iter::FromIterator; /// A way too generic Visitor trait. -pub trait GenericVisitor: Sized { - fn visit(self, input: Input) -> Return; +pub trait GenericVisitor: Sized { + fn visit(self, input: Input) -> Output; } -/// A visitor trait that can be used to traverse `ExprF`s. We need this pattern -/// so that Rust lets us have as much mutability as we can. -/// For example, `traverse_embed` cannot be made using only `traverse_ref`, because -/// `traverse_ref` takes a `FnMut` so we would need to pass multiple mutable -/// reverences to this argument to `traverse_ref`. But Rust's ownership system -/// is all about preventing exactly this ! So we have to be more clever. -/// The visitor pattern allows us to have only one mutable thing the whole -/// time: the visitor itself. The visitor can then carry around multiple closures -/// or just one, and Rust is ok with either. See for example TraverseRefVisitor -/// and TraverseEmbedVisitor. -/// This is very generic. For a more legible trait, see ExprFInFallibleVisitor -pub trait ExprFVeryGenericVisitor<'a, Ret, SE1, E1>: Sized { +/// A visitor trait that can be used to traverse `ExprF`s. We need this pattern so that Rust lets +/// us have as much mutability as we can. +/// For example, `traverse_ref_with_special_handling_of_binders` cannot be made using only +/// `traverse_ref`, because `traverse_ref` takes a `FnMut` so we would need to pass multiple +/// mutable reverences to this argument to `traverse_ref`. But Rust's ownership system is all about +/// preventing exactly this ! So we have to be more clever. The visitor pattern allows us to have +/// only one mutable thing the whole time: the visitor itself. The visitor can then carry around +/// multiple closures or just one, and Rust is ok with either. See for example TraverseRefVisitor. +pub trait ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>: Sized { type Error; - type SE2; - type E2; - fn visit_subexpr( - &mut self, - subexpr: &'a SE1, - ) -> Result; + fn visit_subexpr(&mut self, subexpr: &'a SE1) -> Result; + fn visit_embed(self, embed: &'a E1) -> Result; fn visit_subexpr_under_binder( - self, - label: &'a Label, + mut self, + _label: &'a Label, subexpr: &'a SE1, - ) -> Result; + ) -> Result { + self.visit_subexpr(subexpr) + } +} - fn visit_embed_squash(self, embed: &'a E1) -> Result; +/// Like ExprFFallibleVisitor, but without the error handling. +pub trait ExprFInFallibleVisitor<'a, SE1, SE2, E1, E2>: Sized { + fn visit_subexpr(&mut self, subexpr: &'a SE1) -> SE2; + fn visit_embed(self, embed: &'a E1) -> E2; - // Called with the result of the map, in the non-embed case. - // Useful to change the result type, and/or avoid some loss of info - fn visit_resulting_exprf( - result: ExprF, - ) -> Result; + fn visit_subexpr_under_binder( + mut self, + _label: &'a Label, + subexpr: &'a SE1, + ) -> SE2 { + self.visit_subexpr(subexpr) + } } -impl<'a, T, Ret, SE1, E1> - GenericVisitor<&'a ExprF, Result> for T +impl<'a, T, SE1, SE2, E1, E2> + GenericVisitor<&'a ExprF, Result, T::Error>> for T where - T: ExprFVeryGenericVisitor<'a, Ret, SE1, E1>, + T: ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>, { - fn visit(self, input: &'a ExprF) -> Result { + fn visit( + self, + input: &'a ExprF, + ) -> Result, T::Error> { fn vec<'a, T, U, Err, F: FnMut(&'a T) -> Result>( x: &'a [T], f: F, @@ -63,27 +67,27 @@ where None => None, }) } - fn dupmap<'a, V, Ret, SE, E, T>( - x: impl IntoIterator, + fn dupmap<'a, V, SE1, SE2, E1, E2, T>( + x: impl IntoIterator, mut v: V, ) -> Result where - SE: 'a, - T: FromIterator<(Label, V::SE2)>, - V: ExprFVeryGenericVisitor<'a, Ret, SE, E>, + SE1: 'a, + T: FromIterator<(Label, SE2)>, + V: ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>, { x.into_iter() .map(|(k, x)| Ok((k.clone(), v.visit_subexpr(x)?))) .collect() } - fn optdupmap<'a, V, Ret, SE, E, T>( - x: impl IntoIterator)>, + fn optdupmap<'a, V, SE1, SE2, E1, E2, T>( + x: impl IntoIterator)>, mut v: V, ) -> Result where - SE: 'a, - T: FromIterator<(Label, Option)>, - V: ExprFVeryGenericVisitor<'a, Ret, SE, E>, + SE1: 'a, + T: FromIterator<(Label, Option)>, + V: ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>, { x.into_iter() .map(|(k, x)| { @@ -100,7 +104,7 @@ where let mut v = self; use crate::ExprF::*; - T::visit_resulting_exprf(match input { + Ok(match input { Var(v) => Var(v.clone()), Lam(l, t, e) => { let t = v.visit_subexpr(t)?; @@ -149,90 +153,18 @@ where Field(e, l) => Field(v.visit_subexpr(e)?, l.clone()), Projection(e, ls) => Projection(v.visit_subexpr(e)?, ls.clone()), Assert(e) => Assert(v.visit_subexpr(e)?), - Embed(a) => return v.visit_embed_squash(a), + Embed(a) => Embed(v.visit_embed(a)?), }) } } -/// Like ExprFVeryGenericVisitor, but sets the return -/// type to ExprF<_> -pub trait ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>: Sized { - type Error; - - fn visit_subexpr(&mut self, subexpr: &'a SE1) -> Result; - fn visit_embed(self, embed: &'a E1) -> Result; - - fn visit_subexpr_under_binder( - mut self, - _label: &'a Label, - subexpr: &'a SE1, - ) -> Result { - self.visit_subexpr(subexpr) - } - - fn visit_embed_squash( - self, - embed: &'a E1, - ) -> Result, Self::Error> { - Ok(ExprF::Embed(self.visit_embed(embed)?)) - } -} - -impl<'a, T, SE1, SE2, E1, E2> - ExprFVeryGenericVisitor<'a, ExprF, SE1, E1> for T +impl<'a, T, SE1, SE2, E1, E2> GenericVisitor<&'a ExprF, ExprF> + for T where - T: ExprFFallibleVisitor<'a, SE1, SE2, E1, E2>, + T: ExprFInFallibleVisitor<'a, SE1, SE2, E1, E2>, { - type Error = T::Error; - type SE2 = SE2; - type E2 = E2; - - fn visit_subexpr( - &mut self, - subexpr: &'a SE1, - ) -> Result { - self.visit_subexpr(subexpr) - } - - fn visit_subexpr_under_binder( - self, - label: &'a Label, - subexpr: &'a SE1, - ) -> Result { - self.visit_subexpr_under_binder(label, subexpr) - } - - fn visit_embed_squash( - self, - embed: &'a E1, - ) -> Result, Self::Error> { - self.visit_embed_squash(embed) - } - - // Called with the result of the map, in the non-embed case. - // Useful to change the result type, and/or avoid some loss of info - fn visit_resulting_exprf( - result: ExprF, - ) -> Result, Self::Error> { - Ok(result) - } -} - -/// Like ExprFFallibleVisitor, but without the error handling. -pub trait ExprFInFallibleVisitor<'a, SE1, SE2, E1, E2>: Sized { - fn visit_subexpr(&mut self, subexpr: &'a SE1) -> SE2; - fn visit_embed(self, embed: &'a E1) -> E2; - - fn visit_subexpr_under_binder( - mut self, - _label: &'a Label, - subexpr: &'a SE1, - ) -> SE2 { - self.visit_subexpr(subexpr) - } - - fn visit_embed_squash(self, embed: &'a E1) -> ExprF { - ExprF::Embed(self.visit_embed(embed)) + fn visit(self, input: &'a ExprF) -> ExprF { + trivial_result(InfallibleWrapper(self).visit(input)) } } @@ -259,23 +191,6 @@ where ) -> Result { Ok(self.0.visit_subexpr_under_binder(label, subexpr)) } - - fn visit_embed_squash( - self, - embed: &'a E1, - ) -> Result, Self::Error> { - Ok(self.0.visit_embed_squash(embed)) - } -} - -impl<'a, T, SE1, SE2, E1, E2> GenericVisitor<&'a ExprF, ExprF> - for T -where - T: ExprFInFallibleVisitor<'a, SE1, SE2, E1, E2>, -{ - fn visit(self, input: &'a ExprF) -> ExprF { - trivial_result(InfallibleWrapper(self).visit(input)) - } } pub struct TraverseRefWithBindersVisitor { -- cgit v1.2.3 From 8d45d633dfa60e8d64c9e6e742de4e33496bf0fa Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 22:11:45 +0200 Subject: Store Imports in their own node instead of in Embed --- dhall/src/phase/binary.rs | 24 ++++++++++++++---------- dhall/src/phase/mod.rs | 8 +++++--- dhall/src/phase/normalize.rs | 3 +++ dhall/src/phase/typecheck.rs | 3 +++ dhall_syntax/src/core/expr.rs | 15 ++++++++++++--- dhall_syntax/src/core/visitor.rs | 11 ++++++----- dhall_syntax/src/parser.rs | 14 +++++++------- dhall_syntax/src/printer.rs | 16 +++++++++++----- 8 files changed, 61 insertions(+), 33 deletions(-) diff --git a/dhall/src/phase/binary.rs b/dhall/src/phase/binary.rs index 3292617..b3a80aa 100644 --- a/dhall/src/phase/binary.rs +++ b/dhall/src/phase/binary.rs @@ -19,7 +19,6 @@ pub fn decode(data: &[u8]) -> Result { } } -//TODO: encode normalized expression too pub fn encode(expr: &ParsedSubExpr) -> Result, EncodeError> { serde_cbor::ser::to_vec(&Serialize::Expr(expr)) .map_err(|e| EncodeError::CBORError(e)) @@ -264,7 +263,7 @@ fn cbor_value_to_dhall( // TODO // Some(x) => { // match cbor_value_to_dhall(&x)?.as_ref() { - // Embed(import) => Some(Box::new( + // Import(import) => Some(Box::new( // import.location_hashed.clone(), // )), // _ => Err(DecodeError::WrongFormatError( @@ -340,7 +339,7 @@ fn cbor_value_to_dhall( "import/type".to_owned(), ))?, }; - Embed(Import { + Import(dhall_syntax::Import { mode, location_hashed: ImportHashed { hash, location }, }) @@ -573,7 +572,10 @@ where .chain(once(expr(x))) .chain(ls.iter().map(label)), ), - Embed(import) => serialize_import(ser, import), + Import(import) => serialize_import(ser, import), + Embed(_) => unimplemented!( + "An expression with resolved imports cannot be binary-encoded" + ), } } @@ -631,12 +633,14 @@ where ImportLocation::Remote(url) => { match &url.headers { None => ser_seq.serialize_element(&Null)?, - Some(location_hashed) => ser_seq.serialize_element( - &self::Serialize::Expr(&rc(ExprF::Embed(Import { - mode: ImportMode::Code, - location_hashed: location_hashed.as_ref().clone(), - }))), - )?, + Some(location_hashed) => { + ser_seq.serialize_element(&self::Serialize::Expr(&rc( + ExprF::Import(dhall_syntax::Import { + mode: ImportMode::Code, + location_hashed: location_hashed.as_ref().clone(), + }), + )))? + } }; ser_seq.serialize_element(&url.authority)?; for p in &url.path { diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs index 7364949..a1e3f29 100644 --- a/dhall/src/phase/mod.rs +++ b/dhall/src/phase/mod.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::fmt::Display; use std::path::Path; -use dhall_syntax::{Const, Import, SubExpr}; +use dhall_syntax::{Const, SubExpr, Void}; use crate::core::thunk::{Thunk, TypedThunk}; use crate::core::value::Value; @@ -17,8 +17,8 @@ pub(crate) mod parse; pub(crate) mod resolve; pub(crate) mod typecheck; -pub type ParsedSubExpr = SubExpr; -pub type DecodedSubExpr = SubExpr; +pub type ParsedSubExpr = SubExpr; +pub type DecodedSubExpr = SubExpr; pub type ResolvedSubExpr = SubExpr; pub type NormalizedSubExpr = SubExpr; @@ -26,6 +26,8 @@ pub type NormalizedSubExpr = SubExpr; pub struct Parsed(ParsedSubExpr, ImportRoot); /// An expression where all imports have been resolved +/// +/// Invariant: there must be no `Import` nodes or `ImportAlt` operations left. #[derive(Debug, Clone)] pub struct Resolved(ResolvedSubExpr); diff --git a/dhall/src/phase/normalize.rs b/dhall/src/phase/normalize.rs index 2970f5f..adbcb02 100644 --- a/dhall/src/phase/normalize.rs +++ b/dhall/src/phase/normalize.rs @@ -644,6 +644,9 @@ pub fn normalize_one_layer(expr: ExprF) -> Value { }; let ret = match expr { + ExprF::Import(_) => unreachable!( + "There should remain no imports in a resolved expression" + ), ExprF::Embed(_) => unreachable!(), ExprF::Var(_) => unreachable!(), ExprF::Annot(x, _) => Ret::Thunk(x), diff --git a/dhall/src/phase/typecheck.rs b/dhall/src/phase/typecheck.rs index e8b2544..89e2da8 100644 --- a/dhall/src/phase/typecheck.rs +++ b/dhall/src/phase/typecheck.rs @@ -407,6 +407,9 @@ fn type_last_layer( let mkerr = |msg: TypeMessage| TypeError::new(ctx, msg); match e { + Import(_) => unreachable!( + "There should remain no imports in a resolved expression" + ), Lam(_, _, _) | Pi(_, _, _) | Let(_, _, _, _) | Embed(_) | Var(_) => { unreachable!() } diff --git a/dhall_syntax/src/core/expr.rs b/dhall_syntax/src/core/expr.rs index 74eb993..4c809f8 100644 --- a/dhall_syntax/src/core/expr.rs +++ b/dhall_syntax/src/core/expr.rs @@ -12,6 +12,12 @@ pub type Double = NaiveDouble; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Void {} +impl std::fmt::Display for Void { + fn fmt(&self, _f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match *self {} + } +} + pub fn trivial_result(x: Result) -> T { match x { Ok(x) => x, @@ -233,7 +239,9 @@ pub enum ExprF { Field(SubExpr, Label), /// `e.{ x, y, z }` Projection(SubExpr, DupTreeSet { // Precedence is magically handled by the ordering of BinOps. ExprF::BinOp(op, _, _) if phase > PrintPhase::BinOp(*op) => true, ExprF::App(_, _) if phase > PrintPhase::App => true, - Field(_, _) | Projection(_, _) if phase > Import => true, + Field(_, _) | Projection(_, _) if phase > PrintPhase::Import => { + true + } _ => false, }; @@ -165,8 +168,8 @@ impl Expr { } } Merge(a, b, c) => Merge( - a.phase(Import), - b.phase(Import), + a.phase(PrintPhase::Import), + b.phase(PrintPhase::Import), c.map(|x| x.phase(PrintPhase::App)), ), Annot(a, b) => Annot(a.phase(Operator), b), @@ -175,8 +178,11 @@ impl Expr { a.phase(PrintPhase::BinOp(op)), b.phase(PrintPhase::BinOp(op)), ), - SomeLit(e) => SomeLit(e.phase(Import)), - ExprF::App(f, a) => ExprF::App(f.phase(Import), a.phase(Import)), + SomeLit(e) => SomeLit(e.phase(PrintPhase::Import)), + ExprF::App(f, a) => ExprF::App( + f.phase(PrintPhase::Import), + a.phase(PrintPhase::Import), + ), Field(a, b) => Field(a.phase(Primitive), b), Projection(e, ls) => Projection(e.phase(Primitive), ls), e => e, -- cgit v1.2.3 From 43094c8c52319aa82b03e73700ca1b5f65da13c6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 22:24:04 +0200 Subject: The syntax has very little sharing; no need for Rc anymore --- dhall_syntax/src/core/expr.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/dhall_syntax/src/core/expr.rs b/dhall_syntax/src/core/expr.rs index 4c809f8..e3f8cec 100644 --- a/dhall_syntax/src/core/expr.rs +++ b/dhall_syntax/src/core/expr.rs @@ -168,8 +168,8 @@ pub enum Builtin { } // Each node carries an annotation. In practice it's either X (no annotation) or a Span. -#[derive(Debug)] -pub struct SubExpr(Rc<(Expr, Option)>); +#[derive(Debug, Clone)] +pub struct SubExpr(Box<(Expr, Option)>); impl std::cmp::PartialEq for SubExpr { fn eq(&self, other: &Self) -> bool { @@ -336,11 +336,11 @@ impl SubExpr { } pub fn new(x: Expr, n: Span) -> Self { - SubExpr(Rc::new((x, Some(n)))) + SubExpr(Box::new((x, Some(n)))) } pub fn from_expr_no_span(x: Expr) -> Self { - SubExpr(Rc::new((x, None))) + SubExpr(Box::new((x, None))) } pub fn from_builtin(b: Builtin) -> Self { @@ -348,7 +348,7 @@ impl SubExpr { } pub fn rewrap(&self, x: Expr) -> SubExpr { - SubExpr(Rc::new((x, (self.0).1.clone()))) + SubExpr(Box::new((x, (self.0).1.clone()))) } } @@ -361,12 +361,6 @@ impl SubExpr { } } -impl Clone for SubExpr { - fn clone(&self) -> Self { - SubExpr(Rc::clone(&self.0)) - } -} - // Should probably rename this pub fn rc(x: Expr) -> SubExpr { SubExpr::from_expr_no_span(x) -- cgit v1.2.3 From c600bae72198350b78fe19cf993b7f4c6f35225a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 23:08:48 +0200 Subject: Implement Hash for ParsedSubExpr --- dhall/src/phase/binary.rs | 41 ++++++++++++++++++----------------------- dhall/src/phase/mod.rs | 16 ++++++++++++++++ dhall_syntax/src/core/expr.rs | 28 +++++++++++++++++++++++----- dhall_syntax/src/core/text.rs | 2 +- 4 files changed, 58 insertions(+), 29 deletions(-) diff --git a/dhall/src/phase/binary.rs b/dhall/src/phase/binary.rs index b3a80aa..9ed823c 100644 --- a/dhall/src/phase/binary.rs +++ b/dhall/src/phase/binary.rs @@ -10,7 +10,7 @@ use dhall_syntax::{ }; use crate::error::{DecodeError, EncodeError}; -use crate::phase::{DecodedSubExpr, ParsedSubExpr}; +use crate::phase::DecodedSubExpr; pub fn decode(data: &[u8]) -> Result { match serde_cbor::de::from_slice(data) { @@ -19,7 +19,7 @@ pub fn decode(data: &[u8]) -> Result { } } -pub fn encode(expr: &ParsedSubExpr) -> Result, EncodeError> { +pub fn encode(expr: &SubExpr) -> Result, EncodeError> { serde_cbor::ser::to_vec(&Serialize::Expr(expr)) .map_err(|e| EncodeError::CBORError(e)) } @@ -423,11 +423,11 @@ where .collect::>() } -enum Serialize<'a> { - Expr(&'a ParsedSubExpr), +enum Serialize<'a, E> { + Expr(&'a SubExpr), CBOR(cbor::Value), - RecordMap(&'a DupTreeMap), - UnionMap(&'a DupTreeMap>), + RecordMap(&'a DupTreeMap>), + UnionMap(&'a DupTreeMap>>), } macro_rules! count { @@ -447,7 +447,7 @@ macro_rules! ser_seq { }}; } -fn serialize_subexpr(ser: S, e: &ParsedSubExpr) -> Result +fn serialize_subexpr(ser: S, e: &SubExpr) -> Result where S: serde::ser::Serializer, { @@ -457,21 +457,14 @@ where use std::iter::once; use self::Serialize::{RecordMap, UnionMap}; - fn expr(x: &ParsedSubExpr) -> self::Serialize<'_> { + fn expr(x: &SubExpr) -> self::Serialize<'_, E> { self::Serialize::Expr(x) } - fn cbor<'a>(v: cbor::Value) -> self::Serialize<'a> { - self::Serialize::CBOR(v) - } - fn tag<'a>(x: u64) -> self::Serialize<'a> { - cbor(U64(x)) - } - fn null<'a>() -> self::Serialize<'a> { - cbor(cbor::Value::Null) - } - fn label<'a>(l: &Label) -> self::Serialize<'a> { - cbor(cbor::Value::String(l.into())) - } + let cbor = + |v: cbor::Value| -> self::Serialize<'_, E> { self::Serialize::CBOR(v) }; + let tag = |x: u64| cbor(U64(x)); + let null = || cbor(cbor::Value::Null); + let label = |l: &Label| cbor(cbor::Value::String(l.into())); match e.as_ref() { Const(c) => ser.serialize_str(&c.to_string()), @@ -634,12 +627,14 @@ where match &url.headers { None => ser_seq.serialize_element(&Null)?, Some(location_hashed) => { - ser_seq.serialize_element(&self::Serialize::Expr(&rc( + let e = rc( ExprF::Import(dhall_syntax::Import { mode: ImportMode::Code, location_hashed: location_hashed.as_ref().clone(), }), - )))? + ); + let s: Serialize<'_, ()> = self::Serialize::Expr(&e); + ser_seq.serialize_element(&s)? } }; ser_seq.serialize_element(&url.authority)?; @@ -665,7 +660,7 @@ where ser_seq.end() } -impl<'a> serde::ser::Serialize for Serialize<'a> { +impl<'a, E> serde::ser::Serialize for Serialize<'a, E> { fn serialize(&self, ser: S) -> Result where S: serde::ser::Serializer, diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs index a1e3f29..b73597c 100644 --- a/dhall/src/phase/mod.rs +++ b/dhall/src/phase/mod.rs @@ -157,6 +157,10 @@ impl Typed { } impl Normalized { + pub fn encode(&self) -> Result, EncodeError> { + crate::phase::binary::encode(&self.to_expr()) + } + #[allow(dead_code)] pub fn to_expr(&self) -> NormalizedSubExpr { self.0.to_expr() @@ -220,6 +224,18 @@ derive_traits_for_wrapper_struct!(Parsed); derive_traits_for_wrapper_struct!(Resolved); derive_traits_for_wrapper_struct!(Normalized); +impl std::hash::Hash for Normalized { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + match self.encode() { + Ok(vec) => vec.hash(state), + Err(_) => {} + } + } +} + impl Eq for Typed {} impl PartialEq for Typed { fn eq(&self, other: &Self) -> bool { diff --git a/dhall_syntax/src/core/expr.rs b/dhall_syntax/src/core/expr.rs index e3f8cec..9729efb 100644 --- a/dhall_syntax/src/core/expr.rs +++ b/dhall_syntax/src/core/expr.rs @@ -61,6 +61,15 @@ impl PartialEq for NaiveDouble { impl Eq for NaiveDouble {} +impl std::hash::Hash for NaiveDouble { + fn hash(&self, state: &mut H) + where + H: std::hash::Hasher, + { + self.0.to_bits().hash(state) + } +} + impl From for NaiveDouble { fn from(x: f64) -> Self { NaiveDouble(x) @@ -74,7 +83,7 @@ impl From for f64 { } /// Constants for a pure type system -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Const { Type, Kind, @@ -86,7 +95,7 @@ pub enum Const { /// The `Label` field is the variable's name (i.e. \"`x`\"). /// The `Int` field is a DeBruijn index. /// See dhall-lang/standard/semantics.md for details -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct V, PrintPhase); +struct PhasedExpr<'a, A>(&'a Expr, PrintPhase); impl<'a, A: Display + Clone> Display for PhasedExpr<'a, A> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { @@ -126,7 +126,7 @@ impl<'a, A: Display + Clone> PhasedExpr<'a, A> { } } -impl Expr { +impl RawExpr { fn fmt_phase( &self, f: &mut fmt::Formatter, @@ -202,7 +202,7 @@ impl Expr { } } -impl Display for SubExpr { +impl Display for Expr { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { self.as_ref().fmt_phase(f, PrintPhase::Base) } diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index ce3468f..9fce42d 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -124,7 +124,7 @@ pub use value::Value; // A Dhall value. pub mod value { - use dhall::phase::{NormalizedSubExpr, Parsed, Typed}; + use dhall::phase::{NormalizedExpr, Parsed, Typed}; use dhall_syntax::Builtin; use super::de::{Error, Result}; @@ -148,7 +148,7 @@ pub mod value { }; Ok(Value(typed)) } - pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + pub(crate) fn to_expr(&self) -> NormalizedExpr { self.0.to_expr() } pub(crate) fn as_typed(&self) -> &Typed { diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 94b7e6c..26708c1 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use dhall::phase::NormalizedSubExpr; +use dhall::phase::NormalizedExpr; use dhall_syntax::ExprF; use crate::de::{Deserialize, Error, Result}; @@ -15,7 +15,7 @@ where } } -struct Deserializer<'a>(Cow<'a, NormalizedSubExpr>); +struct Deserializer<'a>(Cow<'a, NormalizedExpr>); impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> { type Deserializer = Deserializer<'a>; -- cgit v1.2.3 From 55c127e70eb484df486a45b25bcf03479eebda10 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 28 Aug 2019 13:49:27 +0200 Subject: Cleanup conversion of `Value` to `Expr` --- dhall/src/core/value.rs | 34 +++++++--------- dhall/src/core/valuef.rs | 103 ++++++++++++++--------------------------------- dhall/src/phase/mod.rs | 31 +++++++++----- serde_dhall/src/lib.rs | 2 +- 4 files changed, 67 insertions(+), 103 deletions(-) diff --git a/dhall/src/core/value.rs b/dhall/src/core/value.rs index 49e30cd..3cccb1d 100644 --- a/dhall/src/core/value.rs +++ b/dhall/src/core/value.rs @@ -44,6 +44,15 @@ struct ValueInternal { #[derive(Clone)] pub(crate) struct Value(Rc>); +#[derive(Copy, Clone)] +/// Controls conversion from `Value` to `Expr` +pub(crate) struct ToExprOptions { + /// Whether to convert all variables to `_` + pub(crate) alpha: bool, + /// Whether to normalize before converting + pub(crate) normalize: bool, +} + impl ValueInternal { fn into_value(self) -> Value { Value(Rc::new(RefCell::new(self))) @@ -158,20 +167,12 @@ impl Value { self.normalize_whnf(); self.as_valuef() } - /// WARNING: drop this ref before normalizing the same value or you will run into BorrowMut - /// panics. - pub(crate) fn as_nf(&self) -> Ref { - self.normalize_nf(); - self.as_valuef() - } - // TODO: rename `normalize_to_expr` - pub(crate) fn to_expr(&self) -> NormalizedExpr { - self.as_whnf().normalize_to_expr() - } - // TODO: rename `normalize_to_expr_maybe_alpha` - pub(crate) fn to_expr_alpha(&self) -> NormalizedExpr { - self.as_whnf().normalize_to_expr_maybe_alpha(true) + pub(crate) fn to_expr(&self, opts: ToExprOptions) -> NormalizedExpr { + if opts.normalize { + self.normalize_whnf(); + } + self.as_valuef().to_expr(opts) } pub(crate) fn to_whnf_ignore_type(&self) -> ValueF { self.as_whnf().clone() @@ -223,13 +224,6 @@ impl Value { } } - pub(crate) fn normalize_to_expr_maybe_alpha( - &self, - alpha: bool, - ) -> NormalizedExpr { - self.as_nf().normalize_to_expr_maybe_alpha(alpha) - } - pub(crate) fn app(&self, v: Value) -> Value { let body_t = match &*self.get_type_not_sort().as_whnf() { ValueF::Pi(x, t, e) => { diff --git a/dhall/src/core/valuef.rs b/dhall/src/core/valuef.rs index 959f299..7ecec86 100644 --- a/dhall/src/core/valuef.rs +++ b/dhall/src/core/valuef.rs @@ -5,7 +5,7 @@ use dhall_syntax::{ NaiveDouble, Natural, }; -use crate::core::value::Value; +use crate::core::value::{ToExprOptions, Value}; use crate::core::var::{AlphaLabel, AlphaVar, Shift, Subst}; use crate::phase::{Normalized, NormalizedExpr}; @@ -51,38 +51,23 @@ impl ValueF { Value::from_valuef_and_type(self, t) } - /// Convert the value to a fully normalized syntactic expression - pub(crate) fn normalize_to_expr(&self) -> NormalizedExpr { - self.normalize_to_expr_maybe_alpha(false) - } - /// Convert the value to a fully normalized syntactic expression. Also alpha-normalize - /// if alpha is `true` - pub(crate) fn normalize_to_expr_maybe_alpha( - &self, - alpha: bool, - ) -> NormalizedExpr { + pub(crate) fn to_expr(&self, opts: ToExprOptions) -> NormalizedExpr { match self { ValueF::Lam(x, t, e) => rc(ExprF::Lam( - x.to_label_maybe_alpha(alpha), - t.normalize_to_expr_maybe_alpha(alpha), - e.normalize_to_expr_maybe_alpha(alpha), + x.to_label_maybe_alpha(opts.alpha), + t.to_expr(opts), + e.to_expr(opts), )), - ValueF::AppliedBuiltin(b, args) => { - let mut e = rc(ExprF::Builtin(*b)); - for v in args { - e = rc(ExprF::App( - e, - v.normalize_to_expr_maybe_alpha(alpha), - )); - } - e - } + ValueF::AppliedBuiltin(b, args) => args + .iter() + .map(|v| v.to_expr(opts)) + .fold(rc(ExprF::Builtin(*b)), |acc, v| rc(ExprF::App(acc, v))), ValueF::Pi(x, t, e) => rc(ExprF::Pi( - x.to_label_maybe_alpha(alpha), - t.normalize_to_expr_maybe_alpha(alpha), - e.normalize_to_expr_maybe_alpha(alpha), + x.to_label_maybe_alpha(opts.alpha), + t.to_expr(opts), + e.to_expr(opts), )), - ValueF::Var(v) => rc(ExprF::Var(v.to_var(alpha))), + ValueF::Var(v) => rc(ExprF::Var(v.to_var(opts.alpha))), ValueF::Const(c) => rc(ExprF::Const(*c)), ValueF::BoolLit(b) => rc(ExprF::BoolLit(*b)), ValueF::NaturalLit(n) => rc(ExprF::NaturalLit(*n)), @@ -90,73 +75,47 @@ impl ValueF { ValueF::DoubleLit(n) => rc(ExprF::DoubleLit(*n)), ValueF::EmptyOptionalLit(n) => rc(ExprF::App( rc(ExprF::Builtin(Builtin::OptionalNone)), - n.normalize_to_expr_maybe_alpha(alpha), + n.to_expr(opts), )), - ValueF::NEOptionalLit(n) => { - rc(ExprF::SomeLit(n.normalize_to_expr_maybe_alpha(alpha))) - } + ValueF::NEOptionalLit(n) => rc(ExprF::SomeLit(n.to_expr(opts))), ValueF::EmptyListLit(n) => rc(ExprF::EmptyListLit(rc(ExprF::App( rc(ExprF::Builtin(Builtin::List)), - n.normalize_to_expr_maybe_alpha(alpha), + n.to_expr(opts), )))), ValueF::NEListLit(elts) => rc(ExprF::NEListLit( - elts.iter() - .map(|n| n.normalize_to_expr_maybe_alpha(alpha)) - .collect(), + elts.iter().map(|n| n.to_expr(opts)).collect(), )), ValueF::RecordLit(kvs) => rc(ExprF::RecordLit( kvs.iter() - .map(|(k, v)| { - (k.clone(), v.normalize_to_expr_maybe_alpha(alpha)) - }) + .map(|(k, v)| (k.clone(), v.to_expr(opts))) .collect(), )), ValueF::RecordType(kts) => rc(ExprF::RecordType( kts.iter() - .map(|(k, v)| { - (k.clone(), v.normalize_to_expr_maybe_alpha(alpha)) - }) + .map(|(k, v)| (k.clone(), v.to_expr(opts))) .collect(), )), ValueF::UnionType(kts) => rc(ExprF::UnionType( kts.iter() .map(|(k, v)| { - ( - k.clone(), - v.as_ref().map(|v| { - v.normalize_to_expr_maybe_alpha(alpha) - }), - ) + (k.clone(), v.as_ref().map(|v| v.to_expr(opts))) }) .collect(), )), - ValueF::UnionConstructor(l, kts) => { - let kts = kts - .iter() - .map(|(k, v)| { - ( - k.clone(), - v.as_ref().map(|v| { - v.normalize_to_expr_maybe_alpha(alpha) - }), - ) - }) - .collect(); - rc(ExprF::Field(rc(ExprF::UnionType(kts)), l.clone())) - } + ValueF::UnionConstructor(l, kts) => rc(ExprF::Field( + ValueF::UnionType(kts.clone()).to_expr(opts), + l.clone(), + )), ValueF::UnionLit(l, v, kts) => rc(ExprF::App( - ValueF::UnionConstructor(l.clone(), kts.clone()) - .normalize_to_expr_maybe_alpha(alpha), - v.normalize_to_expr_maybe_alpha(alpha), + ValueF::UnionConstructor(l.clone(), kts.clone()).to_expr(opts), + v.to_expr(opts), )), ValueF::TextLit(elts) => { use InterpolatedTextContents::{Expr, Text}; rc(ExprF::TextLit( elts.iter() .map(|contents| match contents { - Expr(e) => { - Expr(e.normalize_to_expr_maybe_alpha(alpha)) - } + Expr(e) => Expr(e.to_expr(opts)), Text(s) => Text(s.clone()), }) .collect(), @@ -164,12 +123,10 @@ impl ValueF { } ValueF::Equivalence(x, y) => rc(ExprF::BinOp( dhall_syntax::BinOp::Equivalence, - x.normalize_to_expr_maybe_alpha(alpha), - y.normalize_to_expr_maybe_alpha(alpha), + x.to_expr(opts), + y.to_expr(opts), )), - ValueF::PartialExpr(e) => { - rc(e.map_ref(|v| v.normalize_to_expr_maybe_alpha(alpha))) - } + ValueF::PartialExpr(e) => rc(e.map_ref(|v| v.to_expr(opts))), } } diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs index ecc9213..2c5505c 100644 --- a/dhall/src/phase/mod.rs +++ b/dhall/src/phase/mod.rs @@ -3,7 +3,7 @@ use std::path::Path; use dhall_syntax::{Builtin, Const, Expr}; -use crate::core::value::Value; +use crate::core::value::{ToExprOptions, Value}; use crate::core::valuef::ValueF; use crate::core::var::{AlphaVar, Shift, Subst}; use crate::error::{EncodeError, Error, ImportError, TypeError}; @@ -71,7 +71,8 @@ impl Resolved { Ok(typecheck::typecheck(self.0)?.into_typed()) } pub fn typecheck_with(self, ty: &Typed) -> Result { - Ok(typecheck::typecheck_with(self.0, ty.to_expr())?.into_typed()) + Ok(typecheck::typecheck_with(self.0, ty.normalize_to_expr())? + .into_typed()) } } @@ -102,11 +103,23 @@ impl Typed { Typed::from_const(Const::Type) } - pub fn to_expr(&self) -> NormalizedExpr { - self.0.to_expr() - } - pub(crate) fn to_expr_alpha(&self) -> NormalizedExpr { - self.0.to_expr_alpha() + pub(crate) fn to_expr(&self) -> NormalizedExpr { + self.0.to_expr(ToExprOptions { + alpha: false, + normalize: false, + }) + } + pub fn normalize_to_expr(&self) -> NormalizedExpr { + self.0.to_expr(ToExprOptions { + alpha: false, + normalize: true, + }) + } + pub(crate) fn normalize_to_expr_alpha(&self) -> NormalizedExpr { + self.0.to_expr(ToExprOptions { + alpha: true, + normalize: true, + }) } pub(crate) fn to_value(&self) -> Value { self.0.clone() @@ -163,10 +176,10 @@ impl Normalized { } pub(crate) fn to_expr(&self) -> NormalizedExpr { - self.0.to_expr() + self.0.normalize_to_expr() } pub(crate) fn to_expr_alpha(&self) -> NormalizedExpr { - self.0.to_expr_alpha() + self.0.normalize_to_expr_alpha() } pub(crate) fn into_typed(self) -> Typed { self.0 diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 9fce42d..3d07539 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -149,7 +149,7 @@ pub mod value { Ok(Value(typed)) } pub(crate) fn to_expr(&self) -> NormalizedExpr { - self.0.to_expr() + self.0.normalize_to_expr() } pub(crate) fn as_typed(&self) -> &Typed { &self.0 -- cgit v1.2.3 From a2c2cd76d256a4e6ca66b9b1bd756fb17e600ef5 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 30 Aug 2019 20:05:12 +0200 Subject: Rework test harness to prepare for new types of tests --- dhall/build.rs | 483 ++++++++++++++++++++++++++++----------------- dhall/src/phase/resolve.rs | 13 +- dhall/src/tests.rs | 323 ++++++++++++++---------------- 3 files changed, 457 insertions(+), 362 deletions(-) diff --git a/dhall/build.rs b/dhall/build.rs index 166b036..4c654c9 100644 --- a/dhall/build.rs +++ b/dhall/build.rs @@ -2,77 +2,99 @@ use std::env; use std::ffi::OsString; use std::fs::File; use std::io::Write; -use std::path::Path; +use std::path::{Path, PathBuf}; use walkdir::WalkDir; +#[derive(Debug, Clone, Copy)] +enum FileType { + Text, + Binary, +} + +impl FileType { + fn to_ext(self) -> &'static str { + match self { + FileType::Text => "dhall", + FileType::Binary => "dhallb", + } + } +} + fn dhall_files_in_dir<'a>( dir: &'a Path, take_a_suffix: bool, + filetype: FileType, ) -> impl Iterator + 'a { WalkDir::new(dir) .into_iter() .filter_map(|e| e.ok()) .filter_map(move |path| { - let path = path.path(); - let path = path.strip_prefix(dir).unwrap(); - let ext = path.extension(); - if ext != Some(&OsString::from("dhall")) - && ext != Some(&OsString::from("dhallb")) - { + let path = path.path().strip_prefix(dir).unwrap(); + let ext = path.extension()?; + if ext != &OsString::from(filetype.to_ext()) { return None; } - let ext = ext.unwrap(); let path = path.to_string_lossy(); let path = &path[..path.len() - 1 - ext.len()]; - let path = if take_a_suffix { - if &path[path.len() - 1..] != "A" { - return None; - } else { - path[..path.len() - 1].to_owned() - } + let path = if take_a_suffix && &path[path.len() - 1..] != "A" { + return None; + } else if take_a_suffix { + path[..path.len() - 1].to_owned() } else { path.to_owned() }; + // Transform path into a valie Rust identifier let name = path.replace("/", "_").replace("-", "_"); Some((name, path)) }) } +#[derive(Debug, Clone)] +struct TestFeature { + /// Name of the module, used in the output of `cargo test` + module_name: &'static str, + /// Directory containing the tests files + directory: PathBuf, + /// Relevant variant of `dhall::tests::Test` + variant: &'static str, + /// Given a file name, whether to exclude it + path_filter: F, + /// Type of the input file + input_type: FileType, + /// Type of the output file, if any + output_type: Option, +} + fn make_test_module( w: &mut impl Write, // Where to output the generated code - mod_name: &str, // Name of the module, used in the output of `cargo test` - subdir: &str, // Directory containing the tests files - feature: &str, // Relevant variant of `dhall::tests::Feature` - mut exclude: impl FnMut(&str) -> bool, // Given a file name, whether to exclude it + mut feature: TestFeature bool>, ) -> std::io::Result<()> { - let all_tests_dir = Path::new("../dhall-lang/tests/"); - let tests_dir = all_tests_dir.join(subdir); - writeln!(w, "mod {} {{", mod_name)?; - for (name, path) in dhall_files_in_dir(&tests_dir.join("success/"), true) { - if exclude(&("success/".to_owned() + &path)) { + let tests_dir = feature.directory; + writeln!(w, "mod {} {{", feature.module_name)?; + let take_a_suffix = feature.output_type.is_some(); + for (name, path) in + dhall_files_in_dir(&tests_dir, take_a_suffix, feature.input_type) + { + if (feature.path_filter)(&path) { continue; } - writeln!( - w, - r#"make_spec_test!({}, Success, success_{}, "{}/success/{}");"#, - feature, - name, - tests_dir.to_string_lossy(), - path - )?; - } - for (name, path) in dhall_files_in_dir(&tests_dir.join("failure/"), false) { - if exclude(&("failure/".to_owned() + &path)) { - continue; - } - writeln!( - w, - r#"make_spec_test!({}, Failure, failure_{}, "{}/failure/{}");"#, - feature, - name, - tests_dir.to_string_lossy(), - path - )?; + let path = tests_dir.join(path); + let path = path.to_string_lossy(); + let test = match feature.output_type { + None => { + let input_file = + format!("\"{}.{}\"", path, feature.input_type.to_ext()); + format!("{}({})", feature.variant, input_file) + } + Some(output_type) => { + let input_file = + format!("\"{}A.{}\"", path, feature.input_type.to_ext()); + let output_file = + format!("\"{}B.{}\"", path, output_type.to_ext()); + format!("{}({}, {})", feature.variant, input_file, output_file) + } + }; + writeln!(w, "make_spec_test!({}, {});", test, name)?; } writeln!(w, "}}")?; Ok(()) @@ -88,177 +110,270 @@ fn main() -> std::io::Result<()> { let out_dir = env::var("OUT_DIR").unwrap(); let parser_tests_path = Path::new(&out_dir).join("spec_tests.rs"); + let spec_tests_dir = Path::new("../dhall-lang/tests/"); let mut file = File::create(parser_tests_path)?; - make_test_module(&mut file, "parse", "parser/", "Parser", |path| { - false - // Too slow in debug mode - || path == "success/largeExpression" - // TODO: projection by expression - || path == "success/recordProjectionByExpression" - || path == "success/RecordProjectionByType" - || path == "success/unit/RecordProjectionByType" - || path == "success/unit/RecordProjectionByTypeEmpty" - || path == "success/unit/RecordProjectFields" - // TODO: RFC3986 URLs - || path == "success/unit/import/urls/emptyPath0" - || path == "success/unit/import/urls/emptyPath1" - || path == "success/unit/import/urls/emptyPathSegment" - // TODO: toMap - || path == "success/toMap" - })?; + make_test_module( + &mut file, + TestFeature { + module_name: "parser_success", + directory: spec_tests_dir.join("parser/success/"), + variant: "ParserSuccess", + path_filter: |path: &str| { + false + // Too slow in debug mode + || path == "largeExpression" + // TODO: projection by expression + || path == "recordProjectionByExpression" + || path == "RecordProjectionByType" + || path == "unit/RecordProjectionByType" + || path == "unit/RecordProjectionByTypeEmpty" + || path == "unit/RecordProjectFields" + // TODO: RFC3986 URLs + || path == "unit/import/urls/emptyPath0" + || path == "unit/import/urls/emptyPath1" + || path == "unit/import/urls/emptyPathSegment" + // TODO: toMap + || path == "toMap" + }, + input_type: FileType::Text, + output_type: Some(FileType::Binary), + }, + )?; + + make_test_module( + &mut file, + TestFeature { + module_name: "parser_failure", + directory: spec_tests_dir.join("parser/failure/"), + variant: "ParserFailure", + path_filter: |_path: &str| false, + input_type: FileType::Text, + output_type: None, + }, + )?; + + make_test_module( + &mut file, + TestFeature { + module_name: "printer", + directory: spec_tests_dir.join("parser/success/"), + variant: "Printer", + path_filter: |path: &str| { + false + // Too slow in debug mode + || path == "largeExpression" + // TODO: projection by expression + || path == "recordProjectionByExpression" + || path == "RecordProjectionByType" + || path == "unit/RecordProjectionByType" + || path == "unit/RecordProjectionByTypeEmpty" + // TODO: RFC3986 URLs + || path == "unit/import/urls/emptyPath0" + || path == "unit/import/urls/emptyPath1" + || path == "unit/import/urls/emptyPathSegment" + // TODO: toMap + || path == "toMap" + }, + input_type: FileType::Text, + output_type: Some(FileType::Binary), + }, + )?; - make_test_module(&mut file, "printer", "parser/", "Printer", |path| { - // Failure tests are only for the parser - path.starts_with("failure/") - // Too slow in debug mode - || path == "success/largeExpression" - // TODO: projection by expression - || path == "success/recordProjectionByExpression" - || path == "success/RecordProjectionByType" - || path == "success/unit/RecordProjectionByType" - || path == "success/unit/RecordProjectionByTypeEmpty" - // TODO: RFC3986 URLs - || path == "success/unit/import/urls/emptyPath0" - || path == "success/unit/import/urls/emptyPath1" - || path == "success/unit/import/urls/emptyPathSegment" - // TODO: toMap - || path == "success/toMap" - })?; + make_test_module( + &mut file, + TestFeature { + module_name: "binary_encoding", + directory: spec_tests_dir.join("parser/success/"), + variant: "BinaryEncoding", + path_filter: |path: &str| { + false + // Too slow in debug mode + || path == "largeExpression" + // See https://github.com/pyfisch/cbor/issues/109 + || path == "double" + || path == "unit/DoubleLitExponentNoDot" + || path == "unit/DoubleLitSecretelyInt" + // TODO: projection by expression + || path == "recordProjectionByExpression" + || path == "RecordProjectionByType" + || path == "unit/RecordProjectionByType" + || path == "unit/RecordProjectionByTypeEmpty" + // TODO: RFC3986 URLs + || path == "unit/import/urls/emptyPath0" + || path == "unit/import/urls/emptyPath1" + || path == "unit/import/urls/emptyPathSegment" + // TODO: toMap + || path == "toMap" + }, + input_type: FileType::Text, + output_type: Some(FileType::Binary), + }, + )?; + + make_test_module( + &mut file, + TestFeature { + module_name: "binary_decoding_success", + directory: spec_tests_dir.join("binary-decode/success/"), + variant: "BinaryDecodingSuccess", + path_filter: |path: &str| { + false + // TODO: projection by expression + || path == "unit/RecordProjectFields" + || path == "unit/recordProjectionByExpression" + // TODO: toMap + || path == "unit/ToMap" + || path == "unit/ToMapAnnotated" + }, + input_type: FileType::Binary, + output_type: Some(FileType::Text), + }, + )?; + + make_test_module( + &mut file, + TestFeature { + module_name: "binary_decoding_failure", + directory: spec_tests_dir.join("binary-decode/failure/"), + variant: "BinaryDecodingFailure", + path_filter: |_path: &str| false, + input_type: FileType::Binary, + output_type: None, + }, + )?; make_test_module( &mut file, - "binary_encoding", - "parser/", - "BinaryEncoding", - |path| { - // Failure tests are only for the parser - path.starts_with("failure/") - // Too slow in debug mode - || path == "success/largeExpression" - // See https://github.com/pyfisch/cbor/issues/109 - || path == "success/double" - || path == "success/unit/DoubleLitExponentNoDot" - || path == "success/unit/DoubleLitSecretelyInt" - // TODO: projection by expression - || path == "success/recordProjectionByExpression" - || path == "success/RecordProjectionByType" - || path == "success/unit/RecordProjectionByType" - || path == "success/unit/RecordProjectionByTypeEmpty" - // TODO: RFC3986 URLs - || path == "success/unit/import/urls/emptyPath0" - || path == "success/unit/import/urls/emptyPath1" - || path == "success/unit/import/urls/emptyPathSegment" - // TODO: toMap - || path == "success/toMap" + TestFeature { + module_name: "beta_normalize", + directory: spec_tests_dir.join("normalization/success/"), + variant: "Normalization", + path_filter: |path: &str| { + // We don't support bignums + path == "simple/integerToDouble" + // Too slow + || path == "remoteSystems" + // TODO: projection by expression + || path == "unit/RecordProjectionByTypeEmpty" + || path == "unit/RecordProjectionByTypeNonEmpty" + || path == "unit/RecordProjectionByTypeNormalizeProjection" + // TODO: fix Double/show + || path == "prelude/JSON/number/1" + // TODO: toMap + || path == "unit/EmptyToMap" + || path == "unit/ToMap" + || path == "unit/ToMapWithType" + // TODO: Normalize field selection further by inspecting the argument + || path == "simplifications/rightBiasedMergeWithinRecordProjectionWithinFieldSelection0" + || path == "simplifications/rightBiasedMergeWithinRecordProjectionWithinFieldSelection1" + || path == "simplifications/rightBiasedMergeWithinRecursiveRecordMergeWithinFieldselection" + || path == "unit/RecordProjectionByTypeWithinFieldSelection" + || path == "unit/RecordProjectionWithinFieldSelection" + || path == "unit/RecursiveRecordMergeWithinFieldSelection0" + || path == "unit/RecursiveRecordMergeWithinFieldSelection1" + || path == "unit/RecursiveRecordMergeWithinFieldSelection2" + || path == "unit/RecursiveRecordMergeWithinFieldSelection3" + || path == "unit/RightBiasedMergeWithinFieldSelection0" + || path == "unit/RightBiasedMergeWithinFieldSelection1" + || path == "unit/RightBiasedMergeWithinFieldSelection2" + || path == "unit/RightBiasedMergeWithinFieldSelection3" + || path == "unit/RightBiasedMergeEquivalentArguments" + }, + input_type: FileType::Text, + output_type: Some(FileType::Text), }, )?; make_test_module( &mut file, - "binary_decoding", - "binary-decode/", - "BinaryDecoding", - |path| { - false - // TODO: projection by expression - || path == "success/unit/RecordProjectFields" - || path == "success/unit/recordProjectionByExpression" - // TODO: toMap - || path == "success/unit/ToMap" - || path == "success/unit/ToMapAnnotated" + TestFeature { + module_name: "alpha_normalize", + directory: spec_tests_dir.join("alpha-normalization/success/"), + variant: "AlphaNormalization", + path_filter: |path: &str| { + // This test doesn't typecheck + path == "unit/FunctionNestedBindingXXFree" + }, + input_type: FileType::Text, + output_type: Some(FileType::Text), }, )?; make_test_module( &mut file, - "beta_normalize", - "normalization/", - "Normalization", - |path| { - // We don't support bignums - path == "success/simple/integerToDouble" - // Too slow - || path == "success/remoteSystems" - // TODO: projection by expression - || path == "success/unit/RecordProjectionByTypeEmpty" - || path == "success/unit/RecordProjectionByTypeNonEmpty" - || path == "success/unit/RecordProjectionByTypeNormalizeProjection" - // TODO: fix Double/show - || path == "success/prelude/JSON/number/1" - // TODO: toMap - || path == "success/unit/EmptyToMap" - || path == "success/unit/ToMap" - || path == "success/unit/ToMapWithType" - // TODO: Normalize field selection further by inspecting the argument - || path == "success/simplifications/rightBiasedMergeWithinRecordProjectionWithinFieldSelection0" - || path == "success/simplifications/rightBiasedMergeWithinRecordProjectionWithinFieldSelection1" - || path == "success/simplifications/rightBiasedMergeWithinRecursiveRecordMergeWithinFieldselection" - || path == "success/unit/RecordProjectionByTypeWithinFieldSelection" - || path == "success/unit/RecordProjectionWithinFieldSelection" - || path == "success/unit/RecursiveRecordMergeWithinFieldSelection0" - || path == "success/unit/RecursiveRecordMergeWithinFieldSelection1" - || path == "success/unit/RecursiveRecordMergeWithinFieldSelection2" - || path == "success/unit/RecursiveRecordMergeWithinFieldSelection3" - || path == "success/unit/RightBiasedMergeWithinFieldSelection0" - || path == "success/unit/RightBiasedMergeWithinFieldSelection1" - || path == "success/unit/RightBiasedMergeWithinFieldSelection2" - || path == "success/unit/RightBiasedMergeWithinFieldSelection3" - || path == "success/unit/RightBiasedMergeEquivalentArguments" + TestFeature { + module_name: "typecheck_success", + directory: spec_tests_dir.join("typecheck/success/"), + variant: "TypecheckSuccess", + path_filter: |path: &str| { + false + // Too slow + || path == "prelude" + }, + input_type: FileType::Text, + output_type: Some(FileType::Text), }, )?; make_test_module( &mut file, - "alpha_normalize", - "alpha-normalization/", - "AlphaNormalization", - |path| { - // This test doesn't typecheck - path == "success/unit/FunctionNestedBindingXXFree" + TestFeature { + module_name: "typecheck_failure", + directory: spec_tests_dir.join("typecheck/failure/"), + variant: "TypecheckFailure", + path_filter: |path: &str| { + false + // TODO: Enable imports in typecheck tests + || path == "importBoundary" + || path == "customHeadersUsingBoundVariable" + // TODO: projection by expression + || path == "unit/RecordProjectionByTypeFieldTypeMismatch" + || path == "unit/RecordProjectionByTypeNotPresent" + // TODO: toMap + || path == "unit/EmptyToMap" + || path == "unit/HeterogenousToMap" + || path == "unit/MistypedToMap1" + || path == "unit/MistypedToMap2" + || path == "unit/MistypedToMap3" + || path == "unit/MistypedToMap4" + || path == "unit/NonRecordToMap" + }, + input_type: FileType::Text, + output_type: None, }, )?; make_test_module( &mut file, - "typecheck", - "typecheck/", - "Typecheck", - |path| { - false - // TODO: Enable imports in typecheck tests - || path == "failure/importBoundary" - || path == "failure/customHeadersUsingBoundVariable" - // Too slow - || path == "success/prelude" - // TODO: projection by expression - || path == "failure/unit/RecordProjectionByTypeFieldTypeMismatch" - || path == "failure/unit/RecordProjectionByTypeNotPresent" - // TODO: toMap - || path == "failure/unit/EmptyToMap" - || path == "failure/unit/HeterogenousToMap" - || path == "failure/unit/MistypedToMap1" - || path == "failure/unit/MistypedToMap2" - || path == "failure/unit/MistypedToMap3" - || path == "failure/unit/MistypedToMap4" - || path == "failure/unit/NonRecordToMap" + TestFeature { + module_name: "type_inference_success", + directory: spec_tests_dir.join("type-inference/success/"), + variant: "TypeInferenceSuccess", + path_filter: |path: &str| { + false + // TODO: projection by expression + || path == "unit/RecordProjectionByType" + || path == "unit/RecordProjectionByTypeEmpty" + || path == "unit/RecordProjectionByTypeJudgmentalEquality" + // TODO: toMap + || path == "unit/ToMap" + || path == "unit/ToMapAnnotated" + }, + input_type: FileType::Text, + output_type: Some(FileType::Text), }, )?; make_test_module( &mut file, - "type_inference", - "type-inference/", - "TypeInference", - |path| { - false - // TODO: projection by expression - || path == "success/unit/RecordProjectionByType" - || path == "success/unit/RecordProjectionByTypeEmpty" - || path == "success/unit/RecordProjectionByTypeJudgmentalEquality" - // TODO: toMap - || path == "success/unit/ToMap" - || path == "success/unit/ToMapAnnotated" + TestFeature { + module_name: "type_inference_failure", + directory: spec_tests_dir.join("type-inference/failure/"), + variant: "TypeInferenceFailure", + path_filter: |_path: &str| false, + input_type: FileType::Text, + output_type: None, }, )?; diff --git a/dhall/src/phase/resolve.rs b/dhall/src/phase/resolve.rs index b715e72..a58f5e4 100644 --- a/dhall/src/phase/resolve.rs +++ b/dhall/src/phase/resolve.rs @@ -109,13 +109,22 @@ pub(crate) fn skip_resolve_expr( mod spec_tests { macro_rules! import_success { ($name:ident, $path:expr) => { - make_spec_test!(Import, Success, $name, &("../dhall-lang/tests/import/success/".to_owned() + $path)); + make_spec_test!( + ImportSuccess( + &("../dhall-lang/tests/import/success/".to_owned() + $path + "A.dhall"), + &("../dhall-lang/tests/import/success/".to_owned() + $path + "B.dhall") + ), + $name + ); }; } // macro_rules! import_failure { // ($name:ident, $path:expr) => { - // make_spec_test!(Import, Failure, $name, &("../dhall-lang/tests/import/failure/".to_owned() + $path)); + // make_spec_test!( + // ImportFailure(&("../dhall-lang/tests/import/failure/".to_owned() + $path + ".dhall")), + // $name + // ); // }; // } diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs index 3055717..ae41038 100644 --- a/dhall/src/tests.rs +++ b/dhall/src/tests.rs @@ -22,13 +22,13 @@ right: `{}`"#, #[macro_export] macro_rules! make_spec_test { - ($type:ident, $status:ident, $name:ident, $path:expr) => { + ($type:expr, $name:ident) => { #[test] #[allow(non_snake_case)] fn $name() { + use crate::tests::Test::*; use crate::tests::*; - match run_test_stringy_error($path, Feature::$type, Status::$status) - { + match run_test_stringy_error($type) { Ok(_) => {} Err(s) => panic!(s), } @@ -44,24 +44,22 @@ use crate::error::{Error, Result}; use crate::phase::Parsed; #[allow(dead_code)] -#[derive(Copy, Clone)] -pub enum Feature { - Parser, - Printer, - BinaryEncoding, - BinaryDecoding, - Import, - Normalization, - AlphaNormalization, - Typecheck, - TypeInference, -} - -#[allow(dead_code)] -#[derive(Copy, Clone)] -pub enum Status { - Success, - Failure, +#[derive(Clone)] +pub enum Test<'a> { + ParserSuccess(&'a str, &'a str), + ParserFailure(&'a str), + Printer(&'a str, &'a str), + BinaryEncoding(&'a str, &'a str), + BinaryDecodingSuccess(&'a str, &'a str), + BinaryDecodingFailure(&'a str), + ImportSuccess(&'a str, &'a str), + ImportFailure(&'a str), + TypecheckSuccess(&'a str, &'a str), + TypecheckFailure(&'a str), + TypeInferenceSuccess(&'a str, &'a str), + TypeInferenceFailure(&'a str), + Normalization(&'a str, &'a str), + AlphaNormalization(&'a str, &'a str), } fn parse_file_str(file_path: &str) -> Result { @@ -70,180 +68,153 @@ fn parse_file_str(file_path: &str) -> Result { #[allow(dead_code)] pub fn run_test_stringy_error( - base_path: &str, - feature: Feature, - status: Status, + test: Test<'_>, ) -> std::result::Result<(), String> { - let base_path: String = base_path.to_string(); - run_test(&base_path, feature, status) - .map_err(|e| e.to_string()) - .map(|_| ()) + run_test(test).map_err(|e| e.to_string()).map(|_| ()) } -#[allow(clippy::single_match)] -pub fn run_test( - base_path: &str, - feature: Feature, - status: Status, -) -> Result<()> { - use self::Feature::*; - use self::Status::*; - let base_path = base_path.to_owned(); - match status { - Success => { - match feature { - BinaryDecoding => { - let expr_file_path = base_path.clone() + "A.dhallb"; - let expr_file_path = PathBuf::from(&expr_file_path); - let mut expr_data = Vec::new(); - { - File::open(&expr_file_path)? - .read_to_end(&mut expr_data)?; - } - let expr = Parsed::parse_binary(&expr_data)?; - let expected_file_path = base_path + "B.dhall"; - let expected = parse_file_str(&expected_file_path)?; - assert_eq_pretty!(expr, expected); - - return Ok(()); +pub fn run_test(test: Test<'_>) -> Result<()> { + use self::Test::*; + match test { + ParserSuccess(expr_file_path, expected_file_path) => { + let expr = parse_file_str(&expr_file_path)?; + // This exercices both parsing and binary decoding + // Compare parse/decoded + let expected = + Parsed::parse_binary_file(&PathBuf::from(expected_file_path))?; + assert_eq_pretty!(expr, expected); + } + ParserFailure(file_path) => { + let err = parse_file_str(&file_path).unwrap_err(); + match &err { + Error::Parse(_) => {} + Error::IO(e) if e.kind() == std::io::ErrorKind::InvalidData => { } - _ => {} + e => panic!("Expected parse error, got: {:?}", e), } - let expr_file_path = base_path.clone() + "A.dhall"; + } + BinaryEncoding(expr_file_path, expected_file_path) => { let expr = parse_file_str(&expr_file_path)?; - - match feature { - Parser => { - // This exercices both parsing and binary decoding - // Compare parse/decoded - let expected_file_path = base_path + "B.dhallb"; - let expected_file_path = PathBuf::from(&expected_file_path); - let mut expected_data = Vec::new(); - { - File::open(&expected_file_path)? - .read_to_end(&mut expected_data)?; - } - let expected = Parsed::parse_binary(&expected_data)?; - assert_eq_pretty!(expr, expected); - - return Ok(()); - } - Printer => { - // Round-trip pretty-printer - let expr_string = expr.to_string(); - let expected = expr; - let expr: Parsed = Parsed::parse_str(&expr_string)?; - assert_eq!(expr, expected); - - return Ok(()); - } - BinaryEncoding => { - let expected_file_path = base_path + "B.dhallb"; - let expected_file_path = PathBuf::from(&expected_file_path); - let mut expected_data = Vec::new(); - { - File::open(&expected_file_path)? - .read_to_end(&mut expected_data)?; - } - let expr_data = expr.encode()?; - - // Compare bit-by-bit - if expr_data != expected_data { - // use std::io::Write; - // File::create(&expected_file_path)?.write_all(&expr_data)?; - // Pretty-print difference - assert_eq_pretty!( - serde_cbor::de::from_slice::< - serde_cbor::value::Value, - >(&expr_data) - .unwrap(), - serde_cbor::de::from_slice::< - serde_cbor::value::Value, - >(&expected_data) - .unwrap() - ); - // If difference was not visible in the cbor::Value - assert_eq!(expr_data, expected_data); - } - - return Ok(()); - } - _ => {} + let mut expected_data = Vec::new(); + { + File::open(&PathBuf::from(&expected_file_path))? + .read_to_end(&mut expected_data)?; } + let expr_data = expr.encode()?; + + // Compare bit-by-bit + if expr_data != expected_data { + // use std::io::Write; + // File::create(&expected_file_path)?.write_all(&expr_data)?; + // Pretty-print difference + assert_eq_pretty!( + serde_cbor::de::from_slice::( + &expr_data + ) + .unwrap(), + serde_cbor::de::from_slice::( + &expected_data + ) + .unwrap() + ); + // If difference was not visible in the cbor::Value + assert_eq!(expr_data, expected_data); + } + } + BinaryDecodingSuccess(expr_file_path, expected_file_path) => { + let expr = + Parsed::parse_binary_file(&PathBuf::from(expr_file_path))?; + let expected = parse_file_str(&expected_file_path)?; + assert_eq_pretty!(expr, expected); + } + BinaryDecodingFailure(file_path) => { + Parsed::parse_binary_file(&PathBuf::from(file_path)).unwrap_err(); + } + Printer(expr_file_path, _) => { + let expected = parse_file_str(&expr_file_path)?; + // Round-trip pretty-printer + let expr: Parsed = Parsed::parse_str(&expected.to_string())?; + assert_eq!(expr, expected); + } + ImportSuccess(expr_file_path, expected_file_path) => { + let expr = parse_file_str(&expr_file_path)? + .resolve()? + .typecheck()? + .normalize(); + let expected = parse_file_str(&expected_file_path)? + .resolve()? + .typecheck()? + .normalize(); - let expr = expr.resolve()?; - - let expected_file_path = base_path + "B.dhall"; + assert_eq_display!(expr, expected); + } + ImportFailure(file_path) => { + parse_file_str(&file_path)?.resolve().unwrap_err(); + } + TypecheckSuccess(expr_file_path, expected_file_path) => { + let expr = parse_file_str(&expr_file_path)?.resolve()?; let expected = parse_file_str(&expected_file_path)? .resolve()? .typecheck()? .normalize(); - match feature { - Parser | Printer | BinaryEncoding | BinaryDecoding => { - unreachable!() - } - Import => { - let expr = expr.typecheck()?.normalize(); - assert_eq_display!(expr, expected); - } - Typecheck => { - expr.typecheck_with(&expected.into_typed())?.get_type()?; - } - TypeInference => { - let expr = expr.typecheck()?; - let ty = expr.get_type()?.normalize(); - assert_eq_display!(ty, expected); - } - Normalization => { - let expr = expr.typecheck()?.normalize(); - assert_eq_display!(expr, expected); - } - AlphaNormalization => { - let expr = expr.typecheck()?.normalize().to_expr_alpha(); - assert_eq_display!(expr, expected.to_expr()); + expr.typecheck_with(&expected.into_typed())?.get_type()?; + } + TypecheckFailure(file_path) => { + let res = parse_file_str(&file_path)?.skip_resolve()?.typecheck(); + match res { + Err(_) => {} + // If e did typecheck, check that it doesn't have a type + Ok(e) => { + e.get_type().unwrap_err(); } } } - Failure => { - let file_path = base_path + ".dhall"; - match feature { - Parser => { - let err = parse_file_str(&file_path).unwrap_err(); - match &err { - Error::Parse(_) => {} - Error::IO(e) - if e.kind() == std::io::ErrorKind::InvalidData => {} - e => panic!("Expected parse error, got: {:?}", e), - } - } - Printer | BinaryEncoding => unreachable!(), - BinaryDecoding => { - let expr_file_path = file_path + "b"; - let mut expr_data = Vec::new(); - { - File::open(&PathBuf::from(&expr_file_path))? - .read_to_end(&mut expr_data)?; - } - Parsed::parse_binary(&expr_data).unwrap_err(); - } - Import => { - parse_file_str(&file_path)?.resolve().unwrap_err(); - } - Normalization | AlphaNormalization => unreachable!(), - Typecheck | TypeInference => { - let res = - parse_file_str(&file_path)?.skip_resolve()?.typecheck(); - match res { - Err(_) => {} - // If e did typecheck, check that it doesn't have a type - Ok(e) => { - e.get_type().unwrap_err(); - } - } + TypeInferenceSuccess(expr_file_path, expected_file_path) => { + let expr = + parse_file_str(&expr_file_path)?.resolve()?.typecheck()?; + let ty = expr.get_type()?.normalize(); + let expected = parse_file_str(&expected_file_path)? + .resolve()? + .typecheck()? + .normalize(); + assert_eq_display!(ty, expected); + } + TypeInferenceFailure(file_path) => { + let res = parse_file_str(&file_path)?.skip_resolve()?.typecheck(); + match res { + Err(_) => {} + // If e did typecheck, check that it doesn't have a type + Ok(e) => { + e.get_type().unwrap_err(); } } } + Normalization(expr_file_path, expected_file_path) => { + let expr = parse_file_str(&expr_file_path)? + .resolve()? + .typecheck()? + .normalize(); + let expected = parse_file_str(&expected_file_path)? + .resolve()? + .typecheck()? + .normalize(); + + assert_eq_display!(expr, expected); + } + AlphaNormalization(expr_file_path, expected_file_path) => { + let expr = parse_file_str(&expr_file_path)? + .resolve()? + .typecheck()? + .normalize() + .to_expr_alpha(); + let expected = parse_file_str(&expected_file_path)? + .resolve()? + .typecheck()? + .normalize(); + + assert_eq_display!(expr, expected.to_expr()); + } } Ok(()) } -- cgit v1.2.3 From aba7e62e49ac9dead0a2868f739091d2d15ff0d1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 31 Aug 2019 21:59:39 +0200 Subject: Implement parsing of `toMap` keyword --- dhall/build.rs | 9 --------- dhall/src/phase/binary.rs | 11 +++++++++++ dhall/src/phase/normalize.rs | 1 + dhall/src/phase/typecheck.rs | 1 + dhall_syntax/src/core/expr.rs | 2 ++ dhall_syntax/src/core/visitor.rs | 3 +++ dhall_syntax/src/parser.rs | 8 +++++++- dhall_syntax/src/printer.rs | 11 +++++++++++ tests_buffer | 2 ++ 9 files changed, 38 insertions(+), 10 deletions(-) diff --git a/dhall/build.rs b/dhall/build.rs index 4c654c9..e31e39d 100644 --- a/dhall/build.rs +++ b/dhall/build.rs @@ -133,8 +133,6 @@ fn main() -> std::io::Result<()> { || path == "unit/import/urls/emptyPath0" || path == "unit/import/urls/emptyPath1" || path == "unit/import/urls/emptyPathSegment" - // TODO: toMap - || path == "toMap" }, input_type: FileType::Text, output_type: Some(FileType::Binary), @@ -172,8 +170,6 @@ fn main() -> std::io::Result<()> { || path == "unit/import/urls/emptyPath0" || path == "unit/import/urls/emptyPath1" || path == "unit/import/urls/emptyPathSegment" - // TODO: toMap - || path == "toMap" }, input_type: FileType::Text, output_type: Some(FileType::Binary), @@ -203,8 +199,6 @@ fn main() -> std::io::Result<()> { || path == "unit/import/urls/emptyPath0" || path == "unit/import/urls/emptyPath1" || path == "unit/import/urls/emptyPathSegment" - // TODO: toMap - || path == "toMap" }, input_type: FileType::Text, output_type: Some(FileType::Binary), @@ -222,9 +216,6 @@ fn main() -> std::io::Result<()> { // TODO: projection by expression || path == "unit/RecordProjectFields" || path == "unit/recordProjectionByExpression" - // TODO: toMap - || path == "unit/ToMap" - || path == "unit/ToMapAnnotated" }, input_type: FileType::Binary, output_type: Some(FileType::Text), diff --git a/dhall/src/phase/binary.rs b/dhall/src/phase/binary.rs index 7dc9be2..40219d9 100644 --- a/dhall/src/phase/binary.rs +++ b/dhall/src/phase/binary.rs @@ -366,6 +366,15 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result { let y = cbor_value_to_dhall(&y)?; Annot(x, y) } + [U64(27), x] => { + let x = cbor_value_to_dhall(&x)?; + ToMap(x, None) + } + [U64(27), x, y] => { + let x = cbor_value_to_dhall(&x)?; + let y = cbor_value_to_dhall(&y)?; + ToMap(x, Some(y)) + } [U64(28), x] => { let x = cbor_value_to_dhall(&x)?; EmptyListLit(x) @@ -550,6 +559,8 @@ where Merge(x, y, Some(z)) => { ser_seq!(ser; tag(6), expr(x), expr(y), expr(z)) } + ToMap(x, None) => ser_seq!(ser; tag(27), expr(x)), + ToMap(x, Some(y)) => ser_seq!(ser; tag(27), expr(x), expr(y)), Projection(x, ls) => ser.collect_seq( once(tag(10)) .chain(once(expr(x))) diff --git a/dhall/src/phase/normalize.rs b/dhall/src/phase/normalize.rs index 82a378c..3f6e99c 100644 --- a/dhall/src/phase/normalize.rs +++ b/dhall/src/phase/normalize.rs @@ -765,6 +765,7 @@ pub(crate) fn normalize_one_layer( } } } + ExprF::ToMap(_, _) => unimplemented!("toMap"), }; match ret { diff --git a/dhall/src/phase/typecheck.rs b/dhall/src/phase/typecheck.rs index 52a4e47..9013c1f 100644 --- a/dhall/src/phase/typecheck.rs +++ b/dhall/src/phase/typecheck.rs @@ -757,6 +757,7 @@ fn type_last_layer( (None, None) => return mkerr(MergeEmptyNeedsAnnotation), } } + ToMap(_, _) => unimplemented!("toMap"), Projection(record, labels) => { let record_type = record.get_type()?; let record_borrow = record_type.as_whnf(); diff --git a/dhall_syntax/src/core/expr.rs b/dhall_syntax/src/core/expr.rs index 2d73a64..51b6c47 100644 --- a/dhall_syntax/src/core/expr.rs +++ b/dhall_syntax/src/core/expr.rs @@ -243,6 +243,8 @@ pub enum ExprF { UnionType(DupTreeMap>), /// `merge x y : t` Merge(SubExpr, SubExpr, Option), + /// `toMap x : t` + ToMap(SubExpr, Option), /// `e.x` Field(SubExpr, Label), /// `e.{ x, y, z }` diff --git a/dhall_syntax/src/core/visitor.rs b/dhall_syntax/src/core/visitor.rs index 49fff60..435771e 100644 --- a/dhall_syntax/src/core/visitor.rs +++ b/dhall_syntax/src/core/visitor.rs @@ -150,6 +150,9 @@ where v.visit_subexpr(y)?, opt(t, |e| v.visit_subexpr(e))?, ), + ToMap(x, t) => { + ToMap(v.visit_subexpr(x)?, opt(t, |e| v.visit_subexpr(e))?) + } Field(e, l) => Field(v.visit_subexpr(e)?, l.clone()), Projection(e, ls) => Projection(v.visit_subexpr(e)?, ls.clone()), Assert(e) => Assert(v.visit_subexpr(e)?), diff --git a/dhall_syntax/src/parser.rs b/dhall_syntax/src/parser.rs index defa79b..24ecc1e 100644 --- a/dhall_syntax/src/parser.rs +++ b/dhall_syntax/src/parser.rs @@ -827,6 +827,7 @@ make_parser! { rule!(assert<()>); rule!(if_<()>); rule!(in_<()>); + rule!(toMap<()>); rule!(empty_list_literal; span; children!( [application_expression(e)] => { @@ -863,6 +864,9 @@ make_parser! { [assert(()), expression(x)] => { spanned(span, Assert(x)) }, + [toMap(()), import_expression(x), application_expression(y)] => { + spanned(span, ToMap(x, Some(y))) + }, [operator_expression(e)] => e, [operator_expression(e), expression(annot)] => { spanned(span, Annot(e, annot)) @@ -934,7 +938,6 @@ make_parser! { )); rule!(Some_<()>); - rule!(toMap<()>); rule!(application_expression; children!( [first_application_expression(e)] => e, @@ -951,6 +954,9 @@ make_parser! { [merge(()), import_expression(x), import_expression(y)] => { spanned(span, Merge(x, y, None)) }, + [toMap(()), import_expression(x)] => { + spanned(span, ToMap(x, None)) + }, [import_expression(e)] => e, )); diff --git a/dhall_syntax/src/printer.rs b/dhall_syntax/src/printer.rs index 276590e..8571d11 100644 --- a/dhall_syntax/src/printer.rs +++ b/dhall_syntax/src/printer.rs @@ -41,6 +41,12 @@ impl Display for ExprF { write!(f, " : {}", c)?; } } + ToMap(a, b) => { + write!(f, "toMap {}", a)?; + if let Some(b) = b { + write!(f, " : {}", b)?; + } + } Annot(a, b) => { write!(f, "{} : {}", a, b)?; } @@ -144,6 +150,7 @@ impl RawExpr { | NEListLit(_) | SomeLit(_) | Merge(_, _, _) + | ToMap(_, _) | Annot(_, _) if phase > Base => { @@ -172,6 +179,10 @@ impl RawExpr { b.phase(PrintPhase::Import), c.map(|x| x.phase(PrintPhase::App)), ), + ToMap(a, b) => ToMap( + a.phase(PrintPhase::Import), + b.map(|x| x.phase(PrintPhase::App)), + ), Annot(a, b) => Annot(a.phase(Operator), b), ExprF::BinOp(op, a, b) => ExprF::BinOp( op, diff --git a/tests_buffer b/tests_buffer index 67de9eb..446c3f0 100644 --- a/tests_buffer +++ b/tests_buffer @@ -10,6 +10,8 @@ success/ LetNoAnnot let x = y in e LetAnnot let x: T = y in e EmptyRecordLiteral {=} + ToMap toMap x + ToMapAnnot toMap x : T failure/ AssertNoAnnotation assert -- cgit v1.2.3 From befc4cda44a4f1e26aa0a301dfc92b455cbcbf18 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 31 Aug 2019 22:37:48 +0200 Subject: Don't URL-decode path segments --- Cargo.lock | 6 +++--- dhall-lang | 2 +- dhall/build.rs | 4 ++++ dhall_syntax/Cargo.toml | 2 +- dhall_syntax/src/parser.rs | 27 ++++++++++++++++++++++----- dhall_syntax/src/printer.rs | 22 +++++----------------- tests_buffer | 1 + 7 files changed, 37 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f754891..9bd7adc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,7 +108,7 @@ dependencies = [ "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -209,7 +209,7 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "1.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -461,7 +461,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "54f0c72a98d8ab3c99560bfd16df8059cc10e1f9a8e83e6e3b97718dd766e9c3" "checksum pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646" "checksum pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5a3492a4ed208ffc247adcdcc7ba2a95be3104f58877d0d02f0df39bf3efb5e" diff --git a/dhall-lang b/dhall-lang index 3d66b4c..fbcc2b9 160000 --- a/dhall-lang +++ b/dhall-lang @@ -1 +1 @@ -Subproject commit 3d66b4cd56627a39b6c615882beab97fbdf9d137 +Subproject commit fbcc2b9ad64c50dd0f0c9967cdea7066edfa80e8 diff --git a/dhall/build.rs b/dhall/build.rs index e31e39d..757eed8 100644 --- a/dhall/build.rs +++ b/dhall/build.rs @@ -123,6 +123,8 @@ fn main() -> std::io::Result<()> { false // Too slow in debug mode || path == "largeExpression" + // Pretty sure the test is incorrect + || path == "unit/import/urls/quotedPathFakeUrlEncode" // TODO: projection by expression || path == "recordProjectionByExpression" || path == "RecordProjectionByType" @@ -186,6 +188,8 @@ fn main() -> std::io::Result<()> { false // Too slow in debug mode || path == "largeExpression" + // Pretty sure the test is incorrect + || path == "unit/import/urls/quotedPathFakeUrlEncode" // See https://github.com/pyfisch/cbor/issues/109 || path == "double" || path == "unit/DoubleLitExponentNoDot" diff --git a/dhall_syntax/Cargo.toml b/dhall_syntax/Cargo.toml index 08d0195..1da10c7 100644 --- a/dhall_syntax/Cargo.toml +++ b/dhall_syntax/Cargo.toml @@ -10,7 +10,7 @@ doctest = false [dependencies] itertools = "0.8.0" -percent-encoding = "1.0.1" +percent-encoding = "2.1.0" pest = "2.1" either = "1.5.2" take_mut = "0.2.2" diff --git a/dhall_syntax/src/parser.rs b/dhall_syntax/src/parser.rs index 24ecc1e..53fd68a 100644 --- a/dhall_syntax/src/parser.rs +++ b/dhall_syntax/src/parser.rs @@ -669,12 +669,29 @@ make_parser! { rule!(unquoted_path_component<&'a str>; captured_str!(s) => s); rule!(quoted_path_component<&'a str>; captured_str!(s) => s); rule!(path_component; children!( - [unquoted_path_component(s)] => { - percent_encoding::percent_decode(s.as_bytes()) - .decode_utf8_lossy() - .into_owned() + [unquoted_path_component(s)] => s.to_string(), + [quoted_path_component(s)] => { + const RESERVED: &percent_encoding::AsciiSet = + &percent_encoding::CONTROLS + .add(b'=').add(b':').add(b'/').add(b'?') + .add(b'#').add(b'[').add(b']').add(b'@') + .add(b'!').add(b'$').add(b'&').add(b'\'') + .add(b'(').add(b')').add(b'*').add(b'+') + .add(b',').add(b';'); + s.chars() + .map(|c| { + // Percent-encode ascii chars + if c.is_ascii() { + percent_encoding::utf8_percent_encode( + &c.to_string(), + RESERVED, + ).to_string() + } else { + c.to_string() + } + }) + .collect() }, - [quoted_path_component(s)] => s.to_string(), )); rule!(path>; children!( [path_component(components)..] => { diff --git a/dhall_syntax/src/printer.rs b/dhall_syntax/src/printer.rs index 8571d11..f3dc648 100644 --- a/dhall_syntax/src/printer.rs +++ b/dhall_syntax/src/printer.rs @@ -360,15 +360,9 @@ impl Display for Import { use FilePrefix::*; use ImportLocation::*; use ImportMode::*; - let fmt_remote_path_component = |s: &str| -> String { - use percent_encoding::{ - utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, - }; - utf8_percent_encode(s, PATH_SEGMENT_ENCODE_SET).to_string() - }; - let fmt_local_path_component = |s: &str| -> String { + let quote_if_needed = |s: &str| -> String { if s.chars().all(|c| c.is_ascii_alphanumeric()) { - s.to_owned() + s.to_string() } else { format!("\"{}\"", s) } @@ -383,19 +377,13 @@ impl Display for Import { Absolute => "", }; write!(f, "{}/", prefix)?; - let path: String = path - .iter() - .map(|c| fmt_local_path_component(c.as_ref())) - .join("/"); + let path: String = + path.iter().map(|c| quote_if_needed(&*c)).join("/"); f.write_str(&path)?; } Remote(url) => { write!(f, "{}://{}/", url.scheme, url.authority,)?; - let path: String = url - .path - .iter() - .map(|c| fmt_remote_path_component(c.as_ref())) - .join("/"); + let path: String = url.path.iter().join("/"); f.write_str(&path)?; if let Some(q) = &url.query { write!(f, "?{}", q)? diff --git a/tests_buffer b/tests_buffer index 446c3f0..6597d69 100644 --- a/tests_buffer +++ b/tests_buffer @@ -3,6 +3,7 @@ parser: ./"a%20b" text interpolation and escapes projection by expression unit tests +fix fakeurlencode test success/ operators/ PrecedenceAll1 a ? b || c + d ++ e # f && g ∧ h ⫽ i ⩓ j * k == l != m n.o -- cgit v1.2.3 From 737abd9be6d35bbce784d9cf249edf7ad14677d6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 31 Aug 2019 22:39:04 +0200 Subject: Update dhall-lang submodule --- dhall-lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhall-lang b/dhall-lang index fbcc2b9..7639b4e 160000 --- a/dhall-lang +++ b/dhall-lang @@ -1 +1 @@ -Subproject commit fbcc2b9ad64c50dd0f0c9967cdea7066edfa80e8 +Subproject commit 7639b4ed2fd457bfa77daabfd329fea3b25581c9 -- cgit v1.2.3