diff options
Diffstat (limited to 'dhall')
-rw-r--r-- | dhall/src/api/mod.rs | 85 | ||||
-rw-r--r-- | dhall/src/api/static_type.rs | 93 | ||||
-rw-r--r-- | dhall/src/api/traits/dynamic_type.rs | 32 | ||||
-rw-r--r-- | dhall/src/api/traits/mod.rs | 4 | ||||
-rw-r--r-- | dhall/src/api/traits/static_type.rs | 149 | ||||
-rw-r--r-- | dhall/src/lib.rs | 10 | ||||
-rw-r--r-- | dhall/src/phase/mod.rs | 57 | ||||
-rw-r--r-- | dhall/src/phase/typecheck.rs | 2 | ||||
-rw-r--r-- | dhall/tests/traits.rs | 67 |
9 files changed, 223 insertions, 276 deletions
diff --git a/dhall/src/api/mod.rs b/dhall/src/api/mod.rs index 0a0ef93..72789d9 100644 --- a/dhall/src/api/mod.rs +++ b/dhall/src/api/mod.rs @@ -1,13 +1,88 @@ mod serde; +pub(crate) mod static_type; pub(crate) mod traits; +// pub struct Value(crate::phase::Normalized); + +pub use typ::Type; + +mod typ { + use crate::core::thunk::{Thunk, TypeThunk}; + use crate::core::value::Value; + use crate::phase::{NormalizedSubExpr, Typed}; + use dhall_syntax::Builtin; + + /// 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 from_normalized_expr(e: NormalizedSubExpr) -> Self { + Type(Typed::from_normalized_expr_untyped(e)) + } + #[doc(hidden)] + pub fn make_record_type( + kts: impl Iterator<Item = (String, Type)>, + ) -> 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<Item = (String, Option<Type>)>, + ) -> 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() + } + } +} + /// Deserialization of Dhall expressions into Rust pub mod de { + pub use super::static_type::StaticType; + pub use super::Type; #[doc(hidden)] - pub use crate::phase::SimpleType; - pub use crate::traits::{Deserialize, SimpleStaticType, StaticType}; + pub use crate::traits::Deserialize; #[doc(hidden)] - pub use dhall_proc_macros::SimpleStaticType; + pub use dhall_proc_macros::StaticType; /// Deserialize an instance of type T from a string of Dhall text. /// @@ -35,6 +110,8 @@ pub mod de { pub fn from_str_auto_type<'a, T: Deserialize<'a> + StaticType>( s: &'a str, ) -> crate::error::Result<T> { - from_str(s, Some(&<T as StaticType>::get_static_type())) + // from_str(s, Some(&<T as StaticType>::static_type())) + // TODO + from_str(s, None) } } diff --git a/dhall/src/api/static_type.rs b/dhall/src/api/static_type.rs new file mode 100644 index 0000000..906bcef --- /dev/null +++ b/dhall/src/api/static_type.rs @@ -0,0 +1,93 @@ +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<bool>`, +/// 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<String, bool>` 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<A, B> 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<T, E> StaticType for std::result::Result<T, E> +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<T> StaticType for Option<T> +where + T: StaticType, +{ + fn static_type() -> Type { + Type::make_optional_type(T::static_type()) + } +} + +impl<T> StaticType for Vec<T> +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/api/traits/dynamic_type.rs b/dhall/src/api/traits/dynamic_type.rs deleted file mode 100644 index 7763a28..0000000 --- a/dhall/src/api/traits/dynamic_type.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::error::TypeError; -use crate::phase::{Normalized, Type, Typed}; -use crate::traits::StaticType; -use std::borrow::Cow; - -pub trait DynamicType { - fn get_type<'a>(&'a self) -> Result<Cow<'a, Type>, TypeError>; -} - -impl<T: StaticType> DynamicType for T { - fn get_type<'a>(&'a self) -> Result<Cow<'a, Type>, TypeError> { - Ok(Cow::Owned(T::get_static_type())) - } -} - -impl DynamicType for Type { - fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { - self.get_type() - } -} - -impl DynamicType for Normalized { - fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { - self.0.get_type() - } -} - -impl DynamicType for Typed { - fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { - self.get_type() - } -} diff --git a/dhall/src/api/traits/mod.rs b/dhall/src/api/traits/mod.rs index 315e17a..3fd21e4 100644 --- a/dhall/src/api/traits/mod.rs +++ b/dhall/src/api/traits/mod.rs @@ -1,6 +1,2 @@ mod deserialize; -mod dynamic_type; -mod static_type; pub use deserialize::Deserialize; -pub use dynamic_type::DynamicType; -pub use static_type::{SimpleStaticType, StaticType}; diff --git a/dhall/src/api/traits/static_type.rs b/dhall/src/api/traits/static_type.rs deleted file mode 100644 index e05dfff..0000000 --- a/dhall/src/api/traits/static_type.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::phase::*; -use dhall_proc_macros as dhall; -use dhall_syntax::*; - -/// A value that has a statically-known Dhall type. -/// -/// This trait is strictly more general than [SimpleStaticType]. -/// The reason is that it allows an arbitrary [Type] to be returned -/// instead of just a [SimpleType]. -/// -/// For now the only interesting impl is [SimpleType] itself, who -/// has a statically-known type which is not itself a [SimpleType]. -pub trait StaticType { - fn get_static_type() -> Type; -} - -/// A Rust type that can be represented as a Dhall type. -/// -/// A typical example is `Option<bool>`, -/// 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<String, bool>` cannot implement -/// [SimpleStaticType] because each different value would -/// have a different Dhall record type. -/// -/// The `Simple` in `SimpleStaticType` indicates that the returned type is -/// a [SimpleType]. -pub trait SimpleStaticType { - fn get_simple_static_type() -> SimpleType; -} - -fn mktype(x: SubExpr<X, X>) -> SimpleType { - x.into() -} - -impl<T: SimpleStaticType> StaticType for T { - fn get_static_type() -> Type { - crate::phase::Normalized::from_thunk_and_type( - crate::core::thunk::Thunk::from_normalized_expr( - T::get_simple_static_type().into(), - ), - Type::const_type(), - ) - .to_type() - } -} - -impl StaticType for SimpleType { - /// By definition, a [SimpleType] has type `Type`. - /// This returns the Dhall expression `Type` - fn get_static_type() -> Type { - Type::const_type() - } -} - -impl SimpleStaticType for bool { - fn get_simple_static_type() -> SimpleType { - mktype(dhall::subexpr!(Bool)) - } -} - -impl SimpleStaticType for Natural { - fn get_simple_static_type() -> SimpleType { - mktype(dhall::subexpr!(Natural)) - } -} - -impl SimpleStaticType for u32 { - fn get_simple_static_type() -> SimpleType { - mktype(dhall::subexpr!(Natural)) - } -} - -impl SimpleStaticType for u64 { - fn get_simple_static_type() -> SimpleType { - mktype(dhall::subexpr!(Natural)) - } -} - -impl SimpleStaticType for Integer { - fn get_simple_static_type() -> SimpleType { - mktype(dhall::subexpr!(Integer)) - } -} - -impl SimpleStaticType for i32 { - fn get_simple_static_type() -> SimpleType { - mktype(dhall::subexpr!(Integer)) - } -} - -impl SimpleStaticType for i64 { - fn get_simple_static_type() -> SimpleType { - mktype(dhall::subexpr!(Integer)) - } -} - -impl SimpleStaticType for String { - fn get_simple_static_type() -> SimpleType { - mktype(dhall::subexpr!(Text)) - } -} - -impl<A: SimpleStaticType, B: SimpleStaticType> SimpleStaticType for (A, B) { - fn get_simple_static_type() -> SimpleType { - let ta: SubExpr<_, _> = A::get_simple_static_type().into(); - let tb: SubExpr<_, _> = B::get_simple_static_type().into(); - mktype(dhall::subexpr!({ _1: ta, _2: tb })) - } -} - -impl<T: SimpleStaticType> SimpleStaticType for Option<T> { - fn get_simple_static_type() -> SimpleType { - let t: SubExpr<_, _> = T::get_simple_static_type().into(); - mktype(dhall::subexpr!(Optional t)) - } -} - -impl<T: SimpleStaticType> SimpleStaticType for Vec<T> { - fn get_simple_static_type() -> SimpleType { - let t: SubExpr<_, _> = T::get_simple_static_type().into(); - mktype(dhall::subexpr!(List t)) - } -} - -impl<'a, T: SimpleStaticType> SimpleStaticType for &'a T { - fn get_simple_static_type() -> SimpleType { - T::get_simple_static_type() - } -} - -impl<T> SimpleStaticType for std::marker::PhantomData<T> { - fn get_simple_static_type() -> SimpleType { - mktype(dhall::subexpr!({})) - } -} - -impl<T: SimpleStaticType, E: SimpleStaticType> SimpleStaticType - for std::result::Result<T, E> -{ - fn get_simple_static_type() -> SimpleType { - let tt: SubExpr<_, _> = T::get_simple_static_type().into(); - let te: SubExpr<_, _> = E::get_simple_static_type().into(); - mktype(dhall::subexpr!(< Ok: tt | Err: te>)) - } -} diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index b35b2ae..c25fc82 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -35,15 +35,15 @@ //! ### Custom datatype //! //! If you have a custom datatype for which you derived [serde::Deserialize], chances are -//! you will be able to derive [SimpleStaticType][de::SimpleStaticType] for it as well. +//! 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::SimpleStaticType; +//! use dhall::de::StaticType; //! -//! #[derive(Debug, Deserialize, SimpleStaticType)] +//! #[derive(Debug, Deserialize, StaticType)] //! struct Point { //! x: u64, //! y: u64, @@ -68,7 +68,7 @@ //! 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 [SimpleStaticType][de::SimpleStaticType] derive doesn't suit your +//! 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 @@ -130,4 +130,4 @@ pub mod error; pub(crate) mod phase; pub(crate) use api::traits; -pub use api::de; +pub use api::*; diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs index af39eb6..5262a27 100644 --- a/dhall/src/phase/mod.rs +++ b/dhall/src/phase/mod.rs @@ -49,19 +49,6 @@ pub(crate) enum Typed { #[derive(Debug, Clone)] pub(crate) struct Normalized(pub(crate) Typed); -/// A Dhall expression representing a simple type. -/// -/// This captures what is usually simply called a "type", like -/// `Bool`, `{ x: Integer }` or `Natural -> Text`. -/// -/// For a more general notion of "type", see [Type]. -#[derive(Debug, Clone)] -pub struct SimpleType(pub(crate) NormalizedSubExpr); - -/// A Dhall expression representing a (possibly higher-kinded) type. -/// -/// This includes [SimpleType]s but also higher-kinded expressions like -/// `Type`, `Kind` and `{ x: Type }`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Type(pub(crate) Typed); @@ -131,6 +118,12 @@ impl Typed { pub(crate) fn from_const(c: Const) -> Self { Typed::Const(c) } + pub(crate) fn from_value_untyped(v: Value) -> Self { + Typed::Untyped(Thunk::from_value(v)) + } + pub(crate) fn from_normalized_expr_untyped(e: NormalizedSubExpr) -> Self { + Typed::from_thunk_untyped(Thunk::from_normalized_expr(e)) + } // TODO: Avoid cloning if possible pub(crate) fn to_value(&self) -> Value { @@ -179,6 +172,7 @@ impl Typed { } impl Type { + // Deprecated pub(crate) fn to_normalized(&self) -> Normalized { self.0.clone().normalize() } @@ -202,19 +196,12 @@ impl Type { self.0.get_type() } - pub(crate) fn const_type() -> Self { - Type::from_const(Const::Type) - } pub(crate) fn from_const(c: Const) -> Self { Type(Typed::from_const(c)) } } impl Normalized { - pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self { - Normalized(Typed::from_thunk_and_type(th, t)) - } - pub(crate) fn to_expr(&self) -> NormalizedSubExpr { self.0.to_expr() } @@ -225,9 +212,12 @@ impl Normalized { pub(crate) fn to_value(&self) -> Value { self.0.to_value() } - pub(crate) fn to_type(self) -> Type { + pub(crate) fn to_type(&self) -> Type { self.0.to_type() } + pub(crate) fn into_typed(self) -> Typed { + self.0 + } pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { self.0.get_type() } @@ -301,7 +291,6 @@ macro_rules! derive_traits_for_wrapper_struct { derive_traits_for_wrapper_struct!(Parsed); derive_traits_for_wrapper_struct!(Resolved); derive_traits_for_wrapper_struct!(Normalized); -derive_traits_for_wrapper_struct!(SimpleType); impl Eq for Typed {} impl PartialEq for Typed { @@ -321,27 +310,3 @@ impl Display for Type { self.to_normalized().fmt(f) } } - -// Exposed for the macros -#[doc(hidden)] -impl From<SimpleType> for NormalizedSubExpr { - fn from(x: SimpleType) -> NormalizedSubExpr { - x.0 - } -} - -// Exposed for the macros -#[doc(hidden)] -impl From<NormalizedSubExpr> for SimpleType { - fn from(x: NormalizedSubExpr) -> SimpleType { - SimpleType(x) - } -} - -// Exposed for the macros -#[doc(hidden)] -impl From<Normalized> for Typed { - fn from(x: Normalized) -> Typed { - x.0 - } -} diff --git a/dhall/src/phase/typecheck.rs b/dhall/src/phase/typecheck.rs index e1dffb0..7f988c8 100644 --- a/dhall/src/phase/typecheck.rs +++ b/dhall/src/phase/typecheck.rs @@ -391,7 +391,7 @@ fn type_with( let e = e.rewrap(Annot(x, t)); return type_with(ctx, e); } - Embed(p) => p.clone().into(), + Embed(p) => p.clone().into_typed(), Var(var) => match ctx.lookup(&var) { Some(typed) => typed, None => { diff --git a/dhall/tests/traits.rs b/dhall/tests/traits.rs index 65220dc..75eaf75 100644 --- a/dhall/tests/traits.rs +++ b/dhall/tests/traits.rs @@ -1,79 +1,76 @@ #![feature(proc_macro_hygiene)] -use dhall::de::SimpleStaticType; -use dhall_proc_macros; +use dhall::de::{StaticType, Type}; +use dhall_proc_macros::subexpr; use dhall_syntax::{SubExpr, X}; #[test] fn test_static_type() { - fn mktype(x: SubExpr<X, X>) -> dhall::de::SimpleType { - x.into() + fn mktype(x: SubExpr<X, X>) -> Type { + Type::from_normalized_expr(x) } + assert_eq!(bool::static_type(), mktype(subexpr!(Bool))); + assert_eq!(String::static_type(), mktype(subexpr!(Text))); assert_eq!( - bool::get_simple_static_type(), - mktype(dhall_proc_macros::subexpr!(Bool)) + <Option<bool>>::static_type(), + mktype(subexpr!(Optional Bool)) ); assert_eq!( - String::get_simple_static_type(), - mktype(dhall_proc_macros::subexpr!(Text)) - ); - assert_eq!( - <Option<bool>>::get_simple_static_type(), - mktype(dhall_proc_macros::subexpr!(Optional Bool)) - ); - assert_eq!( - <(bool, Option<String>)>::get_simple_static_type(), - mktype(dhall_proc_macros::subexpr!({ _1: Bool, _2: Optional Text })) + <(bool, Vec<String>)>::static_type(), + mktype(subexpr!({ _1: Bool, _2: List Text })) ); - #[derive(dhall::de::SimpleStaticType)] + #[derive(dhall::de::StaticType)] #[allow(dead_code)] struct A { field1: bool, field2: Option<bool>, } assert_eq!( - <A as dhall::de::SimpleStaticType>::get_simple_static_type(), - mktype( - dhall_proc_macros::subexpr!({ field1: Bool, field2: Optional Bool }) - ) + <A as dhall::de::StaticType>::static_type(), + mktype(subexpr!({ field1: Bool, field2: Optional Bool })) ); - #[derive(SimpleStaticType)] + #[derive(StaticType)] #[allow(dead_code)] struct B<'a, T: 'a> { field1: &'a T, field2: Option<T>, } - assert_eq!( - <B<'static, bool>>::get_simple_static_type(), - A::get_simple_static_type() - ); + assert_eq!(<B<'static, bool>>::static_type(), A::static_type()); - #[derive(SimpleStaticType)] + #[derive(StaticType)] #[allow(dead_code)] struct C<T>(T, Option<String>); assert_eq!( - <C<bool>>::get_simple_static_type(), - <(bool, Option<String>)>::get_simple_static_type() + <C<bool>>::static_type(), + <(bool, Option<String>)>::static_type() ); - #[derive(SimpleStaticType)] + #[derive(StaticType)] #[allow(dead_code)] struct D(); assert_eq!( - <C<D>>::get_simple_static_type(), - mktype(dhall_proc_macros::subexpr!({ _1: {}, _2: Optional Text })) + <C<D>>::static_type(), + mktype(subexpr!({ _1: {}, _2: Optional Text })) ); - #[derive(SimpleStaticType)] + #[derive(StaticType)] #[allow(dead_code)] enum E<T> { A(T), B(String), }; assert_eq!( - <E<bool>>::get_simple_static_type(), - mktype(dhall_proc_macros::subexpr!(< A: Bool | B: Text >)) + <E<bool>>::static_type(), + mktype(subexpr!(< A: Bool | B: Text >)) ); + + #[derive(StaticType)] + #[allow(dead_code)] + enum F { + A, + B(bool), + }; + assert_eq!(F::static_type(), mktype(subexpr!(< A | B: Bool >))); } |