use std::collections::{BTreeMap, HashMap};
use dhall::builtins::Builtin;
use dhall::operations::OpKind;
use dhall::semantics::{Hir, HirKind, Nir, NirKind};
use dhall::syntax::{Expr, ExprKind, NumKind, Span};
use crate::{Error, ErrorKind, FromDhall, Result, Sealed};
#[doc(hidden)]
#[derive(Debug, Clone)]
pub struct Value {
hir: Hir,
as_simple_val: Option<SimpleValue>,
as_simple_ty: Option<SimpleType>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum SimpleValue {
Num(NumKind),
Text(String),
Optional(Option<Box<SimpleValue>>),
List(Vec<SimpleValue>),
Record(BTreeMap<String, SimpleValue>),
Union(String, Option<Box<SimpleValue>>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SimpleType {
Bool,
Natural,
Integer,
Double,
Text,
Optional(Box<SimpleType>),
List(Box<SimpleType>),
Record(HashMap<String, SimpleType>),
Union(HashMap<String, Option<SimpleType>>),
}
impl Value {
pub(crate) fn from_nir(x: &Nir) -> Self {
Value {
hir: x.to_hir_noenv(),
as_simple_val: SimpleValue::from_nir(x),
as_simple_ty: SimpleType::from_nir(x),
}
}
pub(crate) fn as_hir(&self) -> &Hir {
&self.hir
}
pub(crate) fn to_simple_value(&self) -> Option<SimpleValue> {
self.as_simple_val.clone()
}
pub(crate) fn to_simple_type(&self) -> Option<SimpleType> {
self.as_simple_ty.clone()
}
pub(crate) fn to_expr(&self) -> Expr {
self.hir.to_expr(Default::default())
}
}
impl SimpleValue {
pub(crate) fn from_nir(nir: &Nir) -> Option<Self> {
Some(match nir.kind() {
NirKind::Num(lit) => SimpleValue::Num(lit.clone()),
NirKind::TextLit(x) => SimpleValue::Text(
x.as_text()
.expect("Normal form should ensure the text is a string"),
),
NirKind::EmptyOptionalLit(_) => SimpleValue::Optional(None),
NirKind::NEOptionalLit(x) => {
SimpleValue::Optional(Some(Box::new(Self::from_nir(x)?)))
}
NirKind::EmptyListLit(t) => {
if let NirKind::RecordType(kts) = t.kind() {
if kts.len() == 2
&& kts.contains_key("mapKey")
&& kts.contains_key("mapValue")
{
return Some(SimpleValue::Record(Default::default()));
}
}
SimpleValue::List(vec![])
}
NirKind::NEListLit(xs) => {
if let NirKind::RecordLit(kvs) = xs[0].kind() {
if kvs.len() == 2
&& kvs.contains_key("mapKey")
&& kvs.contains_key("mapValue")
{
let convert_entry = |x: &Nir| match x.kind() {
NirKind::RecordLit(kvs) => {
let k = match kvs.get("mapKey").unwrap().kind()
{
NirKind::TextLit(t)
if t.as_text().is_some() =>
{
t.as_text().unwrap()
}
_ => panic!(
"Expected `mapKey` to be a text \
literal"
),
};
let v = Self::from_nir(
kvs.get("mapValue").unwrap(),
)?;
Some((k, v))
}
_ => unreachable!("Internal type error"),
};
return Some(SimpleValue::Record(
xs.iter()
.map(convert_entry)
.collect::<Option<_>>()?,
));
}
}
SimpleValue::List(
xs.iter().map(Self::from_nir).collect::<Option<_>>()?,
)
}
NirKind::RecordLit(kvs) => SimpleValue::Record(
kvs.iter()
.map(|(k, v)| Some((k.to_string(), Self::from_nir(v)?)))
.collect::<Option<_>>()?,
),
NirKind::UnionLit(field, x, _) => SimpleValue::Union(
field.into(),
Some(Box::new(Self::from_nir(x)?)),
),
NirKind::UnionConstructor(field, ty)
if ty.get(field).map(|f| f.is_some()) == Some(false) =>
{
SimpleValue::Union(field.into(), None)
}
_ => return None,
})
}
}
impl SimpleType {
pub(crate) fn from_nir(nir: &Nir) -> Option<Self> {
Some(match nir.kind() {
NirKind::BuiltinType(b) => match b {
Builtin::Bool => SimpleType::Bool,
Builtin::Natural => SimpleType::Natural,
Builtin::Integer => SimpleType::Integer,
Builtin::Double => SimpleType::Double,
Builtin::Text => SimpleType::Text,
_ => unreachable!(),
},
NirKind::OptionalType(t) => {
SimpleType::Optional(Box::new(Self::from_nir(t)?))
}
NirKind::ListType(t) => {
SimpleType::List(Box::new(Self::from_nir(t)?))
}
NirKind::RecordType(kts) => SimpleType::Record(
kts.iter()
.map(|(k, v)| Some((k.into(), Self::from_nir(v)?)))
.collect::<Option<_>>()?,
),
NirKind::UnionType(kts) => SimpleType::Union(
kts.iter()
.map(|(k, v)| {
Some((
k.into(),
v.as_ref()
.map(|v| Ok(Self::from_nir(v)?))
.transpose()?,
))
})
.collect::<Option<_>>()?,
),
_ => return None,
})
}
pub(crate) fn to_value(&self) -> Value {
Value {
hir: self.to_hir(),
as_simple_val: None,
as_simple_ty: Some(self.clone()),
}
}
pub(crate) fn to_hir(&self) -> Hir {
let hir = |k| Hir::new(HirKind::Expr(k), Span::Artificial);
hir(match self {
SimpleType::Bool => ExprKind::Builtin(Builtin::Bool),
SimpleType::Natural => ExprKind::Builtin(Builtin::Natural),
SimpleType::Integer => ExprKind::Builtin(Builtin::Integer),
SimpleType::Double => ExprKind::Builtin(Builtin::Double),
SimpleType::Text => ExprKind::Builtin(Builtin::Text),
SimpleType::Optional(t) => ExprKind::Op(OpKind::App(
hir(ExprKind::Builtin(Builtin::Optional)),
t.to_hir(),
)),
SimpleType::List(t) => ExprKind::Op(OpKind::App(
hir(ExprKind::Builtin(Builtin::List)),
t.to_hir(),
)),
SimpleType::Record(kts) => ExprKind::RecordType(
kts.iter()
.map(|(k, t)| (k.as_str().into(), t.to_hir()))
.collect(),
),
SimpleType::Union(kts) => ExprKind::UnionType(
kts.iter()
.map(|(k, t)| {
(k.as_str().into(), t.as_ref().map(|t| t.to_hir()))
})
.collect(),
),
})
}
}
impl Sealed for Value {}
impl Sealed for SimpleValue {}
impl Sealed for SimpleType {}
impl FromDhall for Value {
fn from_dhall(v: &Value) -> Result<Self> {
Ok(v.clone())
}
}
impl FromDhall for SimpleValue {
fn from_dhall(v: &Value) -> Result<Self> {
v.to_simple_value().ok_or_else(|| {
Error(ErrorKind::Deserialize(format!(
"this cannot be deserialized into a simple type: {}",
v
)))
})
}
}
impl FromDhall for SimpleType {
fn from_dhall(v: &Value) -> Result<Self> {
v.to_simple_type().ok_or_else(|| {
Error(ErrorKind::Deserialize(format!(
"this cannot be deserialized into a simple type: {}",
v
)))
})
}
}
impl Eq for Value {}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
self.hir == other.hir
}
}
impl std::fmt::Display for Value {
fn fmt(
&self,
f: &mut std::fmt::Formatter,
) -> std::result::Result<(), std::fmt::Error> {
self.to_expr().fmt(f)
}
}