#![allow(non_snake_case)] use std::collections::BTreeMap; use std::rc::Rc; use dhall_core::context::Context; use dhall_core::{ rc, shift, shift0, Builtin, ExprF, Integer, InterpolatedText, InterpolatedTextContents, Label, Natural, SubExpr, V, X, }; use dhall_generator as dhall; use crate::expr::{Normalized, Typed}; type InputSubExpr = SubExpr>; type OutputSubExpr = SubExpr; impl<'a> Typed<'a> { pub fn normalize(self) -> Normalized<'a> { Normalized(normalize(self.0), self.1, self.2) } /// Pretends this expression is normalized. Use with care. #[allow(dead_code)] pub fn skip_normalize(self) -> Normalized<'a> { Normalized( self.0.unroll().squash_embed(|e| e.0.clone()), self.1, self.2, ) } } fn apply_builtin( ctx: NormalizationContext, b: Builtin, args: &[WHNF], ) -> Option { use dhall_core::Builtin::*; use WHNF::*; let ret = match (b, args) { (OptionalNone, [t]) => EmptyOptionalLit(Now::from_whnf(t.clone())), (NaturalIsZero, [NaturalLit(n)]) => BoolLit(*n == 0), (NaturalEven, [NaturalLit(n)]) => BoolLit(*n % 2 == 0), (NaturalOdd, [NaturalLit(n)]) => BoolLit(*n % 2 != 0), (NaturalToInteger, [NaturalLit(n)]) => IntegerLit(*n as isize), (NaturalShow, [NaturalLit(n)]) => { TextLit(vec![InterpolatedTextContents::Text(n.to_string())]) } (ListLength, [_, EmptyListLit(_)]) => NaturalLit(0), (ListLength, [_, NEListLit(xs)]) => NaturalLit(xs.len()), (ListHead, [_, EmptyListLit(n)]) => EmptyOptionalLit(n.clone()), (ListHead, [_, NEListLit(xs)]) => { NEOptionalLit(xs.first().unwrap().clone()) } (ListLast, [_, EmptyListLit(n)]) => EmptyOptionalLit(n.clone()), (ListLast, [_, NEListLit(xs)]) => { NEOptionalLit(xs.last().unwrap().clone()) } (ListReverse, [_, EmptyListLit(n)]) => EmptyListLit(n.clone()), (ListReverse, [_, NEListLit(xs)]) => { let xs = xs.iter().cloned().rev().collect(); NEListLit(xs) } (ListIndexed, [_, EmptyListLit(t)]) => { let mut kts = BTreeMap::new(); kts.insert( "index".into(), Now::from_whnf(AppliedBuiltin(ctx, Natural, vec![])), ); kts.insert("value".into(), t.clone()); EmptyListLit(Now::from_whnf(RecordType(kts))) } (ListIndexed, [_, NEListLit(xs)]) => { let xs = xs .iter() .cloned() .enumerate() .map(|(i, e)| { let i = NaturalLit(i); let mut kvs = BTreeMap::new(); kvs.insert("index".into(), Now::from_whnf(i)); kvs.insert("value".into(), e); Now::from_whnf(RecordLit(kvs)) }) .collect(); NEListLit(xs) } // fold/build fusion (ListBuild, [_, WHNF::AppliedBuiltin(_, ListFold, args)]) if args.len() == 2 => { args.get(1).unwrap().clone() } (ListFold, [_, WHNF::AppliedBuiltin(_, ListBuild, args)]) if args.len() == 2 => { args.get(1).unwrap().clone() } (ListBuild, [t, g]) => g .clone() .app(AppliedBuiltin(ctx.clone(), List, vec![]).app(t.clone())) .app(ListConsClosure(Now::from_whnf(t.clone()), None)) .app(EmptyListLit(Now::from_whnf(t.clone()))), (ListFold, [_, EmptyListLit(_), _, _, nil]) => nil.clone(), (ListFold, [_, NEListLit(xs), _, cons, nil]) => { let mut v = nil.clone(); for x in xs.iter().rev() { v = cons.clone().app(x.clone().normalize()).app(v); } v } // fold/build fusion (OptionalBuild, [_, WHNF::AppliedBuiltin(_, OptionalFold, args)]) if args.len() == 2 => { args.get(1).unwrap().clone() } (OptionalFold, [_, WHNF::AppliedBuiltin(_, OptionalBuild, args)]) if args.len() == 2 => { args.get(1).unwrap().clone() } (OptionalBuild, [t, g]) => g .clone() .app(AppliedBuiltin(ctx.clone(), Optional, vec![]).app(t.clone())) .app(OptionalSomeClosure(Now::from_whnf(t.clone()))) .app(EmptyOptionalLit(Now::from_whnf(t.clone()))), (OptionalFold, [_, EmptyOptionalLit(_), _, _, nothing]) => { nothing.clone() } (OptionalFold, [_, NEOptionalLit(x), _, just, _]) => { just.clone().app(x.clone().normalize()) } // fold/build fusion (NaturalBuild, [WHNF::AppliedBuiltin(_, NaturalFold, args)]) if args.len() == 1 => { args.get(0).unwrap().clone() } (NaturalFold, [WHNF::AppliedBuiltin(_, NaturalBuild, args)]) if args.len() == 1 => { args.get(0).unwrap().clone() } (NaturalBuild, [g]) => g .clone() .app(AppliedBuiltin(ctx.clone(), Natural, vec![])) .app(NaturalSuccClosure) .app(NaturalLit(0)), (NaturalFold, [NaturalLit(0), _, _, zero]) => zero.clone(), (NaturalFold, [NaturalLit(n), t, succ, zero]) => { let fold = AppliedBuiltin(ctx, NaturalFold, vec![]) .app(NaturalLit(n - 1)) .app(t.clone()) .app(succ.clone()) .app(zero.clone()); succ.clone().app(fold) } _ => return None, }; Some(ret) } #[derive(Debug, Clone)] enum EnvItem { Expr(WHNF), Skip(usize), } #[derive(Debug, Clone)] struct NormalizationContext(Rc>); impl NormalizationContext { fn new() -> Self { NormalizationContext(Rc::new(Context::new())) } fn insert(&self, x: &Label, e: WHNF) -> Self { NormalizationContext(Rc::new( self.0.insert(x.clone(), EnvItem::Expr(e)), )) } fn skip(&self, x: &Label) -> Self { NormalizationContext(Rc::new( self.0 .map(|l, e| { use EnvItem::*; match e { Expr(e) => Expr(e.clone().shift0(1, x)), Skip(n) if l == x => Skip(*n + 1), Skip(n) => Skip(*n), } }) .insert(x.clone(), EnvItem::Skip(0)), )) } fn lookup(&self, var: &V