From 84796fd247eb1a13fcd092a7cd7ec2d587b261bd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 8 Mar 2020 17:11:34 +0000 Subject: Add SimpleValue type to facilitate deserialization --- dhall/src/lib.rs | 10 +++- dhall/src/semantics/nze/nir.rs | 34 ++++++++++++++ dhall/src/simple.rs | 29 ++++++++++++ serde_dhall/src/lib.rs | 17 ++++--- serde_dhall/src/serde.rs | 102 ++++++++++++++--------------------------- serde_dhall/tests/de.rs | 2 + 6 files changed, 119 insertions(+), 75 deletions(-) create mode 100644 dhall/src/simple.rs diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 24e4377..0be6db3 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -9,6 +9,7 @@ mod tests; pub mod error; pub mod semantics; +pub mod simple; pub mod syntax; use std::fmt::Display; @@ -29,6 +30,7 @@ pub type ParsedExpr = Expr; pub type DecodedExpr = Expr; pub type ResolvedExpr = Expr; pub type NormalizedExpr = Expr; +pub use crate::simple::{SValKind, SimpleValue}; #[derive(Debug, Clone)] pub struct Parsed(ParsedExpr, ImportLocation); @@ -53,7 +55,7 @@ pub struct Typed { pub struct Normalized(Nir); /// Controls conversion from `Nir` to `Expr` -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub(crate) struct ToExprOptions { /// Whether to convert all variables to `_` pub(crate) alpha: bool, @@ -138,7 +140,11 @@ impl Normalized { /// Converts a value back to the corresponding AST expression. pub fn to_expr(&self) -> NormalizedExpr { - self.0.to_expr(ToExprOptions { alpha: false }) + self.0.to_expr(ToExprOptions::default()) + } + /// Converts a value into a SimpleValue. + pub fn to_simple_value(&self) -> Result { + self.0.to_simple_value().ok_or_else(|| self.to_expr()) } /// Converts a value back to the corresponding Hir expression. pub(crate) fn to_hir(&self) -> Hir { diff --git a/dhall/src/semantics/nze/nir.rs b/dhall/src/semantics/nze/nir.rs index 32ef590..9e3644c 100644 --- a/dhall/src/semantics/nze/nir.rs +++ b/dhall/src/semantics/nze/nir.rs @@ -11,6 +11,7 @@ use crate::syntax::{ Span, }; use crate::{NormalizedExpr, ToExprOptions}; +use crate::{SValKind, SimpleValue}; /// Stores a possibly unevaluated value. Gets (partially) normalized on-demand, sharing computation /// automatically. Uses a Rc to share computation. @@ -142,6 +143,39 @@ impl Nir { pub(crate) fn to_expr_tyenv(&self, tyenv: &TyEnv) -> NormalizedExpr { self.to_hir(tyenv.as_varenv()).to_expr_tyenv(tyenv) } + pub(crate) fn to_simple_value(&self) -> Option { + Some(SimpleValue::new(match self.kind() { + NirKind::Lit(lit) => SValKind::Lit(lit.clone()), + NirKind::TextLit(x) => SValKind::Text( + x.as_text() + .expect("Normal form should ensure the text is a string"), + ), + NirKind::EmptyOptionalLit(_) => SValKind::Optional(None), + NirKind::NEOptionalLit(x) => { + SValKind::Optional(Some(x.to_simple_value()?)) + } + NirKind::EmptyListLit(_) => SValKind::List(vec![]), + NirKind::NEListLit(xs) => SValKind::List( + xs.iter() + .map(|v| v.to_simple_value()) + .collect::>()?, + ), + NirKind::RecordLit(kvs) => SValKind::Record( + kvs.iter() + .map(|(k, v)| Some((k.into(), v.to_simple_value()?))) + .collect::>()?, + ), + NirKind::UnionLit(field, x, _) => { + SValKind::Union(field.into(), Some(x.to_simple_value()?)) + } + NirKind::UnionConstructor(field, ty) + if ty.get(field).map(|f| f.is_some()) == Some(false) => + { + SValKind::Union(field.into(), None) + } + _ => return None, + })) + } pub(crate) fn normalize(&self) { self.0.normalize() diff --git a/dhall/src/simple.rs b/dhall/src/simple.rs new file mode 100644 index 0000000..6457291 --- /dev/null +++ b/dhall/src/simple.rs @@ -0,0 +1,29 @@ +use std::collections::BTreeMap; + +use crate::syntax::LitKind; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SimpleValue { + kind: Box, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SValKind { + Lit(LitKind), + Optional(Option), + List(Vec), + Record(BTreeMap), + Union(String, Option), + Text(String), +} + +impl SimpleValue { + pub(crate) fn new(kind: SValKind) -> Self { + SimpleValue { + kind: Box::new(kind), + } + } + pub fn kind(&self) -> &SValKind { + self.kind.as_ref() + } +} diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 0a53420..55e098c 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -182,16 +182,19 @@ pub use value::Value; #[doc(hidden)] pub mod value { use dhall::syntax::Builtin; - use dhall::{Normalized, NormalizedExpr, Parsed}; + use dhall::{Normalized, NormalizedExpr, Parsed, SimpleValue}; - use super::de::{Error, Result}; + use super::de::Error; /// A Dhall value #[derive(Debug, Clone, PartialEq, Eq)] pub struct Value(Normalized); impl Value { - pub fn from_str(s: &str, ty: Option<&Value>) -> Result { + pub fn from_str( + s: &str, + ty: Option<&Value>, + ) -> super::de::Result { Value::from_str_using_dhall_error_type(s, ty).map_err(Error::Dhall) } fn from_str_using_dhall_error_type( @@ -205,8 +208,10 @@ pub mod value { }; Ok(Value(typed.normalize())) } - pub(crate) fn to_expr(&self) -> NormalizedExpr { - self.0.to_expr() + pub(crate) fn to_simple_value( + &self, + ) -> Result { + self.0.to_simple_value() } pub(crate) fn as_normalized(&self) -> &Normalized { &self.0 @@ -241,7 +246,7 @@ pub mod value { impl super::de::sealed::Sealed for Value {} impl super::de::Deserialize for Value { - fn from_dhall(v: &Value) -> Result { + fn from_dhall(v: &Value) -> super::de::Result { Ok(v.clone()) } } diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 4fd7815..9cb67d1 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -4,25 +4,31 @@ use serde::de::value::{ MapAccessDeserializer, MapDeserializer, SeqDeserializer, }; -use dhall::syntax::{ExprKind, LitKind}; -use dhall::NormalizedExpr; +use dhall::syntax::LitKind; +use dhall::{SValKind, SimpleValue}; use crate::de::{Deserialize, Error, Result}; use crate::Value; impl<'a, T> crate::de::sealed::Sealed for T where T: serde::Deserialize<'a> {} +struct Deserializer<'a>(Cow<'a, SimpleValue>); + 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()))) + let sval = v.to_simple_value().map_err(|expr| { + Error::Deserialize(format!( + "this cannot be deserialized into the serde data model: {}", + expr + )) + })?; + T::deserialize(Deserializer(Cow::Owned(sval))) } } -struct Deserializer<'a>(Cow<'a, NormalizedExpr>); - impl<'de: 'a, 'a> serde::de::IntoDeserializer<'de, Error> for Deserializer<'a> { type Deserializer = Deserializer<'a>; fn into_deserializer(self) -> Self::Deserializer { @@ -38,17 +44,11 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { V: serde::de::Visitor<'de>, { use std::convert::TryInto; - use ExprKind::*; use LitKind::*; - let expr = self.0.as_ref(); - let not_serde_compatible = || { - Err(Error::Deserialize(format!( - "this cannot be deserialized into the serde data model: {}", - expr - ))) - }; + use SValKind::*; - match expr.kind() { + let val = |x| Deserializer(Cow::Borrowed(x)); + match self.0.kind() { Lit(Bool(x)) => visitor.visit_bool(*x), Lit(Natural(x)) => { if let Ok(x64) = (*x).try_into() { @@ -69,54 +69,25 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { } } Lit(Double(x)) => visitor.visit_f64((*x).into()), - TextLit(x) => { - // Normal form ensures that the tail is empty. - assert!(x.tail().is_empty()); - visitor.visit_str(x.head()) + Text(x) => visitor.visit_str(x), + List(xs) => { + visitor.visit_seq(SeqDeserializer::new(xs.iter().map(val))) } - EmptyListLit(..) => { - visitor.visit_seq(SeqDeserializer::new(None::<()>.into_iter())) - } - NEListLit(xs) => visitor.visit_seq(SeqDeserializer::new( - xs.iter().map(|x| Deserializer(Cow::Borrowed(x))), + Optional(None) => visitor.visit_none(), + Optional(Some(x)) => visitor.visit_some(val(x)), + Record(m) => visitor.visit_map(MapDeserializer::new( + m.iter().map(|(k, v)| (k.as_ref(), val(v))), )), - SomeLit(x) => visitor.visit_some(Deserializer(Cow::Borrowed(x))), - App(f, x) => match f.kind() { - Builtin(dhall::syntax::Builtin::OptionalNone) => { - visitor.visit_none() - } - Field(y, name) => match y.kind() { - UnionType(..) => { - let name: String = name.into(); - visitor.visit_enum(MapAccessDeserializer::new( - MapDeserializer::new( - Some((name, Deserializer(Cow::Borrowed(x)))) - .into_iter(), - ), - )) - } - _ => not_serde_compatible(), - }, - _ => not_serde_compatible(), - }, - RecordLit(m) => visitor - .visit_map(MapDeserializer::new(m.iter().map(|(k, v)| { - (k.as_ref(), Deserializer(Cow::Borrowed(v))) - }))), - Field(y, name) => match y.kind() { - UnionType(..) => { - let name: String = name.into(); - visitor.visit_enum(MapAccessDeserializer::new( - MapDeserializer::new(Some((name, ())).into_iter()), - )) - } - _ => not_serde_compatible(), - }, - Const(..) | Var(..) | Lam(..) | Pi(..) | Let(..) | Annot(..) - | Assert(..) | Builtin(..) | BinOp(..) | BoolIf(..) - | RecordType(..) | UnionType(..) | Merge(..) | ToMap(..) - | Projection(..) | ProjectionByExpr(..) | Completion(..) - | Import(..) => not_serde_compatible(), + Union(field_name, Some(x)) => visitor.visit_enum( + MapAccessDeserializer::new(MapDeserializer::new( + Some((field_name.as_str(), val(x))).into_iter(), + )), + ), + Union(field_name, None) => visitor.visit_enum( + MapAccessDeserializer::new(MapDeserializer::new( + Some((field_name.as_str(), ())).into_iter(), + )), + ), } } @@ -124,14 +95,11 @@ impl<'de: 'a, 'a> serde::Deserializer<'de> for Deserializer<'a> { where V: serde::de::Visitor<'de>, { - use ExprKind::*; - let expr = self.0.as_ref(); - - match expr.kind() { + let val = |x| Deserializer(Cow::Borrowed(x)); + match self.0.kind() { // Blindly takes keys in sorted order. - RecordLit(m) => visitor.visit_seq(SeqDeserializer::new( - m.iter().map(|(_, v)| Deserializer(Cow::Borrowed(v))), - )), + SValKind::Record(m) => visitor + .visit_seq(SeqDeserializer::new(m.iter().map(|(_, v)| val(v)))), _ => self.deserialize_any(visitor), } } diff --git a/serde_dhall/tests/de.rs b/serde_dhall/tests/de.rs index 74912dd..4042611 100644 --- a/serde_dhall/tests/de.rs +++ b/serde_dhall/tests/de.rs @@ -51,6 +51,8 @@ fn test_de_typed() { Y(i64), } assert_eq!(parse::("< X | Y: Integer >.X"), Baz::X); + + assert!(from_str_auto_type::("< X | Y: Integer >.Y").is_err()); } #[test] -- cgit v1.2.3