summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dhall/src/binary.rs22
-rw-r--r--dhall/src/typecheck.rs46
-rw-r--r--dhall_core/src/core.rs8
-rw-r--r--dhall_core/src/parser.rs22
-rw-r--r--dhall_core/src/printer.rs11
-rw-r--r--dhall_core/src/visitor.rs31
-rw-r--r--dhall_generator/src/derive.rs35
-rw-r--r--dhall_generator/src/quote.rs59
8 files changed, 163 insertions, 71 deletions
diff --git a/dhall/src/binary.rs b/dhall/src/binary.rs
index 72704de..c12aa2a 100644
--- a/dhall/src/binary.rs
+++ b/dhall/src/binary.rs
@@ -143,11 +143,11 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<ParsedExpr, DecodeError> {
Field(x, l)
}
[U64(11), Object(map)] => {
- let map = cbor_map_to_dhall_map(map)?;
+ let map = cbor_map_to_dhall_opt_map(map)?;
UnionType(map)
}
[U64(12), String(l), x, Object(map)] => {
- let map = cbor_map_to_dhall_map(map)?;
+ let map = cbor_map_to_dhall_opt_map(map)?;
let x = cbor_value_to_dhall(&x)?;
let l = Label::from(l.as_str());
UnionLit(l, x, map)
@@ -343,3 +343,21 @@ fn cbor_map_to_dhall_map(
})
.collect::<Result<_, _>>()
}
+
+fn cbor_map_to_dhall_opt_map(
+ map: &std::collections::BTreeMap<cbor::ObjectKey, cbor::Value>,
+) -> Result<std::collections::BTreeMap<Label, Option<ParsedExpr>>, DecodeError>
+{
+ map.iter()
+ .map(|(k, v)| -> Result<(_, _), _> {
+ let k = k.as_string().ok_or_else(|| {
+ DecodeError::WrongFormatError("map/key".to_owned())
+ })?;
+ let v = match v {
+ cbor::Value::Null => None,
+ _ => Some(cbor_value_to_dhall(v)?),
+ };
+ Ok((Label::from(k.as_ref()), v))
+ })
+ .collect::<Result<_, _>>()
+}
diff --git a/dhall/src/typecheck.rs b/dhall/src/typecheck.rs
index 0e1a10e..d9566d0 100644
--- a/dhall/src/typecheck.rs
+++ b/dhall/src/typecheck.rs
@@ -182,10 +182,16 @@ where
}
(UnionType(ktsL0), UnionType(ktsR0)) => {
ktsL0.len() == ktsR0.len()
- && ktsL0
- .iter()
- .zip(ktsR0.iter())
- .all(|((kL, tL), (kR, tR))| kL == kR && go(ctx, tL, tR))
+ && ktsL0.iter().zip(ktsR0.iter()).all(
+ |((kL, tL), (kR, tR))| {
+ kL == kR
+ && match (tL, tR) {
+ (None, None) => true,
+ (Some(tL), Some(tR)) => go(ctx, tL, tR),
+ _ => false,
+ }
+ },
+ )
}
(_, _) => false,
}
@@ -602,7 +608,7 @@ fn type_last_layer(
let t = tx.embed()?;
Ok(RetExpr(dhall::expr!(Optional t)))
}
- RecordType(kts) | UnionType(kts) => {
+ RecordType(kts) => {
// Check that all types are the same const
let mut k = None;
for (x, t) in kts {
@@ -622,6 +628,29 @@ fn type_last_layer(
let k = k.unwrap_or(dhall_core::Const::Type);
Ok(RetType(const_to_type(k)))
}
+ UnionType(kts) => {
+ // Check that all types are the same const
+ let mut k = None;
+ for (x, t) in kts {
+ if let Some(t) = t {
+ let k2 = ensure_is_const!(
+ t.get_type()?,
+ mkerr(InvalidFieldType(x, t))
+ );
+ match k {
+ None => k = Some(k2),
+ Some(k1) if k1 != k2 => {
+ return Err(mkerr(InvalidFieldType(x, t)))
+ }
+ Some(_) => {}
+ }
+ }
+ }
+ // An empty union type has type Type
+ // An union type with only unary variants has type Type
+ let k = k.unwrap_or(dhall_core::Const::Type);
+ Ok(RetType(const_to_type(k)))
+ }
RecordLit(kvs) => {
let kts = kvs
.into_iter()
@@ -636,12 +665,12 @@ fn type_last_layer(
let mut kts: std::collections::BTreeMap<_, _> = kvs
.into_iter()
.map(|(x, v)| {
- let t = v.normalize().embed();
+ let t = v.map(|x| x.normalize().embed());
Ok((x, t))
})
.collect::<Result<_, _>>()?;
let t = v.get_type_move()?.embed()?;
- kts.insert(x, t);
+ kts.insert(x, Some(t));
Ok(RetExpr(UnionType(kts)))
}
Field(r, x) => match r.get_type()?.unroll_ref()?.as_ref() {
@@ -655,11 +684,12 @@ fn type_last_layer(
UnionType(kts) => match kts.get(&x) {
// Constructor has type T -> < x: T, ... >
// TODO: use "_" instead of x
- Some(t) => Ok(RetExpr(Pi(
+ Some(Some(t)) => Ok(RetExpr(Pi(
x.clone(),
t.embed_absurd(),
r.embed(),
))),
+ Some(None) => Ok(RetType(r.into_type())),
None => Err(mkerr(MissingUnionField(x, r))),
},
_ => Err(mkerr(NotARecord(x, r))),
diff --git a/dhall_core/src/core.rs b/dhall_core/src/core.rs
index 6100981..075ef81 100644
--- a/dhall_core/src/core.rs
+++ b/dhall_core/src/core.rs
@@ -186,10 +186,10 @@ pub enum ExprF<SubExpr, Label, Note, Embed> {
RecordType(BTreeMap<Label, SubExpr>),
/// `{ k1 = v1, k2 = v2 }`
RecordLit(BTreeMap<Label, SubExpr>),
- /// `< k1 : t1, k2 : t2 >`
- UnionType(BTreeMap<Label, SubExpr>),
- /// `< k1 = t1, k2 : t2, k3 : t3 >`
- UnionLit(Label, SubExpr, BTreeMap<Label, SubExpr>),
+ /// `< k1 : t1, k2 >`
+ UnionType(BTreeMap<Label, Option<SubExpr>>),
+ /// `< k1 = t1, k2 : t2, k3 >`
+ UnionLit(Label, SubExpr, BTreeMap<Label, Option<SubExpr>>),
/// `merge x y : t`
Merge(SubExpr, SubExpr, Option<SubExpr>),
/// `e.x`
diff --git a/dhall_core/src/parser.rs b/dhall_core/src/parser.rs
index c4ae2e5..a335ccc 100644
--- a/dhall_core/src/parser.rs
+++ b/dhall_core/src/parser.rs
@@ -874,41 +874,41 @@ make_parser! {
rule!(non_empty_union_type_or_literal
<(Option<(Label, ParsedSubExpr<'a>)>,
- BTreeMap<Label, ParsedSubExpr<'a>>)>;
+ BTreeMap<Label, Option<ParsedSubExpr<'a>>>)>;
children!(
[label(l), union_literal_variant_value((e, entries))] => {
- (Option::Some((l, rc(e))), entries)
+ (Option::Some((l, e)), entries)
},
[label(l), union_type_or_literal_variant_type((e, rest))] => {
let (x, mut entries) = rest;
- entries.insert(l, rc(e));
+ entries.insert(l, e);
(x, entries)
},
));
rule!(union_literal_variant_value
- <(ParsedExpr<'a>, BTreeMap<Label, ParsedSubExpr<'a>>)>;
+ <(ParsedSubExpr<'a>, BTreeMap<Label, Option<ParsedSubExpr<'a>>>)>;
children!(
[expression(e), union_type_entry(entries)..] => {
- (e, entries.collect())
+ (rc(e), entries.collect())
},
));
- rule!(union_type_entry<(Label, ParsedSubExpr<'a>)>; children!(
- [label(name), expression(expr)] => (name, rc(expr))
+ rule!(union_type_entry<(Label, Option<ParsedSubExpr<'a>>)>; children!(
+ [label(name), expression(expr)] => (name, Option::Some(rc(expr)))
));
// TODO: unary union variants
rule!(union_type_or_literal_variant_type
- <(ParsedExpr<'a>,
+ <(Option<ParsedSubExpr<'a>>,
(Option<(Label, ParsedSubExpr<'a>)>,
- BTreeMap<Label, ParsedSubExpr<'a>>))>;
+ BTreeMap<Label, Option<ParsedSubExpr<'a>>>))>;
children!(
[expression(e), non_empty_union_type_or_literal(rest)] => {
- (e, rest)
+ (Option::Some(rc(e)), rest)
},
[expression(e)] => {
- (e, (Option::None, BTreeMap::new()))
+ (Option::Some(rc(e)), (Option::None, BTreeMap::new()))
},
));
diff --git a/dhall_core/src/printer.rs b/dhall_core/src/printer.rs
index bb094de..bb3c427 100644
--- a/dhall_core/src/printer.rs
+++ b/dhall_core/src/printer.rs
@@ -92,12 +92,19 @@ impl<SE: Display + Clone, N, E: Display> Display for ExprF<SE, Label, N, E> {
write!(f, "{} = {}", k, v)
})?,
UnionType(a) => fmt_list("< ", " | ", " >", a, f, |(k, v), f| {
- write!(f, "{} : {}", k, v)
+ write!(f, "{} : ", k)?;
+ if let Some(v) = v {
+ v.fmt(f)?
+ }
+ Ok(())
})?,
UnionLit(a, b, c) => {
write!(f, "< {} = {}", a, b)?;
for (k, v) in c {
- write!(f, " | {} : {}", k, v)?;
+ write!(f, " | {}", k)?;
+ if let Some(v) = v {
+ write!(f, ": {}", v)?;
+ }
}
f.write_str(" >")?
}
diff --git a/dhall_core/src/visitor.rs b/dhall_core/src/visitor.rs
index 3b06f8b..1ea3fb1 100644
--- a/dhall_core/src/visitor.rs
+++ b/dhall_core/src/visitor.rs
@@ -106,6 +106,27 @@ where
.map(|(k, x)| Ok((v.visit_label(k)?, v.visit_subexpr(x)?)))
.collect()
}
+ fn btoptmap<'a, V, SE, L, N, E, SE2, L2, N2, E2>(
+ x: &'a BTreeMap<L, Option<SE>>,
+ mut v: V,
+ ) -> Result<BTreeMap<L2, Option<SE2>>, V::Error>
+ where
+ L: Ord,
+ L2: Ord,
+ V: ExprFFallibleVisitor<'a, SE, SE2, L, L2, N, N2, E, E2>,
+ {
+ x.iter()
+ .map(|(k, x)| {
+ Ok((
+ v.visit_label(k)?,
+ match x {
+ Some(x) => Some(v.visit_subexpr(x)?),
+ None => None,
+ },
+ ))
+ })
+ .collect()
+ }
let mut v = self;
use crate::ExprF::*;
@@ -156,10 +177,12 @@ where
NEOptionalLit(e) => NEOptionalLit(v.visit_subexpr(e)?),
RecordType(kts) => RecordType(btmap(kts, v)?),
RecordLit(kvs) => RecordLit(btmap(kvs, v)?),
- UnionType(kts) => UnionType(btmap(kts, v)?),
- UnionLit(k, x, kvs) => {
- UnionLit(v.visit_label(k)?, v.visit_subexpr(x)?, btmap(kvs, v)?)
- }
+ UnionType(kts) => UnionType(btoptmap(kts, v)?),
+ UnionLit(k, x, kvs) => UnionLit(
+ v.visit_label(k)?,
+ v.visit_subexpr(x)?,
+ btoptmap(kvs, v)?,
+ ),
Merge(x, y, t) => Merge(
v.visit_subexpr(x)?,
v.visit_subexpr(y)?,
diff --git a/dhall_generator/src/derive.rs b/dhall_generator/src/derive.rs
index 740081a..852ffc8 100644
--- a/dhall_generator/src/derive.rs
+++ b/dhall_generator/src/derive.rs
@@ -71,35 +71,26 @@ fn derive_for_enum(
.iter()
.map(|v| {
let name = dhall_core::Label::from(v.ident.to_string());
- let ty = match &v.fields {
+ match &v.fields {
+ syn::Fields::Unit => Ok((name, None)),
syn::Fields::Unnamed(fields) if fields.unnamed.is_empty() => {
- Err(Error::new(
- v.span(),
- "Nullary variants are not supported",
- ))
+ Ok((name, None))
}
- syn::Fields::Unnamed(fields) if fields.unnamed.len() > 1 => {
- Err(Error::new(
- v.span(),
- "Variants with more than one field are not supported",
- ))
+ syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
+ let ty = &fields.unnamed.iter().next().unwrap().ty;
+ constraints.push(ty.clone());
+ let ty = get_simple_static_type(ty);
+ Ok((name, Some(quote!(#ty.into()))))
}
- syn::Fields::Unnamed(fields) => {
- Ok(&fields.unnamed.iter().next().unwrap().ty)
- }
- syn::Fields::Named(_) => Err(Error::new(
+ syn::Fields::Unnamed(_) => Err(Error::new(
v.span(),
- "Named variants are not supported",
+ "Variants with more than one field are not supported",
)),
- syn::Fields::Unit => Err(Error::new(
+ syn::Fields::Named(_) => Err(Error::new(
v.span(),
- "Nullary variants are not supported",
+ "Named variants are not supported",
)),
- };
- let ty = ty?;
- constraints.push(ty.clone());
- let ty = get_simple_static_type(ty);
- Ok((name, quote!(#ty.into())))
+ }
})
.collect::<Result<_, Error>>()?;
diff --git a/dhall_generator/src/quote.rs b/dhall_generator/src/quote.rs
index 89107a9..c335666 100644
--- a/dhall_generator/src/quote.rs
+++ b/dhall_generator/src/quote.rs
@@ -31,23 +31,6 @@ pub fn quote_exprf<TS>(expr: ExprF<TS, Label, X, X>) -> TokenStream
where
TS: quote::ToTokens + std::fmt::Debug,
{
- let quote_map = |m: BTreeMap<Label, TS>| -> TokenStream {
- let entries = m.into_iter().map(|(k, v)| {
- let k = quote_label(&k);
- quote!(m.insert(#k, #v);)
- });
- quote! { {
- use std::collections::BTreeMap;
- let mut m = BTreeMap::new();
- #( #entries )*
- m
- } }
- };
-
- let quote_vec = |e: Vec<TS>| -> TokenStream {
- quote! { vec![ #(#e),* ] }
- };
-
use dhall_core::ExprF::*;
match expr {
Var(_) => unreachable!(),
@@ -106,7 +89,7 @@ where
quote! { dhall_core::ExprF::RecordLit(#m) }
}
UnionType(m) => {
- let m = quote_map(m);
+ let m = quote_opt_map(m);
quote! { dhall_core::ExprF::UnionType(#m) }
}
e => unimplemented!("{:?}", e),
@@ -206,3 +189,43 @@ fn quote_label(l: &Label) -> TokenStream {
fn rc(x: TokenStream) -> TokenStream {
quote! { dhall_core::rc(#x) }
}
+
+fn quote_opt<TS>(x: Option<TS>) -> TokenStream
+where
+ TS: quote::ToTokens + std::fmt::Debug,
+{
+ match x {
+ Some(x) => quote!(Some(#x)),
+ None => quote!(None),
+ }
+}
+
+fn quote_vec<TS>(e: Vec<TS>) -> TokenStream
+where
+ TS: quote::ToTokens + std::fmt::Debug,
+{
+ quote! { vec![ #(#e),* ] }
+}
+
+fn quote_map<TS>(m: BTreeMap<Label, TS>) -> TokenStream
+where
+ TS: quote::ToTokens + std::fmt::Debug,
+{
+ let entries = m.into_iter().map(|(k, v)| {
+ let k = quote_label(&k);
+ quote!(m.insert(#k, #v);)
+ });
+ quote! { {
+ use std::collections::BTreeMap;
+ let mut m = BTreeMap::new();
+ #( #entries )*
+ m
+ } }
+}
+
+fn quote_opt_map<TS>(m: BTreeMap<Label, Option<TS>>) -> TokenStream
+where
+ TS: quote::ToTokens + std::fmt::Debug,
+{
+ quote_map(m.into_iter().map(|(k, v)| (k, quote_opt(v))).collect())
+}