summaryrefslogtreecommitdiff
path: root/dhall/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'dhall/src/core')
-rw-r--r--dhall/src/core/context.rs136
-rw-r--r--dhall/src/core/mod.rs3
-rw-r--r--dhall/src/core/thunk.rs295
-rw-r--r--dhall/src/core/value.rs616
4 files changed, 1050 insertions, 0 deletions
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..8775ce2
--- /dev/null
+++ b/dhall/src/core/thunk.rs
@@ -0,0 +1,295 @@
+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. Uses RefCell to ensure that
+/// the value gets normalized at most once.
+#[derive(Debug, Clone)]
+pub struct Thunk(Rc<RefCell<ThunkInternal>>);
+
+/// A thunk in type position.
+#[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.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 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().embed_absurd().note_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..31193d2
--- /dev/null
+++ b/dhall/src/core/value.rs
@@ -0,0 +1,616 @@
+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.
+/// Equality is up to alpha-equivalence (renaming of bound variables)
+/// and beta-equivalence (normalization).
+#[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 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
+ }
+}