summaryrefslogtreecommitdiff
path: root/dhall/src/phase/binary.rs
diff options
context:
space:
mode:
authorNadrieril2019-05-12 18:44:28 +0200
committerNadrieril2019-05-12 18:44:44 +0200
commitc2b4a2d9b40efbe4f6cb6fd04f6cb90639f4985f (patch)
tree02e0f7b57b56f949240cedbdfabac234d9486834 /dhall/src/phase/binary.rs
parent2d1a333d6c1e8571ca91d29333c284104153b0ef (diff)
Implement binary encoding
Closes #39
Diffstat (limited to 'dhall/src/phase/binary.rs')
-rw-r--r--dhall/src/phase/binary.rs311
1 files changed, 291 insertions, 20 deletions
diff --git a/dhall/src/phase/binary.rs b/dhall/src/phase/binary.rs
index 7f72e80..a3ab5de 100644
--- a/dhall/src/phase/binary.rs
+++ b/dhall/src/phase/binary.rs
@@ -2,24 +2,32 @@ use itertools::Itertools;
use serde_cbor::value::value as cbor;
use std::iter::FromIterator;
+use dhall_syntax::map::DupTreeMap;
use dhall_syntax::{
rc, ExprF, FilePrefix, Hash, Import, ImportHashed, ImportLocation,
ImportMode, Integer, InterpolatedText, Label, Natural, Scheme, SubExpr,
- URL, V, X,
+ URL, V,
};
-use crate::error::DecodeError;
+use crate::error::{DecodeError, EncodeError};
+use crate::phase::{DecodedSubExpr, ParsedSubExpr};
-type ParsedExpr = SubExpr<X, Import>;
-
-pub fn decode(data: &[u8]) -> Result<ParsedExpr, DecodeError> {
+pub fn decode(data: &[u8]) -> Result<DecodedSubExpr, DecodeError> {
match serde_cbor::de::from_slice(data) {
Ok(v) => cbor_value_to_dhall(&v),
Err(e) => Err(DecodeError::CBORError(e)),
}
}
-fn cbor_value_to_dhall(data: &cbor::Value) -> Result<ParsedExpr, DecodeError> {
+//TODO: encode normalized expression too
+pub fn encode(expr: &ParsedSubExpr) -> Result<Vec<u8>, EncodeError> {
+ serde_cbor::ser::to_vec(&Serialize::Expr(expr))
+ .map_err(|e| EncodeError::CBORError(e))
+}
+
+fn cbor_value_to_dhall(
+ data: &cbor::Value,
+) -> Result<DecodedSubExpr, DecodeError> {
use cbor::Value::*;
use dhall_syntax::{BinOp, Builtin, Const};
use ExprF::*;
@@ -238,12 +246,11 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<ParsedExpr, DecodeError> {
))?,
};
let path = rest
- .map(|s| {
- s.as_string().ok_or_else(|| {
- DecodeError::WrongFormatError(
- "import/remote/path".to_owned(),
- )
- })
+ .map(|s| match s.as_string() {
+ Some(s) => Ok(s.clone()),
+ None => Err(DecodeError::WrongFormatError(
+ "import/remote/path".to_owned(),
+ )),
})
.collect::<Result<_, _>>()?;
ImportLocation::Remote(URL {
@@ -265,12 +272,11 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<ParsedExpr, DecodeError> {
))?,
};
let path = rest
- .map(|s| {
- s.as_string().ok_or_else(|| {
- DecodeError::WrongFormatError(
- "import/local/path".to_owned(),
- )
- })
+ .map(|s| match s.as_string() {
+ Some(s) => Ok(s.clone()),
+ None => Err(DecodeError::WrongFormatError(
+ "import/local/path".to_owned(),
+ )),
})
.collect::<Result<_, _>>()?;
ImportLocation::Local(prefix, path)
@@ -336,7 +342,7 @@ fn cbor_map_to_dhall_map<'a, T>(
map: impl IntoIterator<Item = (&'a cbor::ObjectKey, &'a cbor::Value)>,
) -> Result<T, DecodeError>
where
- T: FromIterator<(Label, ParsedExpr)>,
+ T: FromIterator<(Label, DecodedSubExpr)>,
{
map.into_iter()
.map(|(k, v)| -> Result<(_, _), _> {
@@ -353,7 +359,7 @@ fn cbor_map_to_dhall_opt_map<'a, T>(
map: impl IntoIterator<Item = (&'a cbor::ObjectKey, &'a cbor::Value)>,
) -> Result<T, DecodeError>
where
- T: FromIterator<(Label, Option<ParsedExpr>)>,
+ T: FromIterator<(Label, Option<DecodedSubExpr>)>,
{
map.into_iter()
.map(|(k, v)| -> Result<(_, _), _> {
@@ -368,3 +374,268 @@ where
})
.collect::<Result<_, _>>()
}
+
+enum Serialize<'a> {
+ Expr(&'a ParsedSubExpr),
+ CBOR(cbor::Value),
+ RecordMap(&'a DupTreeMap<Label, ParsedSubExpr>),
+ UnionMap(&'a DupTreeMap<Label, Option<ParsedSubExpr>>),
+}
+
+macro_rules! count {
+ (@replace_with $_t:tt $sub:expr) => { $sub };
+ ($($tts:tt)*) => {0usize $(+ count!(@replace_with $tts 1usize))*};
+}
+
+macro_rules! ser_seq {
+ ($ser:expr; $($elt:expr),* $(,)?) => {{
+ use serde::ser::SerializeSeq;
+ let count = count!($($elt)*);
+ let mut ser_seq = $ser.serialize_seq(Some(count))?;
+ $(
+ ser_seq.serialize_element(&$elt)?;
+ )*
+ ser_seq.end()
+ }};
+}
+
+fn serialize_subexpr<S>(ser: S, e: &ParsedSubExpr) -> Result<S::Ok, S::Error>
+where
+ S: serde::ser::Serializer,
+{
+ use cbor::Value::{String, I64, U64};
+ use dhall_syntax::ExprF::*;
+ use std::iter::once;
+
+ use self::Serialize::{RecordMap, UnionMap};
+ fn expr(x: &ParsedSubExpr) -> self::Serialize<'_> {
+ self::Serialize::Expr(x)
+ }
+ fn cbor<'a>(v: cbor::Value) -> self::Serialize<'a> {
+ self::Serialize::CBOR(v)
+ }
+ fn tag<'a>(x: u64) -> self::Serialize<'a> {
+ cbor(U64(x))
+ }
+ fn null<'a>() -> self::Serialize<'a> {
+ cbor(cbor::Value::Null)
+ }
+ fn label<'a>(l: &Label) -> self::Serialize<'a> {
+ cbor(cbor::Value::String(l.into()))
+ }
+
+ match e.as_ref() {
+ Const(c) => ser.serialize_str(&c.to_string()),
+ Builtin(b) => ser.serialize_str(&b.to_string()),
+ BoolLit(b) => ser.serialize_bool(*b),
+ NaturalLit(n) => ser_seq!(ser; tag(15), U64(*n as u64)),
+ IntegerLit(n) => ser_seq!(ser; tag(16), I64(*n as i64)),
+ DoubleLit(n) => {
+ let n: f64 = (*n).into();
+ ser.serialize_f64(n)
+ }
+ BoolIf(x, y, z) => ser_seq!(ser; tag(14), expr(x), expr(y), expr(z)),
+ Var(V(l, n)) if l == &"_".into() => ser.serialize_u64(*n as u64),
+ Var(V(l, n)) => ser_seq!(ser; label(l), U64(*n as u64)),
+ Lam(l, x, y) if l == &"_".into() => {
+ ser_seq!(ser; tag(1), expr(x), expr(y))
+ }
+ Lam(l, x, y) => ser_seq!(ser; tag(1), label(l), expr(x), expr(y)),
+ Pi(l, x, y) if l == &"_".into() => {
+ ser_seq!(ser; tag(2), expr(x), expr(y))
+ }
+ Pi(l, x, y) => ser_seq!(ser; tag(2), label(l), expr(x), expr(y)),
+ // TODO: multilet
+ Let(l, None, x, y) => {
+ ser_seq!(ser; tag(25), label(l), null(), expr(x), expr(y))
+ }
+ Let(l, Some(t), x, y) => {
+ ser_seq!(ser; tag(25), label(l), expr(t), expr(x), expr(y))
+ }
+ App(_, _) => {
+ let (f, args) = collect_nested_applications(e);
+ ser.collect_seq(
+ once(tag(0))
+ .chain(once(expr(f)))
+ .chain(args.into_iter().rev().map(expr)),
+ )
+ }
+ Annot(x, y) => ser_seq!(ser; tag(26), expr(x), expr(y)),
+ OldOptionalLit(None, t) => ser_seq!(ser; tag(5), expr(t)),
+ OldOptionalLit(Some(x), t) => ser_seq!(ser; tag(5), expr(t), expr(x)),
+ SomeLit(x) => ser_seq!(ser; tag(5), null(), expr(x)),
+ EmptyListLit(x) => ser_seq!(ser; tag(4), expr(x)),
+ NEListLit(xs) => ser.collect_seq(
+ once(tag(4)).chain(once(null())).chain(xs.iter().map(expr)),
+ ),
+ TextLit(xs) => {
+ use dhall_syntax::InterpolatedTextContents::{Expr, Text};
+ ser.collect_seq(once(tag(18)).chain(xs.iter().map(|x| match x {
+ Expr(x) => expr(x),
+ Text(x) => cbor(String(x.clone())),
+ })))
+ }
+ RecordType(map) => ser_seq!(ser; tag(7), RecordMap(map)),
+ RecordLit(map) => ser_seq!(ser; tag(8), RecordMap(map)),
+ UnionType(map) => ser_seq!(ser; tag(11), UnionMap(map)),
+ UnionLit(l, x, map) => {
+ ser_seq!(ser; tag(12), label(l), expr(x), UnionMap(map))
+ }
+ Field(x, l) => ser_seq!(ser; tag(9), expr(x), label(l)),
+ BinOp(op, x, y) => {
+ use dhall_syntax::BinOp::*;
+ let op = match op {
+ BoolOr => 0,
+ BoolAnd => 1,
+ BoolEQ => 2,
+ BoolNE => 3,
+ NaturalPlus => 4,
+ NaturalTimes => 5,
+ TextAppend => 6,
+ ListAppend => 7,
+ RecursiveRecordMerge => 8,
+ RightBiasedRecordMerge => 9,
+ RecursiveRecordTypeMerge => 10,
+ ImportAlt => 11,
+ };
+ ser_seq!(ser; tag(3), U64(op), expr(x), expr(y))
+ }
+ Merge(x, y, None) => ser_seq!(ser; tag(6), expr(x), expr(y)),
+ Merge(x, y, Some(z)) => {
+ ser_seq!(ser; tag(6), expr(x), expr(y), expr(z))
+ }
+ Projection(x, ls) => ser.collect_seq(
+ once(tag(10))
+ .chain(once(expr(x)))
+ .chain(ls.iter().map(label)),
+ ),
+ Embed(import) => serialize_import(ser, import),
+ }
+}
+
+fn serialize_import<S>(ser: S, import: &Import) -> Result<S::Ok, S::Error>
+where
+ S: serde::ser::Serializer,
+{
+ use cbor::Value::{Array, Null, String, U64};
+ use serde::ser::SerializeSeq;
+
+ let count = 4 + match &import.location_hashed.location {
+ ImportLocation::Remote(url) => 3 + url.path.len(),
+ ImportLocation::Local(_, path) => path.len(),
+ ImportLocation::Env(_) => 1,
+ ImportLocation::Missing => 0,
+ };
+ let mut ser_seq = ser.serialize_seq(Some(count))?;
+
+ ser_seq.serialize_element(&U64(24))?;
+
+ let hash = match &import.location_hashed.hash {
+ None => Null,
+ Some(h) => {
+ Array(vec![String(h.protocol.clone()), String(h.hash.clone())])
+ }
+ };
+ ser_seq.serialize_element(&hash)?;
+
+ let mode = match import.mode {
+ ImportMode::Code => 0,
+ ImportMode::RawText => 1,
+ };
+ ser_seq.serialize_element(&U64(mode))?;
+
+ let scheme = match &import.location_hashed.location {
+ ImportLocation::Remote(url) => match url.scheme {
+ Scheme::HTTP => 0,
+ Scheme::HTTPS => 1,
+ },
+ ImportLocation::Local(prefix, _) => match prefix {
+ FilePrefix::Absolute => 2,
+ FilePrefix::Here => 3,
+ FilePrefix::Parent => 4,
+ FilePrefix::Home => 5,
+ },
+ ImportLocation::Env(_) => 6,
+ ImportLocation::Missing => 7,
+ };
+ ser_seq.serialize_element(&U64(scheme))?;
+
+ match &import.location_hashed.location {
+ ImportLocation::Remote(url) => {
+ match &url.headers {
+ None => ser_seq.serialize_element(&Null)?,
+ Some(_x) => unimplemented!(),
+ // match cbor_value_to_dhall(&x)?.as_ref() {
+ // Embed(import) => Some(Box::new(
+ // import.location_hashed.clone(),
+ // )),
+ // }
+ };
+ ser_seq.serialize_element(&url.authority)?;
+ for p in &url.path {
+ ser_seq.serialize_element(p)?;
+ }
+ match &url.query {
+ None => ser_seq.serialize_element(&Null)?,
+ Some(x) => ser_seq.serialize_element(x)?,
+ };
+ }
+ ImportLocation::Local(_, path) => {
+ for p in path {
+ ser_seq.serialize_element(p)?;
+ }
+ }
+ ImportLocation::Env(env) => {
+ ser_seq.serialize_element(env)?;
+ }
+ ImportLocation::Missing => {}
+ }
+
+ ser_seq.end()
+}
+
+impl<'a> serde::ser::Serialize for Serialize<'a> {
+ fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::ser::Serializer,
+ {
+ match self {
+ Serialize::Expr(e) => serialize_subexpr(ser, e),
+ Serialize::CBOR(v) => v.serialize(ser),
+ Serialize::RecordMap(map) => {
+ ser.collect_map(map.iter().map(|(k, v)| {
+ (cbor::Value::String(k.into()), Serialize::Expr(v))
+ }))
+ }
+ Serialize::UnionMap(map) => {
+ ser.collect_map(map.iter().map(|(k, v)| {
+ let v = match v {
+ Some(x) => Serialize::Expr(x),
+ None => Serialize::CBOR(cbor::Value::Null),
+ };
+ (cbor::Value::String(k.into()), v)
+ }))
+ }
+ }
+ }
+}
+
+fn collect_nested_applications<'a, N, E>(
+ e: &'a SubExpr<N, E>,
+) -> (&'a SubExpr<N, E>, Vec<&'a SubExpr<N, E>>) {
+ fn go<'a, N, E>(
+ e: &'a SubExpr<N, E>,
+ vec: &mut Vec<&'a SubExpr<N, E>>,
+ ) -> &'a SubExpr<N, E> {
+ match e.as_ref() {
+ ExprF::App(f, a) => {
+ vec.push(a);
+ go(f, vec)
+ }
+ _ => e,
+ }
+ }
+ let mut vec = vec![];
+ let e = go(e, &mut vec);
+ (e, vec)
+}