summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadrieril2020-03-08 17:11:34 +0000
committerNadrieril2020-03-31 21:44:01 +0100
commit84796fd247eb1a13fcd092a7cd7ec2d587b261bd (patch)
tree1be14161e1181ae058329f4d6bc29cc9c8409aab
parent5a5aa49e64197899006751db72e404f4b2292d4e (diff)
Add SimpleValue type to facilitate deserialization
-rw-r--r--dhall/src/lib.rs10
-rw-r--r--dhall/src/semantics/nze/nir.rs34
-rw-r--r--dhall/src/simple.rs29
-rw-r--r--serde_dhall/src/lib.rs17
-rw-r--r--serde_dhall/src/serde.rs102
-rw-r--r--serde_dhall/tests/de.rs2
6 files changed, 119 insertions, 75 deletions
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<SimpleValue, Expr> {
+ 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<RefCell> 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<SimpleValue> {
+ 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::<Option<_>>()?,
+ ),
+ NirKind::RecordLit(kvs) => SValKind::Record(
+ kvs.iter()
+ .map(|(k, v)| Some((k.into(), v.to_simple_value()?)))
+ .collect::<Option<_>>()?,
+ ),
+ 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<SValKind>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum SValKind {
+ Lit(LitKind),
+ Optional(Option<SimpleValue>),
+ List(Vec<SimpleValue>),
+ Record(BTreeMap<String, SimpleValue>),
+ Union(String, Option<SimpleValue>),
+ 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<Self> {
+ pub fn from_str(
+ s: &str,
+ ty: Option<&Value>,
+ ) -> super::de::Result<Self> {
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<SimpleValue, NormalizedExpr> {
+ 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<Self> {
+ fn from_dhall(v: &Value) -> super::de::Result<Self> {
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<Self> {
- 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::<Baz>("< X | Y: Integer >.X"), Baz::X);
+
+ assert!(from_str_auto_type::<Baz>("< X | Y: Integer >.Y").is_err());
}
#[test]