summaryrefslogtreecommitdiff
path: root/dhall
diff options
context:
space:
mode:
authorNadrieril2019-05-07 18:12:04 +0200
committerNadrieril2019-05-07 18:12:04 +0200
commit3da450aa3fae23214aa982643b9bc4dd0ea4eaa6 (patch)
tree02e00cd008d2e7dc899b9211379596fe792f41c8 /dhall
parentd8a3e831fb67f86269c4baa99f9f0798a73a7247 (diff)
parent14dfeb8e7d2aa87a361a711a485243449426b144 (diff)
Merge branch 'reorganize'
Diffstat (limited to 'dhall')
-rw-r--r--dhall/src/api/mod.rs40
-rw-r--r--dhall/src/api/serde.rs (renamed from dhall/src/serde.rs)2
-rw-r--r--dhall/src/api/traits/deserialize.rs (renamed from dhall/src/traits/deserialize.rs)2
-rw-r--r--dhall/src/api/traits/dynamic_type.rs (renamed from dhall/src/traits/dynamic_type.rs)9
-rw-r--r--dhall/src/api/traits/mod.rs (renamed from dhall/src/traits/mod.rs)0
-rw-r--r--dhall/src/api/traits/static_type.rs (renamed from dhall/src/traits/static_type.rs)6
-rw-r--r--dhall/src/core/context.rs136
-rw-r--r--dhall/src/core/mod.rs3
-rw-r--r--dhall/src/core/thunk.rs297
-rw-r--r--dhall/src/core/value.rs627
-rw-r--r--dhall/src/error.rs52
-rw-r--r--dhall/src/error/mod.rs169
-rw-r--r--dhall/src/error/text/AnnotMismatch.txt (renamed from dhall/src/errors/AnnotMismatch.txt)0
-rw-r--r--dhall/src/error/text/CantTextAppend.txt (renamed from dhall/src/errors/CantTextAppend.txt)0
-rw-r--r--dhall/src/error/text/DuplicateAlternative.txt (renamed from dhall/src/errors/DuplicateAlternative.txt)0
-rw-r--r--dhall/src/error/text/FieldCollision.txt (renamed from dhall/src/errors/FieldCollision.txt)0
-rw-r--r--dhall/src/error/text/HandlerInputTypeMismatch.txt (renamed from dhall/src/errors/HandlerInputTypeMismatch.txt)0
-rw-r--r--dhall/src/error/text/HandlerNotAFunction.txt (renamed from dhall/src/errors/HandlerNotAFunction.txt)0
-rw-r--r--dhall/src/error/text/HandlerOutputTypeMismatch.txt (renamed from dhall/src/errors/HandlerOutputTypeMismatch.txt)0
-rw-r--r--dhall/src/error/text/IfBranchMismatch.txt (renamed from dhall/src/errors/IfBranchMismatch.txt)0
-rw-r--r--dhall/src/error/text/IfBranchMustBeTerm.txt (renamed from dhall/src/errors/IfBranchMustBeTerm.txt)0
-rw-r--r--dhall/src/error/text/InvalidAlterantive.txt (renamed from dhall/src/errors/InvalidAlterantive.txt)0
-rw-r--r--dhall/src/error/text/InvalidAlterantiveType.txt (renamed from dhall/src/errors/InvalidAlterantiveType.txt)0
-rw-r--r--dhall/src/error/text/InvalidField.txt (renamed from dhall/src/errors/InvalidField.txt)0
-rw-r--r--dhall/src/error/text/InvalidFieldType.txt (renamed from dhall/src/errors/InvalidFieldType.txt)0
-rw-r--r--dhall/src/error/text/InvalidInputType.txt (renamed from dhall/src/errors/InvalidInputType.txt)0
-rw-r--r--dhall/src/error/text/InvalidListElement.txt (renamed from dhall/src/errors/InvalidListElement.txt)0
-rw-r--r--dhall/src/error/text/InvalidListType.txt (renamed from dhall/src/errors/InvalidListType.txt)0
-rw-r--r--dhall/src/error/text/InvalidOptionType.txt (renamed from dhall/src/errors/InvalidOptionType.txt)0
-rw-r--r--dhall/src/error/text/InvalidOptionalElement.txt (renamed from dhall/src/errors/InvalidOptionalElement.txt)0
-rw-r--r--dhall/src/error/text/InvalidOptionalLiteral.txt (renamed from dhall/src/errors/InvalidOptionalLiteral.txt)0
-rw-r--r--dhall/src/error/text/InvalidOutputType.txt (renamed from dhall/src/errors/InvalidOutputType.txt)0
-rw-r--r--dhall/src/error/text/InvalidPredicate.txt (renamed from dhall/src/errors/InvalidPredicate.txt)0
-rw-r--r--dhall/src/error/text/MissingField.txt (renamed from dhall/src/errors/MissingField.txt)0
-rw-r--r--dhall/src/error/text/MissingHandler.txt (renamed from dhall/src/errors/MissingHandler.txt)0
-rw-r--r--dhall/src/error/text/MustCombineARecord.txt (renamed from dhall/src/errors/MustCombineARecord.txt)0
-rw-r--r--dhall/src/error/text/MustMergeARecord.txt (renamed from dhall/src/errors/MustMergeARecord.txt)0
-rw-r--r--dhall/src/error/text/MustMergeUnion.txt (renamed from dhall/src/errors/MustMergeUnion.txt)0
-rw-r--r--dhall/src/error/text/NoDependentLet.txt (renamed from dhall/src/errors/NoDependentLet.txt)0
-rw-r--r--dhall/src/error/text/NoDependentTypes.txt (renamed from dhall/src/errors/NoDependentTypes.txt)0
-rw-r--r--dhall/src/error/text/NotAFunction.txt (renamed from dhall/src/errors/NotAFunction.txt)0
-rw-r--r--dhall/src/error/text/NotARecord.txt (renamed from dhall/src/errors/NotARecord.txt)0
-rw-r--r--dhall/src/error/text/TypeMismatch.txt (renamed from dhall/src/errors/TypeMismatch.txt)0
-rw-r--r--dhall/src/error/text/UnboundVariable.txt (renamed from dhall/src/errors/UnboundVariable.txt)0
-rw-r--r--dhall/src/error/text/Untyped.txt (renamed from dhall/src/errors/Untyped.txt)0
-rw-r--r--dhall/src/error/text/UnusedHandler.txt (renamed from dhall/src/errors/UnusedHandler.txt)0
-rw-r--r--dhall/src/expr.rs244
-rw-r--r--dhall/src/lib.rs51
-rw-r--r--dhall/src/main.rs93
-rw-r--r--dhall/src/parser.rs6
-rw-r--r--dhall/src/phase/binary.rs (renamed from dhall/src/binary.rs)17
-rw-r--r--dhall/src/phase/mod.rs334
-rw-r--r--dhall/src/phase/normalize.rs (renamed from dhall/src/normalize.rs)1040
-rw-r--r--dhall/src/phase/parse.rs38
-rw-r--r--dhall/src/phase/resolve.rs (renamed from dhall/src/imports.rs)62
-rw-r--r--dhall/src/phase/typecheck.rs (renamed from dhall/src/typecheck.rs)599
-rw-r--r--dhall/src/tests.rs3
-rw-r--r--dhall/tests/traits.rs2
58 files changed, 1846 insertions, 1986 deletions
diff --git a/dhall/src/api/mod.rs b/dhall/src/api/mod.rs
new file mode 100644
index 0000000..0a0ef93
--- /dev/null
+++ b/dhall/src/api/mod.rs
@@ -0,0 +1,40 @@
+mod serde;
+pub(crate) mod traits;
+
+/// Deserialization of Dhall expressions into Rust
+pub mod de {
+ #[doc(hidden)]
+ pub use crate::phase::SimpleType;
+ pub use crate::traits::{Deserialize, SimpleStaticType, StaticType};
+ #[doc(hidden)]
+ pub use dhall_proc_macros::SimpleStaticType;
+
+ /// Deserialize an instance of type T from a string of Dhall text.
+ ///
+ /// This will recursively resolve all imports in the expression, and
+ /// typecheck it before deserialization. Relative imports will be resolved relative to the
+ /// provided file. More control over this process is not yet available
+ /// but will be in a coming version of this crate.
+ ///
+ /// If a type is provided, this additionally checks that the provided
+ /// expression has that type.
+ pub fn from_str<'a, T: Deserialize<'a>>(
+ s: &'a str,
+ ty: Option<&crate::phase::Type>,
+ ) -> crate::error::Result<T> {
+ T::from_str(s, ty)
+ }
+
+ /// Deserialize an instance of type T from a string of Dhall text,
+ /// additionally checking that it matches the type of T.
+ ///
+ /// This will recursively resolve all imports in the expression, and
+ /// typecheck it before deserialization. Relative imports will be resolved relative to the
+ /// provided file. More control over this process is not yet available
+ /// but will be in a coming version of this crate.
+ pub fn from_str_auto_type<'a, T: Deserialize<'a> + StaticType>(
+ s: &'a str,
+ ) -> crate::error::Result<T> {
+ from_str(s, Some(&<T as StaticType>::get_static_type()))
+ }
+}
diff --git a/dhall/src/serde.rs b/dhall/src/api/serde.rs
index 96bc765..93921ba 100644
--- a/dhall/src/serde.rs
+++ b/dhall/src/api/serde.rs
@@ -1,5 +1,5 @@
use crate::error::{Error, Result};
-use crate::expr::{Normalized, Type};
+use crate::phase::{Normalized, Type};
use crate::traits::Deserialize;
use dhall_syntax::*;
use std::borrow::Cow;
diff --git a/dhall/src/traits/deserialize.rs b/dhall/src/api/traits/deserialize.rs
index e3ff2d5..9673cf9 100644
--- a/dhall/src/traits/deserialize.rs
+++ b/dhall/src/api/traits/deserialize.rs
@@ -1,5 +1,5 @@
use crate::error::*;
-use crate::expr::*;
+use crate::phase::*;
/// A data structure that can be deserialized from a Dhall expression
///
diff --git a/dhall/src/traits/dynamic_type.rs b/dhall/src/api/traits/dynamic_type.rs
index 858642e..7763a28 100644
--- a/dhall/src/traits/dynamic_type.rs
+++ b/dhall/src/api/traits/dynamic_type.rs
@@ -1,9 +1,6 @@
-use crate::expr::*;
+use crate::error::TypeError;
+use crate::phase::{Normalized, Type, Typed};
use crate::traits::StaticType;
-#[allow(unused_imports)]
-use crate::typecheck::{TypeError, TypeMessage, TypecheckContext};
-#[allow(unused_imports)]
-use dhall_syntax::{Const, ExprF};
use std::borrow::Cow;
pub trait DynamicType {
@@ -30,6 +27,6 @@ impl DynamicType for Normalized {
impl DynamicType for Typed {
fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> {
- self.0.get_type()
+ self.get_type()
}
}
diff --git a/dhall/src/traits/mod.rs b/dhall/src/api/traits/mod.rs
index 315e17a..315e17a 100644
--- a/dhall/src/traits/mod.rs
+++ b/dhall/src/api/traits/mod.rs
diff --git a/dhall/src/traits/static_type.rs b/dhall/src/api/traits/static_type.rs
index f90b8df..e05dfff 100644
--- a/dhall/src/traits/static_type.rs
+++ b/dhall/src/api/traits/static_type.rs
@@ -1,4 +1,4 @@
-use crate::expr::*;
+use crate::phase::*;
use dhall_proc_macros as dhall;
use dhall_syntax::*;
@@ -38,8 +38,8 @@ fn mktype(x: SubExpr<X, X>) -> SimpleType {
impl<T: SimpleStaticType> StaticType for T {
fn get_static_type() -> Type {
- crate::expr::Normalized::from_thunk_and_type(
- crate::normalize::Thunk::from_normalized_expr(
+ crate::phase::Normalized::from_thunk_and_type(
+ crate::core::thunk::Thunk::from_normalized_expr(
T::get_simple_static_type().into(),
),
Type::const_type(),
diff --git a/dhall/src/core/context.rs b/dhall/src/core/context.rs
new file mode 100644
index 0000000..9b5beed
--- /dev/null
+++ b/dhall/src/core/context.rs
@@ -0,0 +1,136 @@
+use std::borrow::Cow;
+use std::rc::Rc;
+
+use dhall_syntax::context::Context;
+use dhall_syntax::{Label, V};
+
+use crate::core::thunk::Thunk;
+use crate::core::value::{AlphaVar, Value};
+use crate::phase::{Normalized, Type, Typed};
+
+#[derive(Debug, Clone)]
+enum NzEnvItem {
+ Thunk(Thunk),
+ Skip(AlphaVar),
+}
+
+#[derive(Debug, Clone)]
+pub(crate) struct NormalizationContext(Rc<Context<Label, NzEnvItem>>);
+
+#[derive(Debug, Clone)]
+pub(crate) enum TyEnvItem {
+ Type(AlphaVar, Type),
+ Value(Normalized),
+}
+
+#[derive(Debug, Clone)]
+pub(crate) struct TypecheckContext(pub(crate) Context<Label, TyEnvItem>);
+
+impl NzEnvItem {
+ pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ use NzEnvItem::*;
+ match self {
+ Thunk(e) => Thunk(e.shift(delta, var)),
+ Skip(v) => Skip(v.shift(delta, var)),
+ }
+ }
+
+ pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
+ match self {
+ NzEnvItem::Thunk(e) => NzEnvItem::Thunk(e.subst_shift(var, val)),
+ NzEnvItem::Skip(v) if v == var => NzEnvItem::Thunk(val.to_thunk()),
+ NzEnvItem::Skip(v) => NzEnvItem::Skip(v.shift(-1, var)),
+ }
+ }
+}
+
+impl NormalizationContext {
+ pub(crate) fn new() -> Self {
+ NormalizationContext(Rc::new(Context::new()))
+ }
+ pub(crate) fn skip(&self, x: &Label) -> Self {
+ NormalizationContext(Rc::new(
+ self.0
+ .map(|_, e| e.shift(1, &x.into()))
+ .insert(x.clone(), NzEnvItem::Skip(x.into())),
+ ))
+ }
+ pub(crate) fn lookup(&self, var: &V<Label>) -> Value {
+ let V(x, n) = var;
+ match self.0.lookup(x, *n) {
+ Some(NzEnvItem::Thunk(t)) => t.to_value(),
+ Some(NzEnvItem::Skip(newvar)) => Value::Var(newvar.clone()),
+ // Free variable
+ None => Value::Var(AlphaVar::from_var(var.clone())),
+ }
+ }
+ pub(crate) fn from_typecheck_ctx(tc_ctx: &TypecheckContext) -> Self {
+ use TyEnvItem::*;
+ let mut ctx = Context::new();
+ for (k, vs) in tc_ctx.0.iter_keys() {
+ for v in vs.iter() {
+ let new_item = match v {
+ Type(var, _) => NzEnvItem::Skip(var.clone()),
+ Value(e) => NzEnvItem::Thunk(e.to_thunk()),
+ };
+ ctx = ctx.insert(k.clone(), new_item);
+ }
+ }
+ NormalizationContext(Rc::new(ctx))
+ }
+
+ pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ NormalizationContext(Rc::new(self.0.map(|_, e| e.shift(delta, var))))
+ }
+
+ pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
+ NormalizationContext(Rc::new(
+ self.0.map(|_, e| e.subst_shift(var, val)),
+ ))
+ }
+}
+
+impl TyEnvItem {
+ fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ use TyEnvItem::*;
+ match self {
+ Type(v, e) => Type(v.shift(delta, var), e.shift(delta, var)),
+ Value(e) => Value(e.shift(delta, var)),
+ }
+ }
+}
+
+impl TypecheckContext {
+ pub(crate) fn new() -> Self {
+ TypecheckContext(Context::new())
+ }
+ pub(crate) fn insert_type(&self, x: &Label, t: Type) -> Self {
+ TypecheckContext(self.0.map(|_, e| e.shift(1, &x.into())).insert(
+ x.clone(),
+ TyEnvItem::Type(x.into(), t.shift(1, &x.into())),
+ ))
+ }
+ pub(crate) fn insert_value(&self, x: &Label, t: Normalized) -> Self {
+ TypecheckContext(self.0.insert(x.clone(), TyEnvItem::Value(t)))
+ }
+ pub(crate) fn lookup(&self, var: &V<Label>) -> Option<Cow<'_, Type>> {
+ let V(x, n) = var;
+ match self.0.lookup(x, *n) {
+ Some(TyEnvItem::Type(_, t)) => Some(Cow::Borrowed(&t)),
+ Some(TyEnvItem::Value(t)) => Some(t.get_type()?),
+ None => None,
+ }
+ }
+ pub(crate) fn to_normalization_ctx(&self) -> NormalizationContext {
+ NormalizationContext::from_typecheck_ctx(self)
+ }
+}
+
+impl PartialEq for TypecheckContext {
+ fn eq(&self, _: &Self) -> bool {
+ // don't count contexts when comparing stuff
+ // this is dirty but needed for now
+ true
+ }
+}
+impl Eq for TypecheckContext {}
diff --git a/dhall/src/core/mod.rs b/dhall/src/core/mod.rs
new file mode 100644
index 0000000..8f5c2ca
--- /dev/null
+++ b/dhall/src/core/mod.rs
@@ -0,0 +1,3 @@
+pub(crate) mod context;
+pub(crate) mod thunk;
+pub(crate) mod value;
diff --git a/dhall/src/core/thunk.rs b/dhall/src/core/thunk.rs
new file mode 100644
index 0000000..2b013bb
--- /dev/null
+++ b/dhall/src/core/thunk.rs
@@ -0,0 +1,297 @@
+use std::cell::{Ref, RefCell};
+use std::rc::Rc;
+
+use crate::core::context::NormalizationContext;
+use crate::core::context::TypecheckContext;
+use crate::core::value::{AlphaVar, Value};
+use crate::error::TypeError;
+use crate::phase::normalize::{
+ apply_any, normalize_whnf, InputSubExpr, OutputSubExpr,
+};
+use crate::phase::typecheck::mktype;
+use crate::phase::{Type, Typed};
+
+#[derive(Debug, Clone, Copy)]
+enum Marker {
+ /// Weak Head Normal Form, i.e. subexpressions may not be normalized
+ WHNF,
+ /// Normal form, i.e. completely normalized
+ NF,
+}
+use Marker::{NF, WHNF};
+
+#[derive(Debug)]
+enum ThunkInternal {
+ /// Non-normalized value alongside a normalization context
+ Unnormalized(NormalizationContext, InputSubExpr),
+ /// Partially normalized value.
+ /// Invariant: if the marker is `NF`, the value must be fully normalized
+ Value(Marker, Value),
+}
+
+/// Stores a possibly unevaluated value. Gets (partially) normalized on-demand,
+/// sharing computation automatically.
+/// Uses a RefCell to share computation.
+#[derive(Debug, Clone)]
+pub struct Thunk(Rc<RefCell<ThunkInternal>>);
+
+/// A thunk in type position. Can optionally store a Type from the typechecking phase to preserve
+/// type information through the normalization phase.
+#[derive(Debug, Clone)]
+pub(crate) enum TypeThunk {
+ Thunk(Thunk),
+ Type(Type),
+}
+
+impl ThunkInternal {
+ fn into_thunk(self) -> Thunk {
+ Thunk(Rc::new(RefCell::new(self)))
+ }
+
+ fn normalize_whnf(&mut self) {
+ match self {
+ ThunkInternal::Unnormalized(ctx, e) => {
+ *self = ThunkInternal::Value(
+ WHNF,
+ normalize_whnf(ctx.clone(), e.clone()),
+ )
+ }
+ // Already at least in WHNF
+ ThunkInternal::Value(_, _) => {}
+ }
+ }
+
+ fn normalize_nf(&mut self) {
+ match self {
+ ThunkInternal::Unnormalized(_, _) => {
+ self.normalize_whnf();
+ self.normalize_nf();
+ }
+ ThunkInternal::Value(m @ WHNF, v) => {
+ v.normalize_mut();
+ *m = NF;
+ }
+ // Already in NF
+ ThunkInternal::Value(NF, _) => {}
+ }
+ }
+
+ // Always use normalize_whnf before
+ fn as_whnf(&self) -> &Value {
+ match self {
+ ThunkInternal::Unnormalized(_, _) => unreachable!(),
+ ThunkInternal::Value(_, v) => v,
+ }
+ }
+
+ // Always use normalize_nf before
+ fn as_nf(&self) -> &Value {
+ match self {
+ ThunkInternal::Unnormalized(_, _) => unreachable!(),
+ ThunkInternal::Value(WHNF, _) => unreachable!(),
+ ThunkInternal::Value(NF, v) => v,
+ }
+ }
+
+ fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ match self {
+ ThunkInternal::Unnormalized(ctx, e) => {
+ ThunkInternal::Unnormalized(ctx.shift(delta, var), e.clone())
+ }
+ ThunkInternal::Value(m, v) => {
+ ThunkInternal::Value(*m, v.shift(delta, var))
+ }
+ }
+ }
+
+ fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
+ match self {
+ ThunkInternal::Unnormalized(ctx, e) => ThunkInternal::Unnormalized(
+ ctx.subst_shift(var, val),
+ e.clone(),
+ ),
+ ThunkInternal::Value(_, v) => {
+ // The resulting value may not stay in normal form after substitution
+ ThunkInternal::Value(WHNF, v.subst_shift(var, val))
+ }
+ }
+ }
+}
+
+impl Thunk {
+ pub(crate) fn new(ctx: NormalizationContext, e: InputSubExpr) -> Thunk {
+ ThunkInternal::Unnormalized(ctx, e).into_thunk()
+ }
+
+ pub(crate) fn from_value(v: Value) -> Thunk {
+ ThunkInternal::Value(WHNF, v).into_thunk()
+ }
+
+ pub(crate) fn from_normalized_expr(e: OutputSubExpr) -> Thunk {
+ Thunk::new(NormalizationContext::new(), e.absurd())
+ }
+
+ // Normalizes contents to normal form; faster than `normalize_nf` if
+ // no one else shares this thunk
+ pub(crate) fn normalize_mut(&mut self) {
+ match Rc::get_mut(&mut self.0) {
+ // Mutate directly if sole owner
+ Some(refcell) => RefCell::get_mut(refcell).normalize_nf(),
+ // Otherwise mutate through the refcell
+ None => self.0.borrow_mut().normalize_nf(),
+ }
+ }
+
+ fn do_normalize_whnf(&self) {
+ let borrow = self.0.borrow();
+ match &*borrow {
+ ThunkInternal::Unnormalized(_, _) => {
+ drop(borrow);
+ self.0.borrow_mut().normalize_whnf();
+ }
+ // Already at least in WHNF
+ ThunkInternal::Value(_, _) => {}
+ }
+ }
+
+ fn do_normalize_nf(&self) {
+ let borrow = self.0.borrow();
+ match &*borrow {
+ ThunkInternal::Unnormalized(_, _)
+ | ThunkInternal::Value(WHNF, _) => {
+ drop(borrow);
+ self.0.borrow_mut().normalize_nf();
+ }
+ // Already in NF
+ ThunkInternal::Value(NF, _) => {}
+ }
+ }
+
+ // WARNING: avoid normalizing any thunk while holding on to this ref
+ // or you could run into BorrowMut panics
+ pub(crate) fn normalize_nf(&self) -> Ref<Value> {
+ self.do_normalize_nf();
+ Ref::map(self.0.borrow(), ThunkInternal::as_nf)
+ }
+
+ // WARNING: avoid normalizing any thunk while holding on to this ref
+ // or you could run into BorrowMut panics
+ pub(crate) fn as_value(&self) -> Ref<Value> {
+ self.do_normalize_whnf();
+ Ref::map(self.0.borrow(), ThunkInternal::as_whnf)
+ }
+
+ pub(crate) fn to_value(&self) -> Value {
+ self.as_value().clone()
+ }
+
+ pub(crate) fn normalize_to_expr(&self) -> OutputSubExpr {
+ self.normalize_to_expr_maybe_alpha(false)
+ }
+
+ pub(crate) fn normalize_to_expr_maybe_alpha(
+ &self,
+ alpha: bool,
+ ) -> OutputSubExpr {
+ self.normalize_nf().normalize_to_expr_maybe_alpha(alpha)
+ }
+
+ pub(crate) fn app_val(&self, val: Value) -> Value {
+ self.app_thunk(val.into_thunk())
+ }
+
+ pub(crate) fn app_thunk(&self, th: Thunk) -> Value {
+ apply_any(self.clone(), th)
+ }
+
+ pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ self.0.borrow().shift(delta, var).into_thunk()
+ }
+
+ pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
+ self.0.borrow().subst_shift(var, val).into_thunk()
+ }
+}
+
+impl TypeThunk {
+ pub(crate) fn from_value(v: Value) -> TypeThunk {
+ TypeThunk::from_thunk(Thunk::from_value(v))
+ }
+
+ pub(crate) fn from_thunk(th: Thunk) -> TypeThunk {
+ TypeThunk::Thunk(th)
+ }
+
+ pub(crate) fn from_type(t: Type) -> TypeThunk {
+ TypeThunk::Type(t)
+ }
+
+ pub(crate) fn normalize_mut(&mut self) {
+ match self {
+ TypeThunk::Thunk(th) => th.normalize_mut(),
+ TypeThunk::Type(_) => {}
+ }
+ }
+
+ pub(crate) fn normalize_nf(&self) -> Value {
+ match self {
+ TypeThunk::Thunk(th) => th.normalize_nf().clone(),
+ TypeThunk::Type(t) => t.to_value().normalize(),
+ }
+ }
+
+ pub(crate) fn to_value(&self) -> Value {
+ match self {
+ TypeThunk::Thunk(th) => th.to_value(),
+ TypeThunk::Type(t) => t.to_value(),
+ }
+ }
+
+ pub(crate) fn to_type(
+ &self,
+ ctx: &TypecheckContext,
+ ) -> Result<Type, TypeError> {
+ match self {
+ TypeThunk::Type(t) => Ok(t.clone()),
+ TypeThunk::Thunk(th) => {
+ // TODO: rule out statically
+ mktype(ctx, th.normalize_to_expr().absurd())
+ }
+ }
+ }
+
+ pub(crate) fn normalize_to_expr_maybe_alpha(
+ &self,
+ alpha: bool,
+ ) -> OutputSubExpr {
+ self.normalize_nf().normalize_to_expr_maybe_alpha(alpha)
+ }
+
+ pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ match self {
+ TypeThunk::Thunk(th) => TypeThunk::Thunk(th.shift(delta, var)),
+ TypeThunk::Type(t) => TypeThunk::Type(t.shift(delta, var)),
+ }
+ }
+
+ pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
+ match self {
+ TypeThunk::Thunk(th) => TypeThunk::Thunk(th.subst_shift(var, val)),
+ TypeThunk::Type(t) => TypeThunk::Type(t.subst_shift(var, val)),
+ }
+ }
+}
+
+impl std::cmp::PartialEq for Thunk {
+ fn eq(&self, other: &Self) -> bool {
+ &*self.as_value() == &*other.as_value()
+ }
+}
+impl std::cmp::Eq for Thunk {}
+
+impl std::cmp::PartialEq for TypeThunk {
+ fn eq(&self, other: &Self) -> bool {
+ self.to_value() == other.to_value()
+ }
+}
+impl std::cmp::Eq for TypeThunk {}
diff --git a/dhall/src/core/value.rs b/dhall/src/core/value.rs
new file mode 100644
index 0000000..28fcb3e
--- /dev/null
+++ b/dhall/src/core/value.rs
@@ -0,0 +1,627 @@
+use std::collections::BTreeMap;
+
+use dhall_proc_macros as dhall;
+use dhall_syntax::{
+ rc, Builtin, Const, ExprF, Integer, InterpolatedTextContents, Label,
+ Natural, V, X,
+};
+
+use crate::core::thunk::{Thunk, TypeThunk};
+use crate::phase::normalize::{
+ apply_builtin, normalize_one_layer, squash_textlit, OutputSubExpr,
+};
+use crate::phase::Typed;
+
+/// Stores a pair of variables: a normal one and if relevant one
+/// that corresponds to the alpha-normalized version of the first one.
+/// Equality is up to alpha-equivalence.
+#[derive(Debug, Clone, Eq)]
+pub(crate) struct AlphaVar {
+ normal: V<Label>,
+ alpha: Option<V<()>>,
+}
+
+// Exactly like a Label, but equality returns always true.
+// This is so that Value equality is exactly alpha-equivalence.
+#[derive(Debug, Clone, Eq)]
+pub(crate) struct AlphaLabel(Label);
+
+/// A semantic value. The invariants ensure this value represents a Weak-Head
+/// Normal Form (WHNF). This means that this first constructor is the first constructor of the
+/// final Normal Form (NF).
+/// This WHNF must be preserved by operations on `Value`s. In particular, `subst_shift` could break
+/// the invariants so need to be careful to reevaluate as needed.
+/// Subexpressions are Thunks, which are partially evaluated expressions that are normalized
+/// on-demand. When all the Thunks in a Value are at least in WHNF, and recursively so, then the
+/// Value is in NF. This is because WHNF ensures that we have the first constructor of the NF; so
+/// if we have the first constructor of the NF at all levels, we actually have the NF.
+/// Equality is up to alpha-equivalence (renaming of bound variables) and beta-equivalence
+/// (normalization). Equality will normalize only as needed.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) enum Value {
+ /// Closures
+ Lam(AlphaLabel, TypeThunk, Thunk),
+ Pi(AlphaLabel, TypeThunk, TypeThunk),
+ // Invariant: the evaluation must not be able to progress further.
+ AppliedBuiltin(Builtin, Vec<Thunk>),
+ /// `λ(x: a) -> Some x`
+ OptionalSomeClosure(TypeThunk),
+ /// `λ(x : a) -> λ(xs : List a) -> [ x ] # xs`
+ /// `λ(xs : List a) -> [ x ] # xs`
+ ListConsClosure(TypeThunk, Option<Thunk>),
+ /// `λ(x : Natural) -> x + 1`
+ NaturalSuccClosure,
+
+ Var(AlphaVar),
+ Const(Const),
+ BoolLit(bool),
+ NaturalLit(Natural),
+ IntegerLit(Integer),
+ EmptyOptionalLit(TypeThunk),
+ NEOptionalLit(Thunk),
+ EmptyListLit(TypeThunk),
+ NEListLit(Vec<Thunk>),
+ RecordLit(BTreeMap<Label, Thunk>),
+ RecordType(BTreeMap<Label, TypeThunk>),
+ UnionType(BTreeMap<Label, Option<TypeThunk>>),
+ UnionConstructor(Label, BTreeMap<Label, Option<TypeThunk>>),
+ UnionLit(Label, Thunk, BTreeMap<Label, Option<TypeThunk>>),
+ // Invariant: this must not contain interpolations that are themselves TextLits, and
+ // contiguous text values must be merged.
+ TextLit(Vec<InterpolatedTextContents<Thunk>>),
+ // Invariant: this must not contain a value captured by one of the variants above.
+ PartialExpr(ExprF<Thunk, Label, X>),
+}
+
+impl AlphaVar {
+ pub(crate) fn to_var(&self, alpha: bool) -> V<Label> {
+ match (alpha, &self.alpha) {
+ (true, Some(x)) => V("_".into(), x.1),
+ _ => self.normal.clone(),
+ }
+ }
+ pub(crate) fn from_var(normal: V<Label>) -> Self {
+ AlphaVar {
+ normal,
+ alpha: None,
+ }
+ }
+ pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ AlphaVar {
+ normal: self.normal.shift(delta, &var.normal),
+ alpha: match (&self.alpha, &var.alpha) {
+ (Some(x), Some(v)) => Some(x.shift(delta, v)),
+ _ => None,
+ },
+ }
+ }
+}
+
+impl AlphaLabel {
+ fn to_label_maybe_alpha(&self, alpha: bool) -> Label {
+ if alpha {
+ "_".into()
+ } else {
+ self.to_label()
+ }
+ }
+ fn to_label(&self) -> Label {
+ self.clone().into()
+ }
+}
+
+impl Value {
+ pub(crate) fn into_thunk(self) -> Thunk {
+ Thunk::from_value(self)
+ }
+
+ /// Convert the value to a fully normalized syntactic expression
+ pub(crate) fn normalize_to_expr(&self) -> OutputSubExpr {
+ self.normalize_to_expr_maybe_alpha(false)
+ }
+ /// Convert the value to a fully normalized syntactic expression. Also alpha-normalize
+ /// if alpha is `true`
+ pub(crate) fn normalize_to_expr_maybe_alpha(
+ &self,
+ alpha: bool,
+ ) -> OutputSubExpr {
+ match self {
+ Value::Lam(x, t, e) => rc(ExprF::Lam(
+ x.to_label_maybe_alpha(alpha),
+ t.normalize_to_expr_maybe_alpha(alpha),
+ e.normalize_to_expr_maybe_alpha(alpha),
+ )),
+ Value::AppliedBuiltin(b, args) => {
+ let mut e = rc(ExprF::Builtin(*b));
+ for v in args {
+ e = rc(ExprF::App(
+ e,
+ v.normalize_to_expr_maybe_alpha(alpha),
+ ));
+ }
+ e
+ }
+ Value::OptionalSomeClosure(n) => {
+ let a = n.normalize_to_expr_maybe_alpha(alpha);
+ dhall::subexpr!(λ(x: a) -> Some x)
+ }
+ Value::ListConsClosure(a, None) => {
+ // Avoid accidental capture of the new `x` variable
+ let a1 = a.shift(1, &Label::from("x").into());
+ let a1 = a1.normalize_to_expr_maybe_alpha(alpha);
+ let a = a.normalize_to_expr_maybe_alpha(alpha);
+ dhall::subexpr!(λ(x : a) -> λ(xs : List a1) -> [ x ] # xs)
+ }
+ Value::ListConsClosure(n, Some(v)) => {
+ // Avoid accidental capture of the new `xs` variable
+ let v = v.shift(1, &Label::from("xs").into());
+ let v = v.normalize_to_expr_maybe_alpha(alpha);
+ let a = n.normalize_to_expr_maybe_alpha(alpha);
+ dhall::subexpr!(λ(xs : List a) -> [ v ] # xs)
+ }
+ Value::NaturalSuccClosure => {
+ dhall::subexpr!(λ(x : Natural) -> x + 1)
+ }
+ Value::Pi(x, t, e) => rc(ExprF::Pi(
+ x.to_label_maybe_alpha(alpha),
+ t.normalize_to_expr_maybe_alpha(alpha),
+ e.normalize_to_expr_maybe_alpha(alpha),
+ )),
+ Value::Var(v) => rc(ExprF::Var(v.to_var(alpha))),
+ Value::Const(c) => rc(ExprF::Const(*c)),
+ Value::BoolLit(b) => rc(ExprF::BoolLit(*b)),
+ Value::NaturalLit(n) => rc(ExprF::NaturalLit(*n)),
+ Value::IntegerLit(n) => rc(ExprF::IntegerLit(*n)),
+ Value::EmptyOptionalLit(n) => rc(ExprF::App(
+ rc(ExprF::Builtin(Builtin::OptionalNone)),
+ n.normalize_to_expr_maybe_alpha(alpha),
+ )),
+ Value::NEOptionalLit(n) => {
+ rc(ExprF::SomeLit(n.normalize_to_expr_maybe_alpha(alpha)))
+ }
+ Value::EmptyListLit(n) => {
+ rc(ExprF::EmptyListLit(n.normalize_to_expr_maybe_alpha(alpha)))
+ }
+ Value::NEListLit(elts) => rc(ExprF::NEListLit(
+ elts.into_iter()
+ .map(|n| n.normalize_to_expr_maybe_alpha(alpha))
+ .collect(),
+ )),
+ Value::RecordLit(kvs) => rc(ExprF::RecordLit(
+ kvs.iter()
+ .map(|(k, v)| {
+ (k.clone(), v.normalize_to_expr_maybe_alpha(alpha))
+ })
+ .collect(),
+ )),
+ Value::RecordType(kts) => rc(ExprF::RecordType(
+ kts.iter()
+ .map(|(k, v)| {
+ (k.clone(), v.normalize_to_expr_maybe_alpha(alpha))
+ })
+ .collect(),
+ )),
+ Value::UnionType(kts) => rc(ExprF::UnionType(
+ kts.iter()
+ .map(|(k, v)| {
+ (
+ k.clone(),
+ v.as_ref().map(|v| {
+ v.normalize_to_expr_maybe_alpha(alpha)
+ }),
+ )
+ })
+ .collect(),
+ )),
+ Value::UnionConstructor(l, kts) => {
+ let kts = kts
+ .iter()
+ .map(|(k, v)| {
+ (
+ k.clone(),
+ v.as_ref().map(|v| {
+ v.normalize_to_expr_maybe_alpha(alpha)
+ }),
+ )
+ })
+ .collect();
+ rc(ExprF::Field(rc(ExprF::UnionType(kts)), l.clone()))
+ }
+ Value::UnionLit(l, v, kts) => rc(ExprF::UnionLit(
+ l.clone(),
+ v.normalize_to_expr_maybe_alpha(alpha),
+ kts.iter()
+ .map(|(k, v)| {
+ (
+ k.clone(),
+ v.as_ref().map(|v| {
+ v.normalize_to_expr_maybe_alpha(alpha)
+ }),
+ )
+ })
+ .collect(),
+ )),
+ Value::TextLit(elts) => {
+ use InterpolatedTextContents::{Expr, Text};
+ rc(ExprF::TextLit(
+ elts.iter()
+ .map(|contents| match contents {
+ Expr(e) => {
+ Expr(e.normalize_to_expr_maybe_alpha(alpha))
+ }
+ Text(s) => Text(s.clone()),
+ })
+ .collect(),
+ ))
+ }
+ Value::PartialExpr(e) => {
+ rc(e.map_ref_simple(|v| v.normalize_to_expr_maybe_alpha(alpha)))
+ }
+ }
+ }
+
+ // Deprecated
+ pub(crate) fn normalize(&self) -> Value {
+ let mut v = self.clone();
+ v.normalize_mut();
+ v
+ }
+
+ pub(crate) fn normalize_mut(&mut self) {
+ match self {
+ Value::NaturalSuccClosure
+ | Value::Var(_)
+ | Value::Const(_)
+ | Value::BoolLit(_)
+ | Value::NaturalLit(_)
+ | Value::IntegerLit(_) => {}
+
+ Value::EmptyOptionalLit(tth)
+ | Value::OptionalSomeClosure(tth)
+ | Value::EmptyListLit(tth) => {
+ tth.normalize_mut();
+ }
+
+ Value::NEOptionalLit(th) => {
+ th.normalize_mut();
+ }
+ Value::Lam(_, t, e) => {
+ t.normalize_mut();
+ e.normalize_mut();
+ }
+ Value::Pi(_, t, e) => {
+ t.normalize_mut();
+ e.normalize_mut();
+ }
+ Value::AppliedBuiltin(_, args) => {
+ for x in args.iter_mut() {
+ x.normalize_mut();
+ }
+ }
+ Value::ListConsClosure(t, v) => {
+ t.normalize_mut();
+ for x in v.iter_mut() {
+ x.normalize_mut();
+ }
+ }
+ Value::NEListLit(elts) => {
+ for x in elts.iter_mut() {
+ x.normalize_mut();
+ }
+ }
+ Value::RecordLit(kvs) => {
+ for x in kvs.values_mut() {
+ x.normalize_mut();
+ }
+ }
+ Value::RecordType(kvs) => {
+ for x in kvs.values_mut() {
+ x.normalize_mut();
+ }
+ }
+ Value::UnionType(kts) | Value::UnionConstructor(_, kts) => {
+ for x in kts.values_mut().flat_map(|opt| opt) {
+ x.normalize_mut();
+ }
+ }
+ Value::UnionLit(_, v, kts) => {
+ v.normalize_mut();
+ for x in kts.values_mut().flat_map(|opt| opt) {
+ x.normalize_mut();
+ }
+ }
+ Value::TextLit(elts) => {
+ for x in elts.iter_mut() {
+ use InterpolatedTextContents::{Expr, Text};
+ match x {
+ Expr(n) => n.normalize_mut(),
+ Text(_) => {}
+ }
+ }
+ }
+ Value::PartialExpr(e) => {
+ // TODO: need map_mut_simple
+ e.map_ref_simple(|v| {
+ v.normalize_nf();
+ });
+ }
+ }
+ }
+
+ /// Apply to a value
+ pub(crate) fn app(self, val: Value) -> Value {
+ self.app_val(val)
+ }
+
+ /// Apply to a value
+ pub(crate) fn app_val(self, val: Value) -> Value {
+ self.app_thunk(val.into_thunk())
+ }
+
+ /// Apply to a thunk
+ pub(crate) fn app_thunk(self, th: Thunk) -> Value {
+ Thunk::from_value(self).app_thunk(th)
+ }
+
+ pub(crate) fn from_builtin(b: Builtin) -> Value {
+ Value::AppliedBuiltin(b, vec![])
+ }
+
+ pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ match self {
+ Value::Lam(x, t, e) => Value::Lam(
+ x.clone(),
+ t.shift(delta, var),
+ e.shift(delta, &var.shift(1, &x.into())),
+ ),
+ Value::AppliedBuiltin(b, args) => Value::AppliedBuiltin(
+ *b,
+ args.iter().map(|v| v.shift(delta, var)).collect(),
+ ),
+ Value::NaturalSuccClosure => Value::NaturalSuccClosure,
+ Value::OptionalSomeClosure(tth) => {
+ Value::OptionalSomeClosure(tth.shift(delta, var))
+ }
+ Value::ListConsClosure(t, v) => Value::ListConsClosure(
+ t.shift(delta, var),
+ v.as_ref().map(|v| v.shift(delta, var)),
+ ),
+ Value::Pi(x, t, e) => Value::Pi(
+ x.clone(),
+ t.shift(delta, var),
+ e.shift(delta, &var.shift(1, &x.into())),
+ ),
+ Value::Var(v) => Value::Var(v.shift(delta, var)),
+ Value::Const(c) => Value::Const(*c),
+ Value::BoolLit(b) => Value::BoolLit(*b),
+ Value::NaturalLit(n) => Value::NaturalLit(*n),
+ Value::IntegerLit(n) => Value::IntegerLit(*n),
+ Value::EmptyOptionalLit(tth) => {
+ Value::EmptyOptionalLit(tth.shift(delta, var))
+ }
+ Value::NEOptionalLit(th) => {
+ Value::NEOptionalLit(th.shift(delta, var))
+ }
+ Value::EmptyListLit(tth) => {
+ Value::EmptyListLit(tth.shift(delta, var))
+ }
+ Value::NEListLit(elts) => Value::NEListLit(
+ elts.iter().map(|v| v.shift(delta, var)).collect(),
+ ),
+ Value::RecordLit(kvs) => Value::RecordLit(
+ kvs.iter()
+ .map(|(k, v)| (k.clone(), v.shift(delta, var)))
+ .collect(),
+ ),
+ Value::RecordType(kvs) => Value::RecordType(
+ kvs.iter()
+ .map(|(k, v)| (k.clone(), v.shift(delta, var)))
+ .collect(),
+ ),
+ Value::UnionType(kts) => Value::UnionType(
+ kts.iter()
+ .map(|(k, v)| {
+ (k.clone(), v.as_ref().map(|v| v.shift(delta, var)))
+ })
+ .collect(),
+ ),
+ Value::UnionConstructor(x, kts) => Value::UnionConstructor(
+ x.clone(),
+ kts.iter()
+ .map(|(k, v)| {
+ (k.clone(), v.as_ref().map(|v| v.shift(delta, var)))
+ })
+ .collect(),
+ ),
+ Value::UnionLit(x, v, kts) => Value::UnionLit(
+ x.clone(),
+ v.shift(delta, var),
+ kts.iter()
+ .map(|(k, v)| {
+ (k.clone(), v.as_ref().map(|v| v.shift(delta, var)))
+ })
+ .collect(),
+ ),
+ Value::TextLit(elts) => Value::TextLit(
+ elts.iter()
+ .map(|contents| {
+ use InterpolatedTextContents::{Expr, Text};
+ match contents {
+ Expr(th) => Expr(th.shift(delta, var)),
+ Text(s) => Text(s.clone()),
+ }
+ })
+ .collect(),
+ ),
+ Value::PartialExpr(e) => {
+ Value::PartialExpr(e.map_ref_with_special_handling_of_binders(
+ |v| v.shift(delta, var),
+ |x, v| v.shift(delta, &var.shift(1, &x.into())),
+ X::clone,
+ Label::clone,
+ ))
+ }
+ }
+ }
+
+ pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
+ match self {
+ // Retry normalizing since substituting may allow progress
+ Value::AppliedBuiltin(b, args) => apply_builtin(
+ *b,
+ args.iter().map(|v| v.subst_shift(var, val)).collect(),
+ ),
+ // Retry normalizing since substituting may allow progress
+ Value::PartialExpr(e) => {
+ normalize_one_layer(e.map_ref_with_special_handling_of_binders(
+ |v| v.subst_shift(var, val),
+ |x, v| {
+ v.subst_shift(
+ &var.shift(1, &x.into()),
+ &val.shift(1, &x.into()),
+ )
+ },
+ X::clone,
+ Label::clone,
+ ))
+ }
+ // Retry normalizing since substituting may allow progress
+ Value::TextLit(elts) => {
+ Value::TextLit(squash_textlit(elts.iter().map(|contents| {
+ use InterpolatedTextContents::{Expr, Text};
+ match contents {
+ Expr(th) => Expr(th.subst_shift(var, val)),
+ Text(s) => Text(s.clone()),
+ }
+ })))
+ }
+ Value::Lam(x, t, e) => Value::Lam(
+ x.clone(),
+ t.subst_shift(var, val),
+ e.subst_shift(
+ &var.shift(1, &x.into()),
+ &val.shift(1, &x.into()),
+ ),
+ ),
+ Value::NaturalSuccClosure => Value::NaturalSuccClosure,
+ Value::OptionalSomeClosure(tth) => {
+ Value::OptionalSomeClosure(tth.subst_shift(var, val))
+ }
+ Value::ListConsClosure(t, v) => Value::ListConsClosure(
+ t.subst_shift(var, val),
+ v.as_ref().map(|v| v.subst_shift(var, val)),
+ ),
+ Value::Pi(x, t, e) => Value::Pi(
+ x.clone(),
+ t.subst_shift(var, val),
+ e.subst_shift(
+ &var.shift(1, &x.into()),
+ &val.shift(1, &x.into()),
+ ),
+ ),
+ Value::Var(v) if v == var => val.to_value().clone(),
+ Value::Var(v) => Value::Var(v.shift(-1, var)),
+ Value::Const(c) => Value::Const(*c),
+ Value::BoolLit(b) => Value::BoolLit(*b),
+ Value::NaturalLit(n) => Value::NaturalLit(*n),
+ Value::IntegerLit(n) => Value::IntegerLit(*n),
+ Value::EmptyOptionalLit(tth) => {
+ Value::EmptyOptionalLit(tth.subst_shift(var, val))
+ }
+ Value::NEOptionalLit(th) => {
+ Value::NEOptionalLit(th.subst_shift(var, val))
+ }
+ Value::EmptyListLit(tth) => {
+ Value::EmptyListLit(tth.subst_shift(var, val))
+ }
+ Value::NEListLit(elts) => Value::NEListLit(
+ elts.iter().map(|v| v.subst_shift(var, val)).collect(),
+ ),
+ Value::RecordLit(kvs) => Value::RecordLit(
+ kvs.iter()
+ .map(|(k, v)| (k.clone(), v.subst_shift(var, val)))
+ .collect(),
+ ),
+ Value::RecordType(kvs) => Value::RecordType(
+ kvs.iter()
+ .map(|(k, v)| (k.clone(), v.subst_shift(var, val)))
+ .collect(),
+ ),
+ Value::UnionType(kts) => Value::UnionType(
+ kts.iter()
+ .map(|(k, v)| {
+ (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val)))
+ })
+ .collect(),
+ ),
+ Value::UnionConstructor(x, kts) => Value::UnionConstructor(
+ x.clone(),
+ kts.iter()
+ .map(|(k, v)| {
+ (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val)))
+ })
+ .collect(),
+ ),
+ Value::UnionLit(x, v, kts) => Value::UnionLit(
+ x.clone(),
+ v.subst_shift(var, val),
+ kts.iter()
+ .map(|(k, v)| {
+ (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val)))
+ })
+ .collect(),
+ ),
+ }
+ }
+}
+
+impl std::cmp::PartialEq for AlphaVar {
+ fn eq(&self, other: &Self) -> bool {
+ match (&self.alpha, &other.alpha) {
+ (Some(x), Some(y)) => x == y,
+ (None, None) => self.normal == other.normal,
+ _ => false,
+ }
+ }
+}
+impl std::cmp::PartialEq for AlphaLabel {
+ fn eq(&self, _other: &Self) -> bool {
+ true
+ }
+}
+
+impl From<Label> for AlphaVar {
+ fn from(x: Label) -> AlphaVar {
+ AlphaVar {
+ normal: V(x, 0),
+ alpha: Some(V((), 0)),
+ }
+ }
+}
+impl<'a> From<&'a Label> for AlphaVar {
+ fn from(x: &'a Label) -> AlphaVar {
+ x.clone().into()
+ }
+}
+impl From<AlphaLabel> for AlphaVar {
+ fn from(x: AlphaLabel) -> AlphaVar {
+ let l: Label = x.into();
+ l.into()
+ }
+}
+impl<'a> From<&'a AlphaLabel> for AlphaVar {
+ fn from(x: &'a AlphaLabel) -> AlphaVar {
+ x.clone().into()
+ }
+}
+
+impl From<Label> for AlphaLabel {
+ fn from(x: Label) -> AlphaLabel {
+ AlphaLabel(x)
+ }
+}
+impl From<AlphaLabel> for Label {
+ fn from(x: AlphaLabel) -> Label {
+ x.0
+ }
+}
diff --git a/dhall/src/error.rs b/dhall/src/error.rs
deleted file mode 100644
index 6ed0bfb..0000000
--- a/dhall/src/error.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-pub type Result<T> = std::result::Result<T, Error>;
-
-#[derive(Debug)]
-#[non_exhaustive]
-pub enum Error {
- IO(std::io::Error),
- Parse(dhall_syntax::ParseError),
- Decode(crate::binary::DecodeError),
- Resolve(crate::imports::ImportError),
- Typecheck(crate::typecheck::TypeError),
- Deserialize(String),
-}
-
-impl std::fmt::Display for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- match self {
- Error::IO(err) => write!(f, "{}", err),
- Error::Parse(err) => write!(f, "{}", err),
- Error::Decode(err) => write!(f, "{:?}", err),
- Error::Resolve(err) => write!(f, "{:?}", err),
- Error::Typecheck(err) => write!(f, "{:?}", err),
- Error::Deserialize(err) => write!(f, "{}", err),
- }
- }
-}
-
-impl std::error::Error for Error {}
-impl From<std::io::Error> for Error {
- fn from(err: std::io::Error) -> Error {
- Error::IO(err)
- }
-}
-impl From<dhall_syntax::ParseError> for Error {
- fn from(err: dhall_syntax::ParseError) -> Error {
- Error::Parse(err)
- }
-}
-impl From<crate::binary::DecodeError> for Error {
- fn from(err: crate::binary::DecodeError) -> Error {
- Error::Decode(err)
- }
-}
-impl From<crate::imports::ImportError> for Error {
- fn from(err: crate::imports::ImportError) -> Error {
- Error::Resolve(err)
- }
-}
-impl From<crate::typecheck::TypeError> for Error {
- fn from(err: crate::typecheck::TypeError) -> Error {
- Error::Typecheck(err)
- }
-}
diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs
new file mode 100644
index 0000000..f84d078
--- /dev/null
+++ b/dhall/src/error/mod.rs
@@ -0,0 +1,169 @@
+use std::io::Error as IOError;
+
+use dhall_syntax::{BinOp, Import, Label, ParseError, V};
+
+use crate::core::context::TypecheckContext;
+use crate::phase::resolve::ImportStack;
+use crate::phase::{Normalized, Type, Typed};
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum Error {
+ IO(IOError),
+ Parse(ParseError),
+ Decode(DecodeError),
+ Resolve(ImportError),
+ Typecheck(TypeError),
+ Deserialize(String),
+}
+
+#[derive(Debug)]
+pub enum ImportError {
+ Recursive(Import, Box<Error>),
+ UnexpectedImport(Import),
+ ImportCycle(ImportStack, Import),
+}
+
+#[derive(Debug)]
+pub enum DecodeError {
+ CBORError(serde_cbor::error::Error),
+ WrongFormatError(String),
+}
+
+/// A structured type error that includes context
+#[derive(Debug)]
+pub struct TypeError {
+ type_message: TypeMessage,
+ context: TypecheckContext,
+}
+
+/// The specific type error
+#[derive(Debug)]
+pub(crate) enum TypeMessage {
+ UnboundVariable(V<Label>),
+ InvalidInputType(Normalized),
+ InvalidOutputType(Normalized),
+ NotAFunction(Typed),
+ TypeMismatch(Typed, Normalized, Typed),
+ AnnotMismatch(Typed, Normalized),
+ Untyped,
+ InvalidListElement(usize, Normalized, Typed),
+ InvalidListType(Normalized),
+ InvalidOptionalType(Normalized),
+ InvalidPredicate(Typed),
+ IfBranchMismatch(Typed, Typed),
+ IfBranchMustBeTerm(bool, Typed),
+ InvalidFieldType(Label, Type),
+ NotARecord(Label, Normalized),
+ MissingRecordField(Label, Typed),
+ MissingUnionField(Label, Normalized),
+ BinOpTypeMismatch(BinOp, Typed),
+ NoDependentTypes(Normalized, Normalized),
+ InvalidTextInterpolation(Typed),
+ Sort,
+ Unimplemented,
+}
+
+impl TypeError {
+ pub(crate) fn new(
+ context: &TypecheckContext,
+ type_message: TypeMessage,
+ ) -> Self {
+ TypeError {
+ context: context.clone(),
+ type_message,
+ }
+ }
+}
+
+impl From<TypeError> for std::option::NoneError {
+ fn from(_: TypeError) -> std::option::NoneError {
+ std::option::NoneError
+ }
+}
+
+impl std::error::Error for TypeMessage {
+ fn description(&self) -> &str {
+ use TypeMessage::*;
+ match *self {
+ // UnboundVariable => "Unbound variable",
+ InvalidInputType(_) => "Invalid function input",
+ InvalidOutputType(_) => "Invalid function output",
+ NotAFunction(_) => "Not a function",
+ TypeMismatch(_, _, _) => "Wrong type of function argument",
+ _ => "Unhandled error",
+ }
+ }
+}
+
+impl std::fmt::Display for TypeMessage {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ // UnboundVariable(_) => {
+ // f.write_str(include_str!("errors/UnboundVariable.txt"))
+ // }
+ // TypeMismatch(e0, e1, e2) => {
+ // let template = include_str!("errors/TypeMismatch.txt");
+ // let s = template
+ // .replace("$txt0", &format!("{}", e0.as_expr()))
+ // .replace("$txt1", &format!("{}", e1.as_expr()))
+ // .replace("$txt2", &format!("{}", e2.as_expr()))
+ // .replace(
+ // "$txt3",
+ // &format!(
+ // "{}",
+ // e2.get_type()
+ // .unwrap()
+ // .as_normalized()
+ // .unwrap()
+ // .as_expr()
+ // ),
+ // );
+ // f.write_str(&s)
+ // }
+ _ => f.write_str("Unhandled error message"),
+ }
+ }
+}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ Error::IO(err) => write!(f, "{}", err),
+ Error::Parse(err) => write!(f, "{}", err),
+ Error::Decode(err) => write!(f, "{:?}", err),
+ Error::Resolve(err) => write!(f, "{:?}", err),
+ Error::Typecheck(err) => write!(f, "{:?}", err),
+ Error::Deserialize(err) => write!(f, "{}", err),
+ }
+ }
+}
+
+impl std::error::Error for Error {}
+impl From<IOError> for Error {
+ fn from(err: IOError) -> Error {
+ Error::IO(err)
+ }
+}
+impl From<ParseError> for Error {
+ fn from(err: ParseError) -> Error {
+ Error::Parse(err)
+ }
+}
+impl From<DecodeError> for Error {
+ fn from(err: DecodeError) -> Error {
+ Error::Decode(err)
+ }
+}
+impl From<ImportError> for Error {
+ fn from(err: ImportError) -> Error {
+ Error::Resolve(err)
+ }
+}
+impl From<TypeError> for Error {
+ fn from(err: TypeError) -> Error {
+ Error::Typecheck(err)
+ }
+}
diff --git a/dhall/src/errors/AnnotMismatch.txt b/dhall/src/error/text/AnnotMismatch.txt
index 4904bf8..4904bf8 100644
--- a/dhall/src/errors/AnnotMismatch.txt
+++ b/dhall/src/error/text/AnnotMismatch.txt
diff --git a/dhall/src/errors/CantTextAppend.txt b/dhall/src/error/text/CantTextAppend.txt
index 26b9ceb..26b9ceb 100644
--- a/dhall/src/errors/CantTextAppend.txt
+++ b/dhall/src/error/text/CantTextAppend.txt
diff --git a/dhall/src/errors/DuplicateAlternative.txt b/dhall/src/error/text/DuplicateAlternative.txt
index 077f8aa..077f8aa 100644
--- a/dhall/src/errors/DuplicateAlternative.txt
+++ b/dhall/src/error/text/DuplicateAlternative.txt
diff --git a/dhall/src/errors/FieldCollision.txt b/dhall/src/error/text/FieldCollision.txt
index 2b2d260..2b2d260 100644
--- a/dhall/src/errors/FieldCollision.txt
+++ b/dhall/src/error/text/FieldCollision.txt
diff --git a/dhall/src/errors/HandlerInputTypeMismatch.txt b/dhall/src/error/text/HandlerInputTypeMismatch.txt
index 7d3525b..7d3525b 100644
--- a/dhall/src/errors/HandlerInputTypeMismatch.txt
+++ b/dhall/src/error/text/HandlerInputTypeMismatch.txt
diff --git a/dhall/src/errors/HandlerNotAFunction.txt b/dhall/src/error/text/HandlerNotAFunction.txt
index ff87443..ff87443 100644
--- a/dhall/src/errors/HandlerNotAFunction.txt
+++ b/dhall/src/error/text/HandlerNotAFunction.txt
diff --git a/dhall/src/errors/HandlerOutputTypeMismatch.txt b/dhall/src/error/text/HandlerOutputTypeMismatch.txt
index f359459..f359459 100644
--- a/dhall/src/errors/HandlerOutputTypeMismatch.txt
+++ b/dhall/src/error/text/HandlerOutputTypeMismatch.txt
diff --git a/dhall/src/errors/IfBranchMismatch.txt b/dhall/src/error/text/IfBranchMismatch.txt
index a95b130..a95b130 100644
--- a/dhall/src/errors/IfBranchMismatch.txt
+++ b/dhall/src/error/text/IfBranchMismatch.txt
diff --git a/dhall/src/errors/IfBranchMustBeTerm.txt b/dhall/src/error/text/IfBranchMustBeTerm.txt
index 4c15881..4c15881 100644
--- a/dhall/src/errors/IfBranchMustBeTerm.txt
+++ b/dhall/src/error/text/IfBranchMustBeTerm.txt
diff --git a/dhall/src/errors/InvalidAlterantive.txt b/dhall/src/error/text/InvalidAlterantive.txt
index 391fc3a..391fc3a 100644
--- a/dhall/src/errors/InvalidAlterantive.txt
+++ b/dhall/src/error/text/InvalidAlterantive.txt
diff --git a/dhall/src/errors/InvalidAlterantiveType.txt b/dhall/src/error/text/InvalidAlterantiveType.txt
index f5dadef..f5dadef 100644
--- a/dhall/src/errors/InvalidAlterantiveType.txt
+++ b/dhall/src/error/text/InvalidAlterantiveType.txt
diff --git a/dhall/src/errors/InvalidField.txt b/dhall/src/error/text/InvalidField.txt
index bfbf106..bfbf106 100644
--- a/dhall/src/errors/InvalidField.txt
+++ b/dhall/src/error/text/InvalidField.txt
diff --git a/dhall/src/errors/InvalidFieldType.txt b/dhall/src/error/text/InvalidFieldType.txt
index 4f76a64..4f76a64 100644
--- a/dhall/src/errors/InvalidFieldType.txt
+++ b/dhall/src/error/text/InvalidFieldType.txt
diff --git a/dhall/src/errors/InvalidInputType.txt b/dhall/src/error/text/InvalidInputType.txt
index eabafa4..eabafa4 100644
--- a/dhall/src/errors/InvalidInputType.txt
+++ b/dhall/src/error/text/InvalidInputType.txt
diff --git a/dhall/src/errors/InvalidListElement.txt b/dhall/src/error/text/InvalidListElement.txt
index 59db7b7..59db7b7 100644
--- a/dhall/src/errors/InvalidListElement.txt
+++ b/dhall/src/error/text/InvalidListElement.txt
diff --git a/dhall/src/errors/InvalidListType.txt b/dhall/src/error/text/InvalidListType.txt
index 676647e..676647e 100644
--- a/dhall/src/errors/InvalidListType.txt
+++ b/dhall/src/error/text/InvalidListType.txt
diff --git a/dhall/src/errors/InvalidOptionType.txt b/dhall/src/error/text/InvalidOptionType.txt
index 3bc81de..3bc81de 100644
--- a/dhall/src/errors/InvalidOptionType.txt
+++ b/dhall/src/error/text/InvalidOptionType.txt
diff --git a/dhall/src/errors/InvalidOptionalElement.txt b/dhall/src/error/text/InvalidOptionalElement.txt
index 0254220..0254220 100644
--- a/dhall/src/errors/InvalidOptionalElement.txt
+++ b/dhall/src/error/text/InvalidOptionalElement.txt
diff --git a/dhall/src/errors/InvalidOptionalLiteral.txt b/dhall/src/error/text/InvalidOptionalLiteral.txt
index 41c0fdc..41c0fdc 100644
--- a/dhall/src/errors/InvalidOptionalLiteral.txt
+++ b/dhall/src/error/text/InvalidOptionalLiteral.txt
diff --git a/dhall/src/errors/InvalidOutputType.txt b/dhall/src/error/text/InvalidOutputType.txt
index dd2695d..dd2695d 100644
--- a/dhall/src/errors/InvalidOutputType.txt
+++ b/dhall/src/error/text/InvalidOutputType.txt
diff --git a/dhall/src/errors/InvalidPredicate.txt b/dhall/src/error/text/InvalidPredicate.txt
index 4c15881..4c15881 100644
--- a/dhall/src/errors/InvalidPredicate.txt
+++ b/dhall/src/error/text/InvalidPredicate.txt
diff --git a/dhall/src/errors/MissingField.txt b/dhall/src/error/text/MissingField.txt
index de14a33..de14a33 100644
--- a/dhall/src/errors/MissingField.txt
+++ b/dhall/src/error/text/MissingField.txt
diff --git a/dhall/src/errors/MissingHandler.txt b/dhall/src/error/text/MissingHandler.txt
index 433445e..433445e 100644
--- a/dhall/src/errors/MissingHandler.txt
+++ b/dhall/src/error/text/MissingHandler.txt
diff --git a/dhall/src/errors/MustCombineARecord.txt b/dhall/src/error/text/MustCombineARecord.txt
index 141b969..141b969 100644
--- a/dhall/src/errors/MustCombineARecord.txt
+++ b/dhall/src/error/text/MustCombineARecord.txt
diff --git a/dhall/src/errors/MustMergeARecord.txt b/dhall/src/error/text/MustMergeARecord.txt
index 79094bd..79094bd 100644
--- a/dhall/src/errors/MustMergeARecord.txt
+++ b/dhall/src/error/text/MustMergeARecord.txt
diff --git a/dhall/src/errors/MustMergeUnion.txt b/dhall/src/error/text/MustMergeUnion.txt
index 68df70c..68df70c 100644
--- a/dhall/src/errors/MustMergeUnion.txt
+++ b/dhall/src/error/text/MustMergeUnion.txt
diff --git a/dhall/src/errors/NoDependentLet.txt b/dhall/src/error/text/NoDependentLet.txt
index fdc65b4..fdc65b4 100644
--- a/dhall/src/errors/NoDependentLet.txt
+++ b/dhall/src/error/text/NoDependentLet.txt
diff --git a/dhall/src/errors/NoDependentTypes.txt b/dhall/src/error/text/NoDependentTypes.txt
index 435bdcb..435bdcb 100644
--- a/dhall/src/errors/NoDependentTypes.txt
+++ b/dhall/src/error/text/NoDependentTypes.txt
diff --git a/dhall/src/errors/NotAFunction.txt b/dhall/src/error/text/NotAFunction.txt
index dd2695d..dd2695d 100644
--- a/dhall/src/errors/NotAFunction.txt
+++ b/dhall/src/error/text/NotAFunction.txt
diff --git a/dhall/src/errors/NotARecord.txt b/dhall/src/error/text/NotARecord.txt
index e0eebc8..e0eebc8 100644
--- a/dhall/src/errors/NotARecord.txt
+++ b/dhall/src/error/text/NotARecord.txt
diff --git a/dhall/src/errors/TypeMismatch.txt b/dhall/src/error/text/TypeMismatch.txt
index 4904bf8..4904bf8 100644
--- a/dhall/src/errors/TypeMismatch.txt
+++ b/dhall/src/error/text/TypeMismatch.txt
diff --git a/dhall/src/errors/UnboundVariable.txt b/dhall/src/error/text/UnboundVariable.txt
index bd7d483..bd7d483 100644
--- a/dhall/src/errors/UnboundVariable.txt
+++ b/dhall/src/error/text/UnboundVariable.txt
diff --git a/dhall/src/errors/Untyped.txt b/dhall/src/error/text/Untyped.txt
index 4904bf8..4904bf8 100644
--- a/dhall/src/errors/Untyped.txt
+++ b/dhall/src/error/text/Untyped.txt
diff --git a/dhall/src/errors/UnusedHandler.txt b/dhall/src/error/text/UnusedHandler.txt
index 2e46a12..2e46a12 100644
--- a/dhall/src/errors/UnusedHandler.txt
+++ b/dhall/src/error/text/UnusedHandler.txt
diff --git a/dhall/src/expr.rs b/dhall/src/expr.rs
deleted file mode 100644
index 7dbbf1c..0000000
--- a/dhall/src/expr.rs
+++ /dev/null
@@ -1,244 +0,0 @@
-use crate::imports::ImportRoot;
-use crate::normalize::{Thunk, Value};
-use dhall_syntax::*;
-
-macro_rules! derive_other_traits {
- ($ty:ident) => {
- impl std::cmp::PartialEq for $ty {
- fn eq(&self, other: &Self) -> bool {
- self.0 == other.0
- }
- }
-
- impl std::cmp::Eq for $ty {}
-
- impl std::fmt::Display for $ty {
- fn fmt(
- &self,
- f: &mut std::fmt::Formatter,
- ) -> Result<(), std::fmt::Error> {
- self.0.fmt(f)
- }
- }
- };
-}
-
-#[derive(Debug, Clone)]
-pub(crate) struct Parsed(
- pub(crate) SubExpr<Span, Import>,
- pub(crate) ImportRoot,
-);
-derive_other_traits!(Parsed);
-
-#[derive(Debug, Clone)]
-pub(crate) struct Resolved(pub(crate) SubExpr<Span, Normalized>);
-derive_other_traits!(Resolved);
-
-pub(crate) use self::typed::TypedInternal;
-
-#[derive(Debug, Clone)]
-pub(crate) struct Typed(pub(crate) TypedInternal);
-
-#[derive(Debug, Clone)]
-pub(crate) struct Normalized(pub(crate) TypedInternal);
-
-impl std::cmp::PartialEq for Normalized {
- fn eq(&self, other: &Self) -> bool {
- self.0 == other.0
- }
-}
-
-impl std::cmp::Eq for Normalized {}
-
-impl std::fmt::Display for Normalized {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
- self.to_expr().fmt(f)
- }
-}
-
-mod typed {
- use super::{Type, Typed};
- use crate::normalize::{AlphaVar, Thunk, Value};
- use crate::typecheck::{
- TypeError, TypeInternal, TypeMessage, TypecheckContext,
- };
- use dhall_syntax::{Const, SubExpr, X};
- use std::borrow::Cow;
-
- #[derive(Debug, Clone)]
- pub(crate) enum TypedInternal {
- // The `Sort` higher-kinded type doesn't have a type
- Sort,
- // Any other value, along with its type
- Value(Thunk, Option<Type>),
- }
-
- impl TypedInternal {
- pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self {
- TypedInternal::Value(th, Some(t))
- }
-
- pub(crate) fn from_thunk_untyped(th: Thunk) -> Self {
- TypedInternal::Value(th, None)
- }
-
- // TODO: Avoid cloning if possible
- pub(crate) fn to_value(&self) -> Value {
- match self {
- TypedInternal::Value(th, _) => th.to_value(),
- TypedInternal::Sort => Value::Const(Const::Sort),
- }
- }
-
- pub(crate) fn to_expr(&self) -> SubExpr<X, X> {
- self.to_value().normalize_to_expr()
- }
-
- pub(crate) fn to_expr_alpha(&self) -> SubExpr<X, X> {
- self.to_value().normalize_to_expr_maybe_alpha(true)
- }
-
- pub(crate) fn to_thunk(&self) -> Thunk {
- match self {
- TypedInternal::Value(th, _) => th.clone(),
- TypedInternal::Sort => {
- Thunk::from_value(Value::Const(Const::Sort))
- }
- }
- }
-
- pub(crate) fn to_type(&self) -> Type {
- match self {
- TypedInternal::Sort => Type(TypeInternal::Const(Const::Sort)),
- TypedInternal::Value(th, _) => match &*th.as_value() {
- Value::Const(c) => Type(TypeInternal::Const(*c)),
- _ => {
- Type(TypeInternal::Typed(Box::new(Typed(self.clone()))))
- }
- },
- }
- }
-
- pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> {
- match self {
- TypedInternal::Value(_, Some(t)) => Ok(Cow::Borrowed(t)),
- TypedInternal::Value(_, None) => Err(TypeError::new(
- &TypecheckContext::new(),
- TypeMessage::Untyped,
- )),
- TypedInternal::Sort => Err(TypeError::new(
- &TypecheckContext::new(),
- TypeMessage::Sort,
- )),
- }
- }
-
- pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- match self {
- TypedInternal::Value(th, t) => TypedInternal::Value(
- th.shift(delta, var),
- t.as_ref().map(|x| x.shift(delta, var)),
- ),
- TypedInternal::Sort => TypedInternal::Sort,
- }
- }
-
- pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- match self {
- TypedInternal::Value(th, t) => TypedInternal::Value(
- th.subst_shift(var, val),
- t.as_ref().map(|x| x.subst_shift(var, val)),
- ),
- TypedInternal::Sort => TypedInternal::Sort,
- }
- }
- }
-
- impl std::cmp::PartialEq for TypedInternal {
- fn eq(&self, other: &Self) -> bool {
- self.to_value() == other.to_value()
- }
- }
-
- impl std::cmp::Eq for TypedInternal {}
-}
-
-/// A Dhall expression representing a simple type.
-///
-/// This captures what is usually simply called a "type", like
-/// `Bool`, `{ x: Integer }` or `Natural -> Text`.
-///
-/// For a more general notion of "type", see [Type].
-#[derive(Debug, Clone)]
-pub struct SimpleType(pub(crate) SubExpr<X, X>);
-derive_other_traits!(SimpleType);
-
-pub(crate) use crate::typecheck::TypeInternal;
-
-/// A Dhall expression representing a (possibly higher-kinded) type.
-///
-/// This includes [SimpleType]s but also higher-kinded expressions like
-/// `Type`, `Kind` and `{ x: Type }`.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Type(pub(crate) TypeInternal);
-
-impl std::fmt::Display for Type {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
- self.to_normalized().fmt(f)
- }
-}
-
-// Exposed for the macros
-#[doc(hidden)]
-impl From<SimpleType> for SubExpr<X, X> {
- fn from(x: SimpleType) -> SubExpr<X, X> {
- x.0
- }
-}
-
-// Exposed for the macros
-#[doc(hidden)]
-impl From<SubExpr<X, X>> for SimpleType {
- fn from(x: SubExpr<X, X>) -> SimpleType {
- SimpleType(x)
- }
-}
-
-// Exposed for the macros
-#[doc(hidden)]
-impl From<Normalized> for Typed {
- fn from(x: Normalized) -> Typed {
- Typed(x.0)
- }
-}
-
-impl Normalized {
- pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self {
- Normalized(TypedInternal::from_thunk_and_type(th, t))
- }
- pub(crate) fn to_expr(&self) -> SubExpr<X, X> {
- self.0.to_expr()
- }
- #[allow(dead_code)]
- pub(crate) fn to_expr_alpha(&self) -> SubExpr<X, X> {
- self.0.to_expr_alpha()
- }
- pub(crate) fn to_value(&self) -> Value {
- self.0.to_value()
- }
- pub(crate) fn to_thunk(&self) -> Thunk {
- self.0.to_thunk()
- }
-}
-
-impl Typed {
- pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self {
- Typed(TypedInternal::from_thunk_and_type(th, t))
- }
- pub(crate) fn from_thunk_untyped(th: Thunk) -> Self {
- Typed(TypedInternal::from_thunk_untyped(th))
- }
- pub(crate) fn const_sort() -> Self {
- Typed(TypedInternal::Sort)
- }
-}
diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs
index a531f64..8d1496a 100644
--- a/dhall/src/lib.rs
+++ b/dhall/src/lib.rs
@@ -123,51 +123,10 @@
#[macro_use]
mod tests;
-#[cfg(test)]
-mod parser;
-
-mod binary;
-/// When manipulating Dhall expressions goes wrong.
+pub(crate) mod api;
+pub(crate) mod core;
pub mod error;
-pub mod expr;
-mod imports;
-mod normalize;
-mod serde;
-mod traits;
-mod typecheck;
-
-/// Deserialization of Dhall expressions into Rust
-pub mod de {
- pub use crate::traits::{Deserialize, SimpleStaticType, StaticType};
- #[doc(hidden)]
- pub use dhall_proc_macros::SimpleStaticType;
-
- /// Deserialize an instance of type T from a string of Dhall text.
- ///
- /// This will recursively resolve all imports in the expression, and
- /// typecheck it before deserialization. Relative imports will be resolved relative to the
- /// provided file. More control over this process is not yet available
- /// but will be in a coming version of this crate.
- ///
- /// If a type is provided, this additionally checks that the provided
- /// expression has that type.
- pub fn from_str<'a, T: Deserialize<'a>>(
- s: &'a str,
- ty: Option<&crate::expr::Type>,
- ) -> crate::error::Result<T> {
- T::from_str(s, ty)
- }
+pub(crate) mod phase;
+pub(crate) use api::traits;
- /// Deserialize an instance of type T from a string of Dhall text,
- /// additionally checking that it matches the type of T.
- ///
- /// This will recursively resolve all imports in the expression, and
- /// typecheck it before deserialization. Relative imports will be resolved relative to the
- /// provided file. More control over this process is not yet available
- /// but will be in a coming version of this crate.
- pub fn from_str_auto_type<'a, T: Deserialize<'a> + StaticType>(
- s: &'a str,
- ) -> crate::error::Result<T> {
- from_str(s, Some(&<T as StaticType>::get_static_type()))
- }
-}
+pub use api::de;
diff --git a/dhall/src/main.rs b/dhall/src/main.rs
deleted file mode 100644
index e25a535..0000000
--- a/dhall/src/main.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-// use std::error::Error;
-// use std::io::{self, Read};
-// use term_painter::ToStyle;
-
-// const ERROR_STYLE: term_painter::Color = term_painter::Color::Red;
-// const BOLD: term_painter::Attr = term_painter::Attr::Bold;
-
-// fn print_error(message: &str, source: &str, start: usize, end: usize) {
-// let line_number = bytecount::count(source[..start].as_bytes(), b'\n');
-// let line_start = source[..start].rfind('\n').map(|i| i + 1).unwrap_or(0);
-// let line_end = source[end..].find('\n').unwrap_or(0) + end;
-// let context_prefix = &source[line_start..start];
-// let context_highlighted = &source[start..end];
-// let context_suffix = &source[end..line_end];
-
-// let line_number_str = line_number.to_string();
-// let line_number_width = line_number_str.len();
-
-// BOLD.with(|| {
-// ERROR_STYLE.with(|| {
-// print!("error: ");
-// });
-// println!("{}", message);
-// });
-// BOLD.with(|| {
-// print!(" -->");
-// });
-// println!(" (stdin):{}:0", line_number);
-// BOLD.with(|| {
-// println!("{:w$} |", "", w = line_number_width);
-// print!("{} |", line_number_str);
-// });
-// print!(" {}", context_prefix);
-// BOLD.with(|| {
-// ERROR_STYLE.with(|| {
-// print!("{}", context_highlighted);
-// });
-// });
-// println!("{}", context_suffix);
-// BOLD.with(|| {
-// print!("{:w$} |", "", w = line_number_width);
-// ERROR_STYLE.with(|| {
-// println!(
-// " {:so$}{:^>ew$}",
-// "",
-// "",
-// so = source[line_start..start].chars().count(),
-// ew = ::std::cmp::max(1, source[start..end].chars().count())
-// );
-// });
-// });
-// }
-
-fn main() {
- // let mut buffer = String::new();
- // io::stdin().read_to_string(&mut buffer).unwrap();
-
- // TODO: public API is too restricted for this
- // let expr = match dhall::expr::Parsed::parse_str(&buffer) {
- // Ok(expr) => expr,
- // Err(e) => {
- // print_error(&format!("Parse error {}", e), &buffer, 0, 0);
- // return;
- // }
- // };
-
- // let expr = expr.resolve().unwrap();
-
- // let expr = match expr.typecheck() {
- // Ok(expr) => expr,
- // Err(e) => {
- // // TODO: implement pretty type error printing
- // // let explain = ::std::env::args().any(|s| s == "--explain");
- // // if !explain {
- // // term_painter::Color::BrightBlack.with(|| {
- // // println!("Use \"dhall --explain\" for detailed errors");
- // // });
- // // }
- // // ERROR_STYLE.with(|| print!("Error: "));
- // // println!("{}", e.type_message.description());
- // // if explain {
- // // println!("{}", e.type_message);
- // // }
- // // println!("{}", e.current);
- // // // FIXME Print source position
- // return;
- // }
- // };
-
- // let expr = expr.normalize();
-
- // println!("{}", expr);
-}
diff --git a/dhall/src/parser.rs b/dhall/src/parser.rs
deleted file mode 100644
index 3648c4f..0000000
--- a/dhall/src/parser.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-#[cfg(test)]
-mod spec_tests {
- #![rustfmt::skip]
- // See ../build.rs
- include!(concat!(env!("OUT_DIR"), "/parser_tests.rs"));
-}
diff --git a/dhall/src/binary.rs b/dhall/src/phase/binary.rs
index 9c31d4c..30aa7a0 100644
--- a/dhall/src/binary.rs
+++ b/dhall/src/phase/binary.rs
@@ -1,14 +1,15 @@
-use dhall_syntax::*;
-use itertools::*;
+use itertools::Itertools;
use serde_cbor::value::value as cbor;
-type ParsedExpr = SubExpr<X, Import>;
+use dhall_syntax::{
+ rc, ExprF, FilePrefix, Hash, Import, ImportHashed, ImportLocation,
+ ImportMode, Integer, InterpolatedText, Label, Natural, Scheme, SubExpr,
+ URL, V, X,
+};
-#[derive(Debug)]
-pub enum DecodeError {
- CBORError(serde_cbor::error::Error),
- WrongFormatError(String),
-}
+use crate::error::DecodeError;
+
+type ParsedExpr = SubExpr<X, Import>;
pub fn decode(data: &[u8]) -> Result<ParsedExpr, DecodeError> {
match serde_cbor::de::from_slice(data) {
diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs
new file mode 100644
index 0000000..c8a8ffd
--- /dev/null
+++ b/dhall/src/phase/mod.rs
@@ -0,0 +1,334 @@
+use std::borrow::Cow;
+use std::fmt::Display;
+use std::path::Path;
+
+use dhall_syntax::{Const, Import, Span, SubExpr, X};
+
+use crate::core::context::TypecheckContext;
+use crate::core::thunk::Thunk;
+use crate::core::value::{AlphaVar, Value};
+use crate::error::{Error, ImportError, TypeError, TypeMessage};
+
+use resolve::ImportRoot;
+use typecheck::type_of_const;
+
+pub(crate) mod binary;
+pub(crate) mod normalize;
+pub(crate) mod parse;
+pub(crate) mod resolve;
+pub(crate) mod typecheck;
+
+pub(crate) type ParsedSubExpr = SubExpr<Span, Import>;
+pub(crate) type ResolvedSubExpr = SubExpr<Span, Normalized>;
+pub(crate) type NormalizedSubExpr = SubExpr<X, X>;
+
+#[derive(Debug, Clone)]
+pub(crate) struct Parsed(pub(crate) ParsedSubExpr, pub(crate) ImportRoot);
+
+/// An expression where all imports have been resolved
+#[derive(Debug, Clone)]
+pub(crate) struct Resolved(pub(crate) ResolvedSubExpr);
+
+/// A typed expression
+#[derive(Debug, Clone)]
+pub(crate) enum Typed {
+ // Any value, along with (optionally) its type
+ Value(Thunk, Option<Type>),
+ // One of the base higher-kinded typed.
+ // Used to avoid storing the same tower ot Type->Kind->Sort
+ // over and over again. Also enables having Sort as a type
+ // even though it doesn't itself have a type.
+ Const(Const),
+}
+
+/// A normalized expression.
+///
+/// Invariant: the contained Typed expression must be in normal form,
+#[derive(Debug, Clone)]
+pub(crate) struct Normalized(pub(crate) Typed);
+
+/// A Dhall expression representing a simple type.
+///
+/// This captures what is usually simply called a "type", like
+/// `Bool`, `{ x: Integer }` or `Natural -> Text`.
+///
+/// For a more general notion of "type", see [Type].
+#[derive(Debug, Clone)]
+pub struct SimpleType(pub(crate) NormalizedSubExpr);
+
+/// A Dhall expression representing a (possibly higher-kinded) type.
+///
+/// This includes [SimpleType]s but also higher-kinded expressions like
+/// `Type`, `Kind` and `{ x: Type }`.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Type(pub(crate) Box<Typed>);
+
+impl Parsed {
+ pub fn parse_file(f: &Path) -> Result<Parsed, Error> {
+ parse::parse_file(f)
+ }
+
+ pub fn parse_str(s: &str) -> Result<Parsed, Error> {
+ parse::parse_str(s)
+ }
+
+ #[allow(dead_code)]
+ pub fn parse_binary_file(f: &Path) -> Result<Parsed, Error> {
+ parse::parse_binary_file(f)
+ }
+
+ pub fn resolve(self) -> Result<Resolved, ImportError> {
+ resolve::resolve(self)
+ }
+
+ #[allow(dead_code)]
+ pub fn skip_resolve(self) -> Result<Resolved, ImportError> {
+ resolve::skip_resolve_expr(self)
+ }
+}
+
+impl Resolved {
+ pub fn typecheck(self) -> Result<Typed, TypeError> {
+ typecheck::typecheck(self)
+ }
+ pub fn typecheck_with(self, ty: &Type) -> Result<Typed, TypeError> {
+ typecheck::typecheck_with(self, ty)
+ }
+ /// Pretends this expression has been typechecked. Use with care.
+ #[allow(dead_code)]
+ pub fn skip_typecheck(self) -> Typed {
+ typecheck::skip_typecheck(self)
+ }
+}
+
+impl Typed {
+ /// Reduce an expression to its normal form, performing beta reduction
+ ///
+ /// `normalize` does not type-check the expression. You may want to type-check
+ /// expressions before normalizing them since normalization can convert an
+ /// ill-typed expression into a well-typed expression.
+ ///
+ /// However, `normalize` will not fail if the expression is ill-typed and will
+ /// leave ill-typed sub-expressions unevaluated.
+ pub fn normalize(self) -> Normalized {
+ match &self {
+ Typed::Const(_) => {}
+ Typed::Value(thunk, _) => {
+ thunk.normalize_nf();
+ }
+ }
+ Normalized(self)
+ }
+
+ pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self {
+ Typed::Value(th, Some(t))
+ }
+ pub(crate) fn from_thunk_untyped(th: Thunk) -> Self {
+ Typed::Value(th, None)
+ }
+ pub(crate) fn from_const(c: Const) -> Self {
+ Typed::Const(c)
+ }
+
+ // TODO: Avoid cloning if possible
+ pub(crate) fn to_value(&self) -> Value {
+ match self {
+ Typed::Value(th, _) => th.to_value(),
+ Typed::Const(c) => Value::Const(*c),
+ }
+ }
+ pub(crate) fn to_expr(&self) -> NormalizedSubExpr {
+ self.to_value().normalize_to_expr()
+ }
+ pub(crate) fn to_expr_alpha(&self) -> NormalizedSubExpr {
+ self.to_value().normalize_to_expr_maybe_alpha(true)
+ }
+ pub(crate) fn to_thunk(&self) -> Thunk {
+ match self {
+ Typed::Value(th, _) => th.clone(),
+ Typed::Const(c) => Thunk::from_value(Value::Const(*c)),
+ }
+ }
+ // Deprecated
+ pub(crate) fn to_type(&self) -> Type {
+ self.clone().into_type()
+ }
+ pub(crate) fn into_type(self) -> Type {
+ Type(Box::new(self))
+ }
+
+ pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> {
+ match self {
+ Typed::Value(_, Some(t)) => Ok(Cow::Borrowed(t)),
+ Typed::Value(_, None) => Err(TypeError::new(
+ &TypecheckContext::new(),
+ TypeMessage::Untyped,
+ )),
+ Typed::Const(c) => Ok(Cow::Owned(type_of_const(*c)?)),
+ }
+ }
+
+ pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ match self {
+ Typed::Value(th, t) => Typed::Value(
+ th.shift(delta, var),
+ t.as_ref().map(|x| x.shift(delta, var)),
+ ),
+ Typed::Const(c) => Typed::Const(*c),
+ }
+ }
+
+ pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
+ match self {
+ Typed::Value(th, t) => Typed::Value(
+ th.subst_shift(var, val),
+ t.as_ref().map(|x| x.subst_shift(var, val)),
+ ),
+ Typed::Const(c) => Typed::Const(*c),
+ }
+ }
+}
+
+impl Type {
+ pub(crate) fn to_normalized(&self) -> Normalized {
+ self.0.clone().normalize()
+ }
+ pub(crate) fn to_expr(&self) -> NormalizedSubExpr {
+ self.0.to_expr()
+ }
+ pub(crate) fn to_value(&self) -> Value {
+ self.0.to_value()
+ }
+ pub(crate) fn as_const(&self) -> Option<Const> {
+ // TODO: avoid clone
+ match &self.to_value() {
+ Value::Const(c) => Some(*c),
+ _ => None,
+ }
+ }
+ pub(crate) fn internal_whnf(&self) -> Option<Value> {
+ Some(self.to_value())
+ }
+ pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> {
+ self.0.get_type()
+ }
+
+ pub(crate) fn const_sort() -> Self {
+ Type::from_const(Const::Sort)
+ }
+ pub(crate) fn const_kind() -> Self {
+ Type::from_const(Const::Kind)
+ }
+ pub(crate) fn const_type() -> Self {
+ Type::from_const(Const::Type)
+ }
+ pub(crate) fn from_const(c: Const) -> Self {
+ Type(Box::new(Typed::from_const(c)))
+ }
+
+ pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ Type(Box::new(self.0.shift(delta, var)))
+ }
+ pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
+ Type(Box::new(self.0.subst_shift(var, val)))
+ }
+}
+
+impl Normalized {
+ pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self {
+ Normalized(Typed::from_thunk_and_type(th, t))
+ }
+
+ pub(crate) fn to_expr(&self) -> NormalizedSubExpr {
+ self.0.to_expr()
+ }
+ #[allow(dead_code)]
+ pub(crate) fn to_expr_alpha(&self) -> NormalizedSubExpr {
+ self.0.to_expr_alpha()
+ }
+ pub(crate) fn to_value(&self) -> Value {
+ self.0.to_value()
+ }
+ pub(crate) fn to_thunk(&self) -> Thunk {
+ self.0.to_thunk()
+ }
+ pub(crate) fn to_type(self) -> Type {
+ self.0.to_type()
+ }
+ pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> {
+ self.0.get_type()
+ }
+
+ pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
+ Normalized(self.0.shift(delta, var))
+ }
+}
+
+macro_rules! derive_traits_for_wrapper_struct {
+ ($ty:ident) => {
+ impl std::cmp::PartialEq for $ty {
+ fn eq(&self, other: &Self) -> bool {
+ self.0 == other.0
+ }
+ }
+
+ impl std::cmp::Eq for $ty {}
+
+ impl std::fmt::Display for $ty {
+ fn fmt(
+ &self,
+ f: &mut std::fmt::Formatter,
+ ) -> Result<(), std::fmt::Error> {
+ self.0.fmt(f)
+ }
+ }
+ };
+}
+
+derive_traits_for_wrapper_struct!(Parsed);
+derive_traits_for_wrapper_struct!(Resolved);
+derive_traits_for_wrapper_struct!(Normalized);
+derive_traits_for_wrapper_struct!(SimpleType);
+
+impl Eq for Typed {}
+impl PartialEq for Typed {
+ fn eq(&self, other: &Self) -> bool {
+ self.to_value() == other.to_value()
+ }
+}
+
+impl Display for Typed {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ self.to_expr().fmt(f)
+ }
+}
+
+impl Display for Type {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ self.to_normalized().fmt(f)
+ }
+}
+
+// Exposed for the macros
+#[doc(hidden)]
+impl From<SimpleType> for NormalizedSubExpr {
+ fn from(x: SimpleType) -> NormalizedSubExpr {
+ x.0
+ }
+}
+
+// Exposed for the macros
+#[doc(hidden)]
+impl From<NormalizedSubExpr> for SimpleType {
+ fn from(x: NormalizedSubExpr) -> SimpleType {
+ SimpleType(x)
+ }
+}
+
+// Exposed for the macros
+#[doc(hidden)]
+impl From<Normalized> for Typed {
+ fn from(x: Normalized) -> Typed {
+ x.0
+ }
+}
diff --git a/dhall/src/normalize.rs b/dhall/src/phase/normalize.rs
index 35ab45d..5dfcfb6 100644
--- a/dhall/src/normalize.rs
+++ b/dhall/src/phase/normalize.rs
@@ -1,1025 +1,16 @@
-#![allow(non_snake_case)]
use std::collections::BTreeMap;
-use std::rc::Rc;
-use dhall_proc_macros as dhall;
-use dhall_syntax::context::Context;
-use dhall_syntax::{
- rc, BinOp, Builtin, Const, ExprF, Integer, InterpolatedTextContents, Label,
- Natural, Span, SubExpr, V, X,
-};
+use dhall_syntax::{BinOp, Builtin, ExprF, InterpolatedTextContents, Label, X};
-use crate::expr::{Normalized, Type, Typed, TypedInternal};
+use crate::core::context::NormalizationContext;
+use crate::core::thunk::{Thunk, TypeThunk};
+use crate::core::value::Value;
+use crate::phase::{NormalizedSubExpr, ResolvedSubExpr, Typed};
-type InputSubExpr = SubExpr<Span, Normalized>;
-type OutputSubExpr = SubExpr<X, X>;
+pub(crate) type InputSubExpr = ResolvedSubExpr;
+pub(crate) type OutputSubExpr = NormalizedSubExpr;
-impl Typed {
- /// Reduce an expression to its normal form, performing beta reduction
- ///
- /// `normalize` does not type-check the expression. You may want to type-check
- /// expressions before normalizing them since normalization can convert an
- /// ill-typed expression into a well-typed expression.
- ///
- /// However, `normalize` will not fail if the expression is ill-typed and will
- /// leave ill-typed sub-expressions unevaluated.
- ///
- pub fn normalize(self) -> Normalized {
- match &self.0 {
- TypedInternal::Sort => {}
- TypedInternal::Value(thunk, _) => {
- thunk.normalize_nf();
- }
- }
- Normalized(self.0)
- }
-
- pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- Typed(self.0.shift(delta, var))
- }
-
- pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- Typed(self.0.subst_shift(var, val))
- }
-
- pub(crate) fn to_value(&self) -> Value {
- self.0.to_value()
- }
-
- pub(crate) fn to_thunk(&self) -> Thunk {
- self.0.to_thunk()
- }
-}
-
-/// Stores a pair of variables: a normal one and if relevant one
-/// that corresponds to the alpha-normalized version of the first one.
-#[derive(Debug, Clone, Eq)]
-pub(crate) struct AlphaVar {
- normal: V<Label>,
- alpha: Option<V<()>>,
-}
-
-impl AlphaVar {
- pub(crate) fn to_var(&self, alpha: bool) -> V<Label> {
- match (alpha, &self.alpha) {
- (true, Some(x)) => V("_".into(), x.1),
- _ => self.normal.clone(),
- }
- }
- pub(crate) fn from_var(normal: V<Label>) -> Self {
- AlphaVar {
- normal,
- alpha: None,
- }
- }
- pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- AlphaVar {
- normal: self.normal.shift(delta, &var.normal),
- alpha: match (&self.alpha, &var.alpha) {
- (Some(x), Some(v)) => Some(x.shift(delta, v)),
- _ => None,
- },
- }
- }
-}
-
-/// Equality is up to alpha-equivalence.
-impl std::cmp::PartialEq for AlphaVar {
- fn eq(&self, other: &Self) -> bool {
- match (&self.alpha, &other.alpha) {
- (Some(x), Some(y)) => x == y,
- (None, None) => self.normal == other.normal,
- _ => false,
- }
- }
-}
-
-impl From<Label> for AlphaVar {
- fn from(x: Label) -> AlphaVar {
- AlphaVar {
- normal: V(x, 0),
- alpha: Some(V((), 0)),
- }
- }
-}
-impl<'a> From<&'a Label> for AlphaVar {
- fn from(x: &'a Label) -> AlphaVar {
- x.clone().into()
- }
-}
-impl From<AlphaLabel> for AlphaVar {
- fn from(x: AlphaLabel) -> AlphaVar {
- let l: Label = x.into();
- l.into()
- }
-}
-impl<'a> From<&'a AlphaLabel> for AlphaVar {
- fn from(x: &'a AlphaLabel) -> AlphaVar {
- x.clone().into()
- }
-}
-
-// Exactly like a Label, but equality returns always true.
-// This is so that Value equality is exactly alpha-equivalence.
-#[derive(Debug, Clone, Eq)]
-pub(crate) struct AlphaLabel(Label);
-
-impl AlphaLabel {
- fn to_label_maybe_alpha(&self, alpha: bool) -> Label {
- if alpha {
- "_".into()
- } else {
- self.to_label()
- }
- }
- fn to_label(&self) -> Label {
- self.clone().into()
- }
-}
-
-impl std::cmp::PartialEq for AlphaLabel {
- fn eq(&self, _other: &Self) -> bool {
- true
- }
-}
-
-impl From<Label> for AlphaLabel {
- fn from(x: Label) -> AlphaLabel {
- AlphaLabel(x)
- }
-}
-impl From<AlphaLabel> for Label {
- fn from(x: AlphaLabel) -> Label {
- x.0
- }
-}
-
-#[derive(Debug, Clone)]
-enum EnvItem {
- Thunk(Thunk),
- Skip(AlphaVar),
-}
-
-impl EnvItem {
- fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- use EnvItem::*;
- match self {
- Thunk(e) => Thunk(e.shift(delta, var)),
- Skip(v) => Skip(v.shift(delta, var)),
- }
- }
-
- pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- match self {
- EnvItem::Thunk(e) => EnvItem::Thunk(e.subst_shift(var, val)),
- EnvItem::Skip(v) if v == var => EnvItem::Thunk(val.to_thunk()),
- EnvItem::Skip(v) => EnvItem::Skip(v.shift(-1, var)),
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub(crate) struct NormalizationContext(Rc<Context<Label, EnvItem>>);
-
-impl NormalizationContext {
- pub(crate) fn new() -> Self {
- NormalizationContext(Rc::new(Context::new()))
- }
- fn skip(&self, x: &Label) -> Self {
- NormalizationContext(Rc::new(
- self.0
- .map(|_, e| e.shift(1, &x.into()))
- .insert(x.clone(), EnvItem::Skip(x.into())),
- ))
- }
- fn lookup(&self, var: &V<Label>) -> Value {
- let V(x, n) = var;
- match self.0.lookup(x, *n) {
- Some(EnvItem::Thunk(t)) => t.to_value(),
- Some(EnvItem::Skip(newvar)) => Value::Var(newvar.clone()),
- // Free variable
- None => Value::Var(AlphaVar::from_var(var.clone())),
- }
- }
- pub(crate) fn from_typecheck_ctx(
- tc_ctx: &crate::typecheck::TypecheckContext,
- ) -> Self {
- use crate::typecheck::EnvItem::*;
- let mut ctx = Context::new();
- for (k, vs) in tc_ctx.0.iter_keys() {
- for v in vs.iter() {
- let new_item = match v {
- Type(var, _) => EnvItem::Skip(var.clone()),
- Value(e) => EnvItem::Thunk(e.to_thunk()),
- };
- ctx = ctx.insert(k.clone(), new_item);
- }
- }
- NormalizationContext(Rc::new(ctx))
- }
-
- fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- NormalizationContext(Rc::new(self.0.map(|_, e| e.shift(delta, var))))
- }
-
- fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- NormalizationContext(Rc::new(
- self.0.map(|_, e| e.subst_shift(var, val)),
- ))
- }
-}
-
-/// A semantic value.
-/// Equality is up to alpha-equivalence (renaming of bound variables).
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) enum Value {
- /// Closures
- Lam(AlphaLabel, Thunk, Thunk),
- AppliedBuiltin(Builtin, Vec<Thunk>),
- /// `λ(x: a) -> Some x`
- OptionalSomeClosure(TypeThunk),
- /// `λ(x : a) -> λ(xs : List a) -> [ x ] # xs`
- /// `λ(xs : List a) -> [ x ] # xs`
- ListConsClosure(TypeThunk, Option<Thunk>),
- /// `λ(x : Natural) -> x + 1`
- NaturalSuccClosure,
- Pi(AlphaLabel, TypeThunk, TypeThunk),
-
- Var(AlphaVar),
- Const(Const),
- BoolLit(bool),
- NaturalLit(Natural),
- IntegerLit(Integer),
- EmptyOptionalLit(TypeThunk),
- NEOptionalLit(Thunk),
- EmptyListLit(TypeThunk),
- NEListLit(Vec<Thunk>),
- RecordLit(BTreeMap<Label, Thunk>),
- RecordType(BTreeMap<Label, TypeThunk>),
- UnionType(BTreeMap<Label, Option<TypeThunk>>),
- UnionConstructor(Label, BTreeMap<Label, Option<TypeThunk>>),
- UnionLit(Label, Thunk, BTreeMap<Label, Option<TypeThunk>>),
- TextLit(Vec<InterpolatedTextContents<Thunk>>),
- /// Invariant: This must not contain a value captured by one of the variants above.
- PartialExpr(ExprF<Thunk, Label, X>),
-}
-
-impl Value {
- pub(crate) fn into_thunk(self) -> Thunk {
- Thunk::from_value(self)
- }
-
- /// Convert the value to a fully normalized syntactic expression
- pub(crate) fn normalize_to_expr(&self) -> OutputSubExpr {
- self.normalize_to_expr_maybe_alpha(false)
- }
- /// Convert the value to a fully normalized syntactic expression. Also alpha-normalize
- /// if alpha is `true`
- pub(crate) fn normalize_to_expr_maybe_alpha(
- &self,
- alpha: bool,
- ) -> OutputSubExpr {
- match self {
- Value::Lam(x, t, e) => rc(ExprF::Lam(
- x.to_label_maybe_alpha(alpha),
- t.normalize_to_expr_maybe_alpha(alpha),
- e.normalize_to_expr_maybe_alpha(alpha),
- )),
- Value::AppliedBuiltin(b, args) => {
- let mut e = rc(ExprF::Builtin(*b));
- for v in args {
- e = rc(ExprF::App(
- e,
- v.normalize_to_expr_maybe_alpha(alpha),
- ));
- }
- e
- }
- Value::OptionalSomeClosure(n) => {
- let a = n.normalize_to_expr_maybe_alpha(alpha);
- dhall::subexpr!(λ(x: a) -> Some x)
- }
- Value::ListConsClosure(a, None) => {
- // Avoid accidental capture of the new `x` variable
- let a1 = a.shift(1, &Label::from("x").into());
- let a1 = a1.normalize_to_expr_maybe_alpha(alpha);
- let a = a.normalize_to_expr_maybe_alpha(alpha);
- dhall::subexpr!(λ(x : a) -> λ(xs : List a1) -> [ x ] # xs)
- }
- Value::ListConsClosure(n, Some(v)) => {
- // Avoid accidental capture of the new `xs` variable
- let v = v.shift(1, &Label::from("xs").into());
- let v = v.normalize_to_expr_maybe_alpha(alpha);
- let a = n.normalize_to_expr_maybe_alpha(alpha);
- dhall::subexpr!(λ(xs : List a) -> [ v ] # xs)
- }
- Value::NaturalSuccClosure => {
- dhall::subexpr!(λ(x : Natural) -> x + 1)
- }
- Value::Pi(x, t, e) => rc(ExprF::Pi(
- x.to_label_maybe_alpha(alpha),
- t.normalize_to_expr_maybe_alpha(alpha),
- e.normalize_to_expr_maybe_alpha(alpha),
- )),
- Value::Var(v) => rc(ExprF::Var(v.to_var(alpha))),
- Value::Const(c) => rc(ExprF::Const(*c)),
- Value::BoolLit(b) => rc(ExprF::BoolLit(*b)),
- Value::NaturalLit(n) => rc(ExprF::NaturalLit(*n)),
- Value::IntegerLit(n) => rc(ExprF::IntegerLit(*n)),
- Value::EmptyOptionalLit(n) => rc(ExprF::App(
- rc(ExprF::Builtin(Builtin::OptionalNone)),
- n.normalize_to_expr_maybe_alpha(alpha),
- )),
- Value::NEOptionalLit(n) => {
- rc(ExprF::SomeLit(n.normalize_to_expr_maybe_alpha(alpha)))
- }
- Value::EmptyListLit(n) => {
- rc(ExprF::EmptyListLit(n.normalize_to_expr_maybe_alpha(alpha)))
- }
- Value::NEListLit(elts) => rc(ExprF::NEListLit(
- elts.into_iter()
- .map(|n| n.normalize_to_expr_maybe_alpha(alpha))
- .collect(),
- )),
- Value::RecordLit(kvs) => rc(ExprF::RecordLit(
- kvs.iter()
- .map(|(k, v)| {
- (k.clone(), v.normalize_to_expr_maybe_alpha(alpha))
- })
- .collect(),
- )),
- Value::RecordType(kts) => rc(ExprF::RecordType(
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.normalize_to_expr_maybe_alpha(alpha))
- })
- .collect(),
- )),
- Value::UnionType(kts) => rc(ExprF::UnionType(
- kts.iter()
- .map(|(k, v)| {
- (
- k.clone(),
- v.as_ref().map(|v| {
- v.normalize_to_expr_maybe_alpha(alpha)
- }),
- )
- })
- .collect(),
- )),
- Value::UnionConstructor(l, kts) => {
- let kts = kts
- .iter()
- .map(|(k, v)| {
- (
- k.clone(),
- v.as_ref().map(|v| {
- v.normalize_to_expr_maybe_alpha(alpha)
- }),
- )
- })
- .collect();
- rc(ExprF::Field(rc(ExprF::UnionType(kts)), l.clone()))
- }
- Value::UnionLit(l, v, kts) => rc(ExprF::UnionLit(
- l.clone(),
- v.normalize_to_expr_maybe_alpha(alpha),
- kts.iter()
- .map(|(k, v)| {
- (
- k.clone(),
- v.as_ref().map(|v| {
- v.normalize_to_expr_maybe_alpha(alpha)
- }),
- )
- })
- .collect(),
- )),
- Value::TextLit(elts) => {
- use InterpolatedTextContents::{Expr, Text};
- rc(ExprF::TextLit(
- elts.iter()
- .map(|contents| match contents {
- Expr(e) => {
- Expr(e.normalize_to_expr_maybe_alpha(alpha))
- }
- Text(s) => Text(s.clone()),
- })
- .collect(),
- ))
- }
- Value::PartialExpr(e) => {
- rc(e.map_ref_simple(|v| v.normalize_to_expr_maybe_alpha(alpha)))
- }
- }
- }
-
- // Deprecated
- fn normalize(&self) -> Value {
- let mut v = self.clone();
- v.normalize_mut();
- v
- }
-
- pub(crate) fn normalize_mut(&mut self) {
- match self {
- Value::NaturalSuccClosure
- | Value::Var(_)
- | Value::Const(_)
- | Value::BoolLit(_)
- | Value::NaturalLit(_)
- | Value::IntegerLit(_) => {}
-
- Value::EmptyOptionalLit(tth)
- | Value::OptionalSomeClosure(tth)
- | Value::EmptyListLit(tth) => {
- tth.normalize_mut();
- }
-
- Value::NEOptionalLit(th) => {
- th.normalize_mut();
- }
- Value::Lam(_, t, e) => {
- t.normalize_mut();
- e.normalize_mut();
- }
- Value::Pi(_, t, e) => {
- t.normalize_mut();
- e.normalize_mut();
- }
- Value::AppliedBuiltin(_, args) => {
- for x in args.iter_mut() {
- x.normalize_mut();
- }
- }
- Value::ListConsClosure(t, v) => {
- t.normalize_mut();
- for x in v.iter_mut() {
- x.normalize_mut();
- }
- }
- Value::NEListLit(elts) => {
- for x in elts.iter_mut() {
- x.normalize_mut();
- }
- }
- Value::RecordLit(kvs) => {
- for x in kvs.values_mut() {
- x.normalize_mut();
- }
- }
- Value::RecordType(kvs) => {
- for x in kvs.values_mut() {
- x.normalize_mut();
- }
- }
- Value::UnionType(kts) | Value::UnionConstructor(_, kts) => {
- for x in kts.values_mut().flat_map(|opt| opt) {
- x.normalize_mut();
- }
- }
- Value::UnionLit(_, v, kts) => {
- v.normalize_mut();
- for x in kts.values_mut().flat_map(|opt| opt) {
- x.normalize_mut();
- }
- }
- Value::TextLit(elts) => {
- for x in elts.iter_mut() {
- use InterpolatedTextContents::{Expr, Text};
- match x {
- Expr(n) => n.normalize_mut(),
- Text(_) => {}
- }
- }
- }
- Value::PartialExpr(e) => {
- // TODO: need map_mut_simple
- e.map_ref_simple(|v| {
- v.normalize_nf();
- });
- }
- }
- }
-
- /// Apply to a value
- pub(crate) fn app(self, val: Value) -> Value {
- self.app_val(val)
- }
-
- /// Apply to a value
- pub(crate) fn app_val(self, val: Value) -> Value {
- self.app_thunk(val.into_thunk())
- }
-
- /// Apply to a thunk
- pub(crate) fn app_thunk(self, th: Thunk) -> Value {
- Thunk::from_value(self).app_thunk(th)
- }
-
- pub(crate) fn from_builtin(b: Builtin) -> Value {
- Value::AppliedBuiltin(b, vec![])
- }
-
- fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- match self {
- Value::Lam(x, t, e) => Value::Lam(
- x.clone(),
- t.shift(delta, var),
- e.shift(delta, &var.shift(1, &x.into())),
- ),
- Value::AppliedBuiltin(b, args) => Value::AppliedBuiltin(
- *b,
- args.iter().map(|v| v.shift(delta, var)).collect(),
- ),
- Value::NaturalSuccClosure => Value::NaturalSuccClosure,
- Value::OptionalSomeClosure(tth) => {
- Value::OptionalSomeClosure(tth.shift(delta, var))
- }
- Value::ListConsClosure(t, v) => Value::ListConsClosure(
- t.shift(delta, var),
- v.as_ref().map(|v| v.shift(delta, var)),
- ),
- Value::Pi(x, t, e) => Value::Pi(
- x.clone(),
- t.shift(delta, var),
- e.shift(delta, &var.shift(1, &x.into())),
- ),
- Value::Var(v) => Value::Var(v.shift(delta, var)),
- Value::Const(c) => Value::Const(*c),
- Value::BoolLit(b) => Value::BoolLit(*b),
- Value::NaturalLit(n) => Value::NaturalLit(*n),
- Value::IntegerLit(n) => Value::IntegerLit(*n),
- Value::EmptyOptionalLit(tth) => {
- Value::EmptyOptionalLit(tth.shift(delta, var))
- }
- Value::NEOptionalLit(th) => {
- Value::NEOptionalLit(th.shift(delta, var))
- }
- Value::EmptyListLit(tth) => {
- Value::EmptyListLit(tth.shift(delta, var))
- }
- Value::NEListLit(elts) => Value::NEListLit(
- elts.iter().map(|v| v.shift(delta, var)).collect(),
- ),
- Value::RecordLit(kvs) => Value::RecordLit(
- kvs.iter()
- .map(|(k, v)| (k.clone(), v.shift(delta, var)))
- .collect(),
- ),
- Value::RecordType(kvs) => Value::RecordType(
- kvs.iter()
- .map(|(k, v)| (k.clone(), v.shift(delta, var)))
- .collect(),
- ),
- Value::UnionType(kts) => Value::UnionType(
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.as_ref().map(|v| v.shift(delta, var)))
- })
- .collect(),
- ),
- Value::UnionConstructor(x, kts) => Value::UnionConstructor(
- x.clone(),
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.as_ref().map(|v| v.shift(delta, var)))
- })
- .collect(),
- ),
- Value::UnionLit(x, v, kts) => Value::UnionLit(
- x.clone(),
- v.shift(delta, var),
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.as_ref().map(|v| v.shift(delta, var)))
- })
- .collect(),
- ),
- Value::TextLit(elts) => Value::TextLit(
- elts.iter()
- .map(|contents| {
- use InterpolatedTextContents::{Expr, Text};
- match contents {
- Expr(th) => Expr(th.shift(delta, var)),
- Text(s) => Text(s.clone()),
- }
- })
- .collect(),
- ),
- Value::PartialExpr(e) => {
- Value::PartialExpr(e.map_ref_with_special_handling_of_binders(
- |v| v.shift(delta, var),
- |x, v| v.shift(delta, &var.shift(1, &x.into())),
- X::clone,
- Label::clone,
- ))
- }
- }
- }
-
- pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- match self {
- // Retry normalizing since substituting may allow progress
- Value::AppliedBuiltin(b, args) => apply_builtin(
- *b,
- args.iter().map(|v| v.subst_shift(var, val)).collect(),
- ),
- // Retry normalizing since substituting may allow progress
- Value::PartialExpr(e) => {
- normalize_one_layer(e.map_ref_with_special_handling_of_binders(
- |v| v.subst_shift(var, val),
- |x, v| {
- v.subst_shift(
- &var.shift(1, &x.into()),
- &val.shift(1, &x.into()),
- )
- },
- X::clone,
- Label::clone,
- ))
- }
- // Retry normalizing since substituting may allow progress
- Value::TextLit(elts) => {
- Value::TextLit(squash_textlit(elts.iter().map(|contents| {
- use InterpolatedTextContents::{Expr, Text};
- match contents {
- Expr(th) => Expr(th.subst_shift(var, val)),
- Text(s) => Text(s.clone()),
- }
- })))
- }
- Value::Lam(x, t, e) => Value::Lam(
- x.clone(),
- t.subst_shift(var, val),
- e.subst_shift(
- &var.shift(1, &x.into()),
- &val.shift(1, &x.into()),
- ),
- ),
- Value::NaturalSuccClosure => Value::NaturalSuccClosure,
- Value::OptionalSomeClosure(tth) => {
- Value::OptionalSomeClosure(tth.subst_shift(var, val))
- }
- Value::ListConsClosure(t, v) => Value::ListConsClosure(
- t.subst_shift(var, val),
- v.as_ref().map(|v| v.subst_shift(var, val)),
- ),
- Value::Pi(x, t, e) => Value::Pi(
- x.clone(),
- t.subst_shift(var, val),
- e.subst_shift(
- &var.shift(1, &x.into()),
- &val.shift(1, &x.into()),
- ),
- ),
- Value::Var(v) if v == var => val.to_value().clone(),
- Value::Var(v) => Value::Var(v.shift(-1, var)),
- Value::Const(c) => Value::Const(*c),
- Value::BoolLit(b) => Value::BoolLit(*b),
- Value::NaturalLit(n) => Value::NaturalLit(*n),
- Value::IntegerLit(n) => Value::IntegerLit(*n),
- Value::EmptyOptionalLit(tth) => {
- Value::EmptyOptionalLit(tth.subst_shift(var, val))
- }
- Value::NEOptionalLit(th) => {
- Value::NEOptionalLit(th.subst_shift(var, val))
- }
- Value::EmptyListLit(tth) => {
- Value::EmptyListLit(tth.subst_shift(var, val))
- }
- Value::NEListLit(elts) => Value::NEListLit(
- elts.iter().map(|v| v.subst_shift(var, val)).collect(),
- ),
- Value::RecordLit(kvs) => Value::RecordLit(
- kvs.iter()
- .map(|(k, v)| (k.clone(), v.subst_shift(var, val)))
- .collect(),
- ),
- Value::RecordType(kvs) => Value::RecordType(
- kvs.iter()
- .map(|(k, v)| (k.clone(), v.subst_shift(var, val)))
- .collect(),
- ),
- Value::UnionType(kts) => Value::UnionType(
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val)))
- })
- .collect(),
- ),
- Value::UnionConstructor(x, kts) => Value::UnionConstructor(
- x.clone(),
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val)))
- })
- .collect(),
- ),
- Value::UnionLit(x, v, kts) => Value::UnionLit(
- x.clone(),
- v.subst_shift(var, val),
- kts.iter()
- .map(|(k, v)| {
- (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val)))
- })
- .collect(),
- ),
- }
- }
-}
-
-mod thunk {
- use super::{
- apply_any, normalize_whnf, AlphaVar, InputSubExpr,
- NormalizationContext, OutputSubExpr, Value,
- };
- use crate::expr::Typed;
- use std::cell::{Ref, RefCell};
- use std::rc::Rc;
-
- #[derive(Debug, Clone, Copy)]
- enum Marker {
- /// Weak Head Normal Form, i.e. subexpressions may not be normalized
- WHNF,
- /// Normal form, i.e. completely normalized
- NF,
- }
- use Marker::{NF, WHNF};
-
- #[derive(Debug)]
- enum ThunkInternal {
- /// Non-normalized value alongside a normalization context
- Unnormalized(NormalizationContext, InputSubExpr),
- /// Partially normalized value.
- /// Invariant: if the marker is `NF`, the value must be fully normalized
- Value(Marker, Value),
- }
-
- /// Stores a possibl unevaluated value. Uses RefCell to ensure that
- /// the value gets normalized at most once.
- #[derive(Debug, Clone)]
- pub struct Thunk(Rc<RefCell<ThunkInternal>>);
-
- impl ThunkInternal {
- fn into_thunk(self) -> Thunk {
- Thunk(Rc::new(RefCell::new(self)))
- }
-
- fn normalize_whnf(&mut self) {
- match self {
- ThunkInternal::Unnormalized(ctx, e) => {
- *self = ThunkInternal::Value(
- WHNF,
- normalize_whnf(ctx.clone(), e.clone()),
- )
- }
- // Already at least in WHNF
- ThunkInternal::Value(_, _) => {}
- }
- }
-
- fn normalize_nf(&mut self) {
- match self {
- ThunkInternal::Unnormalized(_, _) => {
- self.normalize_whnf();
- self.normalize_nf();
- }
- ThunkInternal::Value(m @ WHNF, v) => {
- v.normalize_mut();
- *m = NF;
- }
- // Already in NF
- ThunkInternal::Value(NF, _) => {}
- }
- }
-
- // Always use normalize_whnf before
- fn as_whnf(&self) -> &Value {
- match self {
- ThunkInternal::Unnormalized(_, _) => unreachable!(),
- ThunkInternal::Value(_, v) => v,
- }
- }
-
- // Always use normalize_nf before
- fn as_nf(&self) -> &Value {
- match self {
- ThunkInternal::Unnormalized(_, _) => unreachable!(),
- ThunkInternal::Value(WHNF, _) => unreachable!(),
- ThunkInternal::Value(NF, v) => v,
- }
- }
-
- fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- match self {
- ThunkInternal::Unnormalized(ctx, e) => {
- ThunkInternal::Unnormalized(
- ctx.shift(delta, var),
- e.clone(),
- )
- }
- ThunkInternal::Value(m, v) => {
- ThunkInternal::Value(*m, v.shift(delta, var))
- }
- }
- }
-
- fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- match self {
- ThunkInternal::Unnormalized(ctx, e) => {
- ThunkInternal::Unnormalized(
- ctx.subst_shift(var, val),
- e.clone(),
- )
- }
- ThunkInternal::Value(_, v) => {
- // The resulting value may not stay in normal form after substitution
- ThunkInternal::Value(WHNF, v.subst_shift(var, val))
- }
- }
- }
- }
-
- impl Thunk {
- pub(crate) fn new(ctx: NormalizationContext, e: InputSubExpr) -> Thunk {
- ThunkInternal::Unnormalized(ctx, e).into_thunk()
- }
-
- pub(crate) fn from_value(v: Value) -> Thunk {
- ThunkInternal::Value(WHNF, v).into_thunk()
- }
-
- pub(crate) fn from_normalized_expr(e: OutputSubExpr) -> Thunk {
- Thunk::new(
- NormalizationContext::new(),
- e.embed_absurd().note_absurd(),
- )
- }
-
- // Normalizes contents to normal form; faster than `normalize_nf` if
- // no one else shares this thunk
- pub(crate) fn normalize_mut(&mut self) {
- match Rc::get_mut(&mut self.0) {
- // Mutate directly if sole owner
- Some(refcell) => RefCell::get_mut(refcell).normalize_nf(),
- // Otherwise mutate through the refcell
- None => self.0.borrow_mut().normalize_nf(),
- }
- }
-
- fn do_normalize_whnf(&self) {
- let borrow = self.0.borrow();
- match &*borrow {
- ThunkInternal::Unnormalized(_, _) => {
- drop(borrow);
- self.0.borrow_mut().normalize_whnf();
- }
- // Already at least in WHNF
- ThunkInternal::Value(_, _) => {}
- }
- }
-
- fn do_normalize_nf(&self) {
- let borrow = self.0.borrow();
- match &*borrow {
- ThunkInternal::Unnormalized(_, _)
- | ThunkInternal::Value(WHNF, _) => {
- drop(borrow);
- self.0.borrow_mut().normalize_nf();
- }
- // Already in NF
- ThunkInternal::Value(NF, _) => {}
- }
- }
-
- // WARNING: avoid normalizing any thunk while holding on to this ref
- // or you could run into BorrowMut panics
- pub(crate) fn normalize_nf(&self) -> Ref<Value> {
- self.do_normalize_nf();
- Ref::map(self.0.borrow(), ThunkInternal::as_nf)
- }
-
- // WARNING: avoid normalizing any thunk while holding on to this ref
- // or you could run into BorrowMut panics
- pub(crate) fn as_value(&self) -> Ref<Value> {
- self.do_normalize_whnf();
- Ref::map(self.0.borrow(), ThunkInternal::as_whnf)
- }
-
- pub(crate) fn to_value(&self) -> Value {
- self.as_value().clone()
- }
-
- pub(crate) fn normalize_to_expr(&self) -> OutputSubExpr {
- self.normalize_to_expr_maybe_alpha(false)
- }
-
- pub(crate) fn normalize_to_expr_maybe_alpha(
- &self,
- alpha: bool,
- ) -> OutputSubExpr {
- self.normalize_nf().normalize_to_expr_maybe_alpha(alpha)
- }
-
- pub(crate) fn app_val(&self, val: Value) -> Value {
- self.app_thunk(val.into_thunk())
- }
-
- pub(crate) fn app_thunk(&self, th: Thunk) -> Value {
- apply_any(self.clone(), th)
- }
-
- pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- self.0.borrow().shift(delta, var).into_thunk()
- }
-
- pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- self.0.borrow().subst_shift(var, val).into_thunk()
- }
- }
-
- impl std::cmp::PartialEq for Thunk {
- fn eq(&self, other: &Self) -> bool {
- &*self.as_value() == &*other.as_value()
- }
- }
- impl std::cmp::Eq for Thunk {}
-}
-
-pub(crate) use thunk::Thunk;
-
-/// A thunk in type position.
-#[derive(Debug, Clone)]
-pub(crate) enum TypeThunk {
- Thunk(Thunk),
- Type(Type),
-}
-
-impl TypeThunk {
- fn from_value(v: Value) -> TypeThunk {
- TypeThunk::from_thunk(Thunk::from_value(v))
- }
-
- fn from_thunk(th: Thunk) -> TypeThunk {
- TypeThunk::Thunk(th)
- }
-
- pub(crate) fn from_type(t: Type) -> TypeThunk {
- TypeThunk::Type(t)
- }
-
- fn normalize_mut(&mut self) {
- match self {
- TypeThunk::Thunk(th) => th.normalize_mut(),
- TypeThunk::Type(_) => {}
- }
- }
-
- pub(crate) fn normalize_nf(&self) -> Value {
- match self {
- TypeThunk::Thunk(th) => th.normalize_nf().clone(),
- TypeThunk::Type(t) => t.to_value().normalize(),
- }
- }
-
- pub(crate) fn to_value(&self) -> Value {
- match self {
- TypeThunk::Thunk(th) => th.to_value(),
- TypeThunk::Type(t) => t.to_value(),
- }
- }
-
- pub(crate) fn normalize_to_expr_maybe_alpha(
- &self,
- alpha: bool,
- ) -> OutputSubExpr {
- self.normalize_nf().normalize_to_expr_maybe_alpha(alpha)
- }
-
- fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- match self {
- TypeThunk::Thunk(th) => TypeThunk::Thunk(th.shift(delta, var)),
- TypeThunk::Type(t) => TypeThunk::Type(t.shift(delta, var)),
- }
- }
-
- pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- match self {
- TypeThunk::Thunk(th) => TypeThunk::Thunk(th.subst_shift(var, val)),
- TypeThunk::Type(t) => TypeThunk::Type(t.subst_shift(var, val)),
- }
- }
-}
-
-impl std::cmp::PartialEq for TypeThunk {
- fn eq(&self, other: &Self) -> bool {
- self.to_value() == other.to_value()
- }
-}
-impl std::cmp::Eq for TypeThunk {}
-
-fn apply_builtin(b: Builtin, args: Vec<Thunk>) -> Value {
+pub(crate) fn apply_builtin(b: Builtin, args: Vec<Thunk>) -> Value {
use dhall_syntax::Builtin::*;
use Value::*;
@@ -1207,7 +198,7 @@ fn apply_builtin(b: Builtin, args: Vec<Thunk>) -> Value {
}
}
-fn apply_any(f: Thunk, a: Thunk) -> Value {
+pub(crate) fn apply_any(f: Thunk, a: Thunk) -> Value {
let fallback = |f: Thunk, a: Thunk| Value::PartialExpr(ExprF::App(f, a));
let f_borrow = f.as_value();
@@ -1263,7 +254,7 @@ fn apply_any(f: Thunk, a: Thunk) -> Value {
}
}
-fn squash_textlit(
+pub(crate) fn squash_textlit(
elts: impl Iterator<Item = InterpolatedTextContents<Thunk>>,
) -> Vec<InterpolatedTextContents<Thunk>> {
use std::mem::replace;
@@ -1306,7 +297,10 @@ fn squash_textlit(
}
/// Reduces the imput expression to a Value. Evaluates as little as possible.
-fn normalize_whnf(ctx: NormalizationContext, expr: InputSubExpr) -> Value {
+pub(crate) fn normalize_whnf(
+ ctx: NormalizationContext,
+ expr: InputSubExpr,
+) -> Value {
match expr.as_ref() {
ExprF::Embed(e) => return e.to_value(),
ExprF::Var(v) => return ctx.lookup(v),
@@ -1325,7 +319,7 @@ fn normalize_whnf(ctx: NormalizationContext, expr: InputSubExpr) -> Value {
normalize_one_layer(expr)
}
-fn normalize_one_layer(expr: ExprF<Thunk, Label, X>) -> Value {
+pub(crate) fn normalize_one_layer(expr: ExprF<Thunk, Label, X>) -> Value {
use Value::{
BoolLit, EmptyListLit, EmptyOptionalLit, IntegerLit, Lam, NEListLit,
NEOptionalLit, NaturalLit, Pi, RecordLit, RecordType, TextLit,
@@ -1345,7 +339,9 @@ fn normalize_one_layer(expr: ExprF<Thunk, Label, X>) -> Value {
ExprF::Embed(_) => unreachable!(),
ExprF::Var(_) => unreachable!(),
ExprF::Annot(x, _) => RetThunk(x),
- ExprF::Lam(x, t, e) => RetValue(Lam(x.into(), t, e)),
+ ExprF::Lam(x, t, e) => {
+ RetValue(Lam(x.into(), TypeThunk::from_thunk(t), e))
+ }
ExprF::Pi(x, t, e) => RetValue(Pi(
x.into(),
TypeThunk::from_thunk(t),
diff --git a/dhall/src/phase/parse.rs b/dhall/src/phase/parse.rs
new file mode 100644
index 0000000..6b426af
--- /dev/null
+++ b/dhall/src/phase/parse.rs
@@ -0,0 +1,38 @@
+use std::fs::File;
+use std::io::Read;
+use std::path::Path;
+
+use dhall_syntax::parse_expr;
+
+use crate::error::Error;
+use crate::phase::resolve::ImportRoot;
+use crate::phase::Parsed;
+
+pub(crate) fn parse_file(f: &Path) -> Result<Parsed, Error> {
+ let mut buffer = String::new();
+ File::open(f)?.read_to_string(&mut buffer)?;
+ let expr = parse_expr(&*buffer)?;
+ let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned());
+ Ok(Parsed(expr, root))
+}
+
+pub(crate) fn parse_str(s: &str) -> Result<Parsed, Error> {
+ let expr = parse_expr(s)?;
+ let root = ImportRoot::LocalDir(std::env::current_dir()?);
+ Ok(Parsed(expr, root))
+}
+
+pub(crate) fn parse_binary_file(f: &Path) -> Result<Parsed, Error> {
+ let mut buffer = Vec::new();
+ File::open(f)?.read_to_end(&mut buffer)?;
+ let expr = crate::phase::binary::decode(&buffer)?;
+ let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned());
+ Ok(Parsed(expr.note_absurd(), root))
+}
+
+#[cfg(test)]
+mod spec_tests {
+ #![rustfmt::skip]
+ // See build.rs
+ include!(concat!(env!("OUT_DIR"), "/parser_tests.rs"));
+}
diff --git a/dhall/src/imports.rs b/dhall/src/phase/resolve.rs
index 87642a2..5ab03ac 100644
--- a/dhall/src/imports.rs
+++ b/dhall/src/phase/resolve.rs
@@ -1,18 +1,10 @@
-use crate::error::Error;
-use crate::expr::*;
-use dhall_syntax::*;
use std::collections::HashMap;
-use std::fs::File;
-use std::io::Read;
-use std::path::Path;
-use std::path::PathBuf;
-
-#[derive(Debug)]
-pub enum ImportError {
- Recursive(Import, Box<Error>),
- UnexpectedImport(Import),
- ImportCycle(ImportStack, Import),
-}
+use std::path::{Path, PathBuf};
+
+use dhall_syntax::Import;
+
+use crate::error::{Error, ImportError};
+use crate::phase::{Normalized, Parsed, Resolved};
/// A root from which to resolve relative imports.
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -22,7 +14,7 @@ pub enum ImportRoot {
type ImportCache = HashMap<Import, Normalized>;
-type ImportStack = Vec<Import>;
+pub(crate) type ImportStack = Vec<Import>;
fn resolve_import(
import: &Import,
@@ -97,7 +89,11 @@ fn do_resolve_expr(
Ok(Resolved(expr))
}
-fn skip_resolve_expr(
+pub(crate) fn resolve(e: Parsed) -> Result<Resolved, ImportError> {
+ do_resolve_expr(e, &mut HashMap::new(), &Vec::new())
+}
+
+pub(crate) fn skip_resolve_expr(
Parsed(expr, _root): Parsed,
) -> Result<Resolved, ImportError> {
let resolve = |import: &Import| -> Result<Normalized, ImportError> {
@@ -107,40 +103,6 @@ fn skip_resolve_expr(
Ok(Resolved(expr))
}
-impl Parsed {
- pub fn parse_file(f: &Path) -> Result<Parsed, Error> {
- let mut buffer = String::new();
- File::open(f)?.read_to_string(&mut buffer)?;
- let expr = parse_expr(&*buffer)?;
- let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned());
- Ok(Parsed(expr.unnote().note_absurd(), root))
- }
-
- pub fn parse_str(s: &str) -> Result<Parsed, Error> {
- let expr = parse_expr(s)?;
- let root = ImportRoot::LocalDir(std::env::current_dir()?);
- Ok(Parsed(expr, root))
- }
-
- #[allow(dead_code)]
- pub fn parse_binary_file(f: &Path) -> Result<Parsed, Error> {
- let mut buffer = Vec::new();
- File::open(f)?.read_to_end(&mut buffer)?;
- let expr = crate::binary::decode(&buffer)?;
- let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned());
- Ok(Parsed(expr.note_absurd(), root))
- }
-
- pub fn resolve(self) -> Result<Resolved, ImportError> {
- crate::imports::do_resolve_expr(self, &mut HashMap::new(), &Vec::new())
- }
-
- #[allow(dead_code)]
- pub fn skip_resolve(self) -> Result<Resolved, ImportError> {
- crate::imports::skip_resolve_expr(self)
- }
-}
-
#[cfg(test)]
mod spec_tests {
#![rustfmt::skip]
diff --git a/dhall/src/typecheck.rs b/dhall/src/phase/typecheck.rs
index 8d6b6eb..32c1531 100644
--- a/dhall/src/typecheck.rs
+++ b/dhall/src/phase/typecheck.rs
@@ -1,361 +1,18 @@
#![allow(non_snake_case)]
use std::borrow::Borrow;
-use std::borrow::Cow;
use std::collections::BTreeMap;
-use std::fmt;
-use crate::expr::*;
-use crate::normalize::{
- AlphaVar, NormalizationContext, Thunk, TypeThunk, Value,
-};
-use crate::traits::DynamicType;
use dhall_proc_macros as dhall;
-use dhall_syntax;
-use dhall_syntax::context::Context;
-use dhall_syntax::*;
-
-use self::TypeMessage::*;
-
-impl Resolved {
- pub fn typecheck(self) -> Result<Typed, TypeError> {
- type_of(self.0)
- }
- pub fn typecheck_with(self, ty: &Type) -> Result<Typed, TypeError> {
- let expr: SubExpr<_, _> = self.0;
- let ty: SubExpr<_, _> = ty.to_expr().embed_absurd().note_absurd();
- type_of(expr.rewrap(ExprF::Annot(expr.clone(), ty)))
- }
- /// Pretends this expression has been typechecked. Use with care.
- #[allow(dead_code)]
- pub fn skip_typecheck(self) -> Typed {
- Typed::from_thunk_untyped(Thunk::new(
- NormalizationContext::new(),
- self.0,
- ))
- }
-}
-
-impl Typed {
- fn to_type(&self) -> Type {
- match &self.to_value() {
- Value::Const(c) => Type(TypeInternal::Const(*c)),
- _ => Type(TypeInternal::Typed(Box::new(self.clone()))),
- }
- }
-}
-
-impl Normalized {
- fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- Normalized(self.0.shift(delta, var))
- }
- pub(crate) fn to_type(self) -> Type {
- self.0.to_type()
- }
-}
-
-impl Type {
- pub(crate) fn to_normalized(&self) -> Normalized {
- self.0.to_normalized()
- }
- pub(crate) fn to_expr(&self) -> SubExpr<X, X> {
- self.0.to_expr()
- }
- pub(crate) fn to_value(&self) -> Value {
- self.0.to_value()
- }
- pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> {
- self.0.get_type()
- }
- fn as_const(&self) -> Option<Const> {
- self.0.as_const()
- }
- fn internal_whnf(&self) -> Option<Value> {
- self.0.whnf()
- }
- pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- Type(self.0.shift(delta, var))
- }
- pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- Type(self.0.subst_shift(var, val))
- }
-
- fn const_sort() -> Self {
- Type(TypeInternal::Const(Const::Sort))
- }
- fn const_kind() -> Self {
- Type(TypeInternal::Const(Const::Kind))
- }
- pub(crate) fn const_type() -> Self {
- Type(TypeInternal::Const(Const::Type))
- }
-}
-
-impl TypeThunk {
- fn to_type(&self, ctx: &TypecheckContext) -> Result<Type, TypeError> {
- match self {
- TypeThunk::Type(t) => Ok(t.clone()),
- TypeThunk::Thunk(th) => {
- // TODO: rule out statically
- mktype(ctx, th.normalize_to_expr().embed_absurd().note_absurd())
- }
- }
- }
-}
-
-/// A semantic type. This is partially redundant with `dhall_syntax::Expr`, on purpose. `TypeInternal` should
-/// be limited to syntactic expressions: either written by the user or meant to be printed.
-/// The rule is the following: we must _not_ construct values of type `Expr` while typechecking,
-/// but only construct `TypeInternal`s.
-#[derive(Debug, Clone)]
-pub(crate) enum TypeInternal {
- Const(Const),
- /// This must not contain a Const value.
- Typed(Box<Typed>),
-}
-
-impl TypeInternal {
- fn to_typed(&self) -> Typed {
- match self {
- TypeInternal::Typed(e) => e.as_ref().clone(),
- TypeInternal::Const(c) => const_to_typed(*c),
- }
- }
- fn to_normalized(&self) -> Normalized {
- self.to_typed().normalize()
- }
- fn to_expr(&self) -> SubExpr<X, X> {
- self.to_normalized().to_expr()
- }
- fn to_value(&self) -> Value {
- self.to_typed().to_value()
- }
- pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> {
- Ok(match self {
- TypeInternal::Typed(e) => e.get_type()?,
- TypeInternal::Const(c) => Cow::Owned(type_of_const(*c)?),
- })
- }
- fn as_const(&self) -> Option<Const> {
- match self {
- TypeInternal::Const(c) => Some(*c),
- _ => None,
- }
- }
- fn whnf(&self) -> Option<Value> {
- match self {
- TypeInternal::Typed(e) => Some(e.to_value()),
- _ => None,
- }
- }
- fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- use TypeInternal::*;
- match self {
- Typed(e) => Typed(Box::new(e.shift(delta, var))),
- Const(c) => Const(*c),
- }
- }
- fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self {
- use TypeInternal::*;
- match self {
- Typed(e) => Typed(Box::new(e.subst_shift(var, val))),
- Const(c) => Const(*c),
- }
- }
-}
-
-impl Eq for TypeInternal {}
-impl PartialEq for TypeInternal {
- fn eq(&self, other: &Self) -> bool {
- self.to_normalized() == other.to_normalized()
- }
-}
-
-#[derive(Debug, Clone)]
-pub(crate) enum EnvItem {
- Type(AlphaVar, Type),
- Value(Normalized),
-}
-
-impl EnvItem {
- fn shift(&self, delta: isize, var: &AlphaVar) -> Self {
- use EnvItem::*;
- match self {
- Type(v, e) => Type(v.shift(delta, var), e.shift(delta, var)),
- Value(e) => Value(e.shift(delta, var)),
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub(crate) struct TypecheckContext(pub(crate) Context<Label, EnvItem>);
-
-impl TypecheckContext {
- pub(crate) fn new() -> Self {
- TypecheckContext(Context::new())
- }
- pub(crate) fn insert_type(&self, x: &Label, t: Type) -> Self {
- TypecheckContext(
- self.0.map(|_, e| e.shift(1, &x.into())).insert(
- x.clone(),
- EnvItem::Type(x.into(), t.shift(1, &x.into())),
- ),
- )
- }
- pub(crate) fn insert_value(&self, x: &Label, t: Normalized) -> Self {
- TypecheckContext(self.0.insert(x.clone(), EnvItem::Value(t)))
- }
- pub(crate) fn lookup(&self, var: &V<Label>) -> Option<Cow<'_, Type>> {
- let V(x, n) = var;
- match self.0.lookup(x, *n) {
- Some(EnvItem::Type(_, t)) => Some(Cow::Borrowed(&t)),
- Some(EnvItem::Value(t)) => Some(t.get_type()?),
- None => None,
- }
- }
- fn to_normalization_ctx(&self) -> NormalizationContext {
- NormalizationContext::from_typecheck_ctx(self)
- }
-}
-
-impl PartialEq for TypecheckContext {
- fn eq(&self, _: &Self) -> bool {
- // don't count contexts when comparing stuff
- // this is dirty but needed for now
- true
- }
-}
-impl Eq for TypecheckContext {}
-
-fn function_check(a: Const, b: Const) -> Result<Const, ()> {
- use dhall_syntax::Const::*;
- match (a, b) {
- (_, Type) => Ok(Type),
- (Kind, Kind) => Ok(Kind),
- (Sort, Sort) => Ok(Sort),
- (Sort, Kind) => Ok(Sort),
- _ => Err(()),
- }
-}
-
-// Equality up to alpha-equivalence (renaming of bound variables)
-fn prop_equal<T, U>(eL0: T, eR0: U) -> bool
-where
- T: Borrow<Type>,
- U: Borrow<Type>,
-{
- eL0.borrow().to_value() == eR0.borrow().to_value()
-}
-
-fn const_to_typed(c: Const) -> Typed {
- match c {
- Const::Sort => Typed::const_sort(),
- _ => Typed::from_thunk_and_type(
- Value::Const(c).into_thunk(),
- type_of_const(c).unwrap(),
- ),
- }
-}
-
-fn const_to_type(c: Const) -> Type {
- Type(TypeInternal::Const(c))
-}
-
-fn type_of_const(c: Const) -> Result<Type, TypeError> {
- match c {
- Const::Type => Ok(Type::const_kind()),
- Const::Kind => Ok(Type::const_sort()),
- Const::Sort => {
- return Err(TypeError::new(
- &TypecheckContext::new(),
- TypeMessage::Sort,
- ))
- }
- }
-}
-
-fn type_of_builtin<E>(b: Builtin) -> Expr<X, E> {
- use dhall_syntax::Builtin::*;
- match b {
- Bool | Natural | Integer | Double | Text => dhall::expr!(Type),
- List | Optional => dhall::expr!(
- Type -> Type
- ),
-
- NaturalFold => dhall::expr!(
- Natural ->
- forall (natural: Type) ->
- forall (succ: natural -> natural) ->
- forall (zero: natural) ->
- natural
- ),
- NaturalBuild => dhall::expr!(
- (forall (natural: Type) ->
- forall (succ: natural -> natural) ->
- forall (zero: natural) ->
- natural) ->
- Natural
- ),
- NaturalIsZero | NaturalEven | NaturalOdd => dhall::expr!(
- Natural -> Bool
- ),
- NaturalToInteger => dhall::expr!(Natural -> Integer),
- NaturalShow => dhall::expr!(Natural -> Text),
-
- IntegerToDouble => dhall::expr!(Integer -> Double),
- IntegerShow => dhall::expr!(Integer -> Text),
- DoubleShow => dhall::expr!(Double -> Text),
- TextShow => dhall::expr!(Text -> Text),
-
- ListBuild => dhall::expr!(
- forall (a: Type) ->
- (forall (list: Type) ->
- forall (cons: a -> list -> list) ->
- forall (nil: list) ->
- list) ->
- List a
- ),
- ListFold => dhall::expr!(
- forall (a: Type) ->
- List a ->
- forall (list: Type) ->
- forall (cons: a -> list -> list) ->
- forall (nil: list) ->
- list
- ),
- ListLength => dhall::expr!(forall (a: Type) -> List a -> Natural),
- ListHead | ListLast => {
- dhall::expr!(forall (a: Type) -> List a -> Optional a)
- }
- ListIndexed => dhall::expr!(
- forall (a: Type) ->
- List a ->
- List { index: Natural, value: a }
- ),
- ListReverse => dhall::expr!(
- forall (a: Type) -> List a -> List a
- ),
+use dhall_syntax::{
+ rc, Builtin, Const, Expr, ExprF, InterpolatedTextContents, Label, Span,
+ SubExpr, X,
+};
- OptionalBuild => dhall::expr!(
- forall (a: Type) ->
- (forall (optional: Type) ->
- forall (just: a -> optional) ->
- forall (nothing: optional) ->
- optional) ->
- Optional a
- ),
- OptionalFold => dhall::expr!(
- forall (a: Type) ->
- Optional a ->
- forall (optional: Type) ->
- forall (just: a -> optional) ->
- forall (nothing: optional) ->
- optional
- ),
- OptionalNone => dhall::expr!(
- forall (a: Type) -> Optional a
- ),
- }
-}
+use crate::core::context::{NormalizationContext, TypecheckContext};
+use crate::core::thunk::{Thunk, TypeThunk};
+use crate::core::value::Value;
+use crate::error::{TypeError, TypeMessage};
+use crate::phase::{Normalized, Resolved, Type, Typed};
macro_rules! ensure_equal {
($x:expr, $y:expr, $err:expr $(,)*) => {
@@ -386,6 +43,7 @@ pub(crate) enum TypeIntermediate {
impl TypeIntermediate {
fn typecheck(self) -> Result<Typed, TypeError> {
+ use crate::error::TypeMessage::*;
let mkerr = |ctx, msg| TypeError::new(ctx, msg);
Ok(match &self {
TypeIntermediate::Pi(ctx, x, ta, tb) => {
@@ -439,7 +97,7 @@ impl TypeIntermediate {
TypeThunk::from_type(tb.clone()),
)
.into_thunk(),
- const_to_type(k),
+ Type::from_const(k),
)
}
TypeIntermediate::RecordType(ctx, kts) => {
@@ -469,7 +127,7 @@ impl TypeIntermediate {
.collect(),
)
.into_thunk(),
- const_to_type(k),
+ Type::from_const(k),
)
}
TypeIntermediate::UnionType(ctx, kts) => {
@@ -508,7 +166,7 @@ impl TypeIntermediate {
.collect(),
)
.into_thunk(),
- const_to_type(k),
+ Type::from_const(k),
)
}
TypeIntermediate::ListType(ctx, t) => {
@@ -520,7 +178,7 @@ impl TypeIntermediate {
Value::from_builtin(Builtin::List)
.app(t.to_value())
.into_thunk(),
- const_to_type(Const::Type),
+ Type::from_const(Const::Type),
)
}
TypeIntermediate::OptionalType(ctx, t) => {
@@ -532,23 +190,140 @@ impl TypeIntermediate {
Value::from_builtin(Builtin::Optional)
.app(t.to_value())
.into_thunk(),
- const_to_type(Const::Type),
+ Type::from_const(Const::Type),
)
}
})
}
}
+fn function_check(a: Const, b: Const) -> Result<Const, ()> {
+ use dhall_syntax::Const::*;
+ match (a, b) {
+ (_, Type) => Ok(Type),
+ (Kind, Kind) => Ok(Kind),
+ (Sort, Sort) => Ok(Sort),
+ (Sort, Kind) => Ok(Sort),
+ _ => Err(()),
+ }
+}
+
+// Equality up to alpha-equivalence (renaming of bound variables)
+fn prop_equal<T, U>(eL0: T, eR0: U) -> bool
+where
+ T: Borrow<Type>,
+ U: Borrow<Type>,
+{
+ eL0.borrow().to_value() == eR0.borrow().to_value()
+}
+
+pub(crate) fn type_of_const(c: Const) -> Result<Type, TypeError> {
+ match c {
+ Const::Type => Ok(Type::const_kind()),
+ Const::Kind => Ok(Type::const_sort()),
+ Const::Sort => {
+ return Err(TypeError::new(
+ &TypecheckContext::new(),
+ TypeMessage::Sort,
+ ))
+ }
+ }
+}
+
+fn type_of_builtin(b: Builtin) -> Expr<X, X> {
+ use dhall_syntax::Builtin::*;
+ match b {
+ Bool | Natural | Integer | Double | Text => dhall::expr!(Type),
+ List | Optional => dhall::expr!(
+ Type -> Type
+ ),
+
+ NaturalFold => dhall::expr!(
+ Natural ->
+ forall (natural: Type) ->
+ forall (succ: natural -> natural) ->
+ forall (zero: natural) ->
+ natural
+ ),
+ NaturalBuild => dhall::expr!(
+ (forall (natural: Type) ->
+ forall (succ: natural -> natural) ->
+ forall (zero: natural) ->
+ natural) ->
+ Natural
+ ),
+ NaturalIsZero | NaturalEven | NaturalOdd => dhall::expr!(
+ Natural -> Bool
+ ),
+ NaturalToInteger => dhall::expr!(Natural -> Integer),
+ NaturalShow => dhall::expr!(Natural -> Text),
+
+ IntegerToDouble => dhall::expr!(Integer -> Double),
+ IntegerShow => dhall::expr!(Integer -> Text),
+ DoubleShow => dhall::expr!(Double -> Text),
+ TextShow => dhall::expr!(Text -> Text),
+
+ ListBuild => dhall::expr!(
+ forall (a: Type) ->
+ (forall (list: Type) ->
+ forall (cons: a -> list -> list) ->
+ forall (nil: list) ->
+ list) ->
+ List a
+ ),
+ ListFold => dhall::expr!(
+ forall (a: Type) ->
+ List a ->
+ forall (list: Type) ->
+ forall (cons: a -> list -> list) ->
+ forall (nil: list) ->
+ list
+ ),
+ ListLength => dhall::expr!(forall (a: Type) -> List a -> Natural),
+ ListHead | ListLast => {
+ dhall::expr!(forall (a: Type) -> List a -> Optional a)
+ }
+ ListIndexed => dhall::expr!(
+ forall (a: Type) ->
+ List a ->
+ List { index: Natural, value: a }
+ ),
+ ListReverse => dhall::expr!(
+ forall (a: Type) -> List a -> List a
+ ),
+
+ OptionalBuild => dhall::expr!(
+ forall (a: Type) ->
+ (forall (optional: Type) ->
+ forall (just: a -> optional) ->
+ forall (nothing: optional) ->
+ optional) ->
+ Optional a
+ ),
+ OptionalFold => dhall::expr!(
+ forall (a: Type) ->
+ Optional a ->
+ forall (optional: Type) ->
+ forall (just: a -> optional) ->
+ forall (nothing: optional) ->
+ optional
+ ),
+ OptionalNone => dhall::expr!(
+ forall (a: Type) -> Optional a
+ ),
+ }
+}
+
/// Takes an expression that is meant to contain a Type
/// and turn it into a type, typechecking it along the way.
-fn mktype(
+pub(crate) fn mktype(
ctx: &TypecheckContext,
e: SubExpr<Span, Normalized>,
) -> Result<Type, TypeError> {
Ok(type_with(ctx, e)?.to_type())
}
-fn builtin_to_type(b: Builtin) -> Result<Type, TypeError> {
+pub(crate) fn builtin_to_type(b: Builtin) -> Result<Type, TypeError> {
mktype(
&TypecheckContext::new(),
SubExpr::from_expr_no_note(ExprF::Builtin(b)),
@@ -645,6 +420,7 @@ fn type_last_layer(
ctx: &TypecheckContext,
e: ExprF<Typed, Label, Normalized>,
) -> Result<Ret, TypeError> {
+ use crate::error::TypeMessage::*;
use dhall_syntax::BinOp::*;
use dhall_syntax::Builtin::*;
use dhall_syntax::ExprF::*;
@@ -857,9 +633,9 @@ fn type_last_layer(
// ))),
}
}
- Const(c) => Ok(RetTyped(const_to_typed(c))),
+ Const(c) => Ok(RetTyped(Typed::from_const(c))),
Builtin(b) => {
- Ok(RetType(mktype(ctx, rc(type_of_builtin(b)).note_absurd())?))
+ Ok(RetType(mktype(ctx, rc(type_of_builtin(b)).absurd())?))
}
BoolLit(_) => Ok(RetType(builtin_to_type(Bool)?)),
NaturalLit(_) => Ok(RetType(builtin_to_type(Natural)?)),
@@ -927,99 +703,20 @@ fn type_of(e: SubExpr<Span, Normalized>) -> Result<Typed, TypeError> {
Ok(e)
}
-/// The specific type error
-#[derive(Debug)]
-pub(crate) enum TypeMessage {
- UnboundVariable(V<Label>),
- InvalidInputType(Normalized),
- InvalidOutputType(Normalized),
- NotAFunction(Typed),
- TypeMismatch(Typed, Normalized, Typed),
- AnnotMismatch(Typed, Normalized),
- Untyped,
- InvalidListElement(usize, Normalized, Typed),
- InvalidListType(Normalized),
- InvalidOptionalType(Normalized),
- InvalidPredicate(Typed),
- IfBranchMismatch(Typed, Typed),
- IfBranchMustBeTerm(bool, Typed),
- InvalidFieldType(Label, Type),
- NotARecord(Label, Normalized),
- MissingRecordField(Label, Typed),
- MissingUnionField(Label, Normalized),
- BinOpTypeMismatch(BinOp, Typed),
- NoDependentTypes(Normalized, Normalized),
- InvalidTextInterpolation(Typed),
- Sort,
- Unimplemented,
-}
-
-/// A structured type error that includes context
-#[derive(Debug)]
-pub struct TypeError {
- type_message: TypeMessage,
- context: TypecheckContext,
-}
-
-impl TypeError {
- pub(crate) fn new(
- context: &TypecheckContext,
- type_message: TypeMessage,
- ) -> Self {
- TypeError {
- context: context.clone(),
- type_message,
- }
- }
-}
-
-impl From<TypeError> for std::option::NoneError {
- fn from(_: TypeError) -> std::option::NoneError {
- std::option::NoneError
- }
+pub(crate) fn typecheck(e: Resolved) -> Result<Typed, TypeError> {
+ type_of(e.0)
}
-impl ::std::error::Error for TypeMessage {
- fn description(&self) -> &str {
- match *self {
- // UnboundVariable => "Unbound variable",
- InvalidInputType(_) => "Invalid function input",
- InvalidOutputType(_) => "Invalid function output",
- NotAFunction(_) => "Not a function",
- TypeMismatch(_, _, _) => "Wrong type of function argument",
- _ => "Unhandled error",
- }
- }
+pub(crate) fn typecheck_with(
+ e: Resolved,
+ ty: &Type,
+) -> Result<Typed, TypeError> {
+ let expr: SubExpr<_, _> = e.0;
+ let ty: SubExpr<_, _> = ty.to_expr().absurd();
+ type_of(expr.rewrap(ExprF::Annot(expr.clone(), ty)))
}
-
-impl fmt::Display for TypeMessage {
- fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- match self {
- // UnboundVariable(_) => {
- // f.write_str(include_str!("errors/UnboundVariable.txt"))
- // }
- // TypeMismatch(e0, e1, e2) => {
- // let template = include_str!("errors/TypeMismatch.txt");
- // let s = template
- // .replace("$txt0", &format!("{}", e0.as_expr()))
- // .replace("$txt1", &format!("{}", e1.as_expr()))
- // .replace("$txt2", &format!("{}", e2.as_expr()))
- // .replace(
- // "$txt3",
- // &format!(
- // "{}",
- // e2.get_type()
- // .unwrap()
- // .as_normalized()
- // .unwrap()
- // .as_expr()
- // ),
- // );
- // f.write_str(&s)
- // }
- _ => f.write_str("Unhandled error message"),
- }
- }
+pub(crate) fn skip_typecheck(e: Resolved) -> Typed {
+ Typed::from_thunk_untyped(Thunk::new(NormalizationContext::new(), e.0))
}
#[cfg(test)]
diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs
index 6b2ab60..4fa1cf5 100644
--- a/dhall/src/tests.rs
+++ b/dhall/src/tests.rs
@@ -34,8 +34,7 @@ macro_rules! make_spec_test {
}
use crate::error::{Error, Result};
-use crate::expr::Parsed;
-use crate::traits::DynamicType;
+use crate::phase::Parsed;
use std::path::PathBuf;
#[derive(Copy, Clone)]
diff --git a/dhall/tests/traits.rs b/dhall/tests/traits.rs
index 6604c06..65220dc 100644
--- a/dhall/tests/traits.rs
+++ b/dhall/tests/traits.rs
@@ -5,7 +5,7 @@ use dhall_syntax::{SubExpr, X};
#[test]
fn test_static_type() {
- fn mktype(x: SubExpr<X, X>) -> dhall::expr::SimpleType {
+ fn mktype(x: SubExpr<X, X>) -> dhall::de::SimpleType {
x.into()
}