From d93be73890d0db0d34afaaebd3db1b87d68fb9b7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 16 Apr 2019 21:54:32 +0200 Subject: Prepare for nullary union variants --- dhall/src/binary.rs | 22 ++++++++++++++-- dhall/src/typecheck.rs | 46 +++++++++++++++++++++++++++------ dhall_core/src/core.rs | 8 +++--- dhall_core/src/parser.rs | 22 ++++++++-------- dhall_core/src/printer.rs | 11 ++++++-- dhall_core/src/visitor.rs | 31 ++++++++++++++++++++--- dhall_generator/src/derive.rs | 35 ++++++++++--------------- dhall_generator/src/quote.rs | 59 ++++++++++++++++++++++++++++++------------- 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 { 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::>() } + +fn cbor_map_to_dhall_opt_map( + map: &std::collections::BTreeMap, +) -> Result>, 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::>() +} 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::>()?; 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 { RecordType(BTreeMap), /// `{ k1 = v1, k2 = v2 }` RecordLit(BTreeMap), - /// `< k1 : t1, k2 : t2 >` - UnionType(BTreeMap), - /// `< k1 = t1, k2 : t2, k3 : t3 >` - UnionLit(Label, SubExpr, BTreeMap), + /// `< k1 : t1, k2 >` + UnionType(BTreeMap>), + /// `< k1 = t1, k2 : t2, k3 >` + UnionLit(Label, SubExpr, BTreeMap>), /// `merge x y : t` Merge(SubExpr, SubExpr, Option), /// `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>)>; + BTreeMap>>)>; 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>)>; + <(ParsedSubExpr<'a>, BTreeMap>>)>; 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>)>; children!( + [label(name), expression(expr)] => (name, Option::Some(rc(expr))) )); // TODO: unary union variants rule!(union_type_or_literal_variant_type - <(ParsedExpr<'a>, + <(Option>, (Option<(Label, ParsedSubExpr<'a>)>, - BTreeMap>))>; + BTreeMap>>))>; 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 Display for ExprF { 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>, + mut v: V, + ) -> Result>, 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::>()?; 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(expr: ExprF) -> TokenStream where TS: quote::ToTokens + std::fmt::Debug, { - let quote_map = |m: BTreeMap| -> 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| -> 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(x: Option) -> TokenStream +where + TS: quote::ToTokens + std::fmt::Debug, +{ + match x { + Some(x) => quote!(Some(#x)), + None => quote!(None), + } +} + +fn quote_vec(e: Vec) -> TokenStream +where + TS: quote::ToTokens + std::fmt::Debug, +{ + quote! { vec![ #(#e),* ] } +} + +fn quote_map(m: BTreeMap) -> 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(m: BTreeMap>) -> TokenStream +where + TS: quote::ToTokens + std::fmt::Debug, +{ + quote_map(m.into_iter().map(|(k, v)| (k, quote_opt(v))).collect()) +} -- cgit v1.2.3