#![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(b: Builtin, args: Vec) -> WHNF { use dhall_core::Builtin::*; use WHNF::*; let ret = match b { OptionalNone => improved_slice_patterns::match_vec!(args; [t] => EmptyOptionalLit(Now::from_whnf(t)), ), NaturalIsZero => improved_slice_patterns::match_vec!(args; [NaturalLit(n)] => BoolLit(n == 0), ), NaturalEven => improved_slice_patterns::match_vec!(args; [NaturalLit(n)] => BoolLit(n % 2 == 0), ), NaturalOdd => improved_slice_patterns::match_vec!(args; [NaturalLit(n)] => BoolLit(n % 2 != 0), ), NaturalToInteger => improved_slice_patterns::match_vec!(args; [NaturalLit(n)] => IntegerLit(n as isize), ), NaturalShow => improved_slice_patterns::match_vec!(args; [NaturalLit(n)] => { TextLit(vec![InterpolatedTextContents::Text(n.to_string())]) } ), ListLength => improved_slice_patterns::match_vec!(args; [_, EmptyListLit(_)] => NaturalLit(0), [_, NEListLit(xs)] => NaturalLit(xs.len()), ), ListHead => improved_slice_patterns::match_vec!(args; [_, EmptyListLit(n)] => EmptyOptionalLit(n), [_, NEListLit(xs)] => { NEOptionalLit(xs.into_iter().next().unwrap()) } ), ListLast => improved_slice_patterns::match_vec!(args; [_, EmptyListLit(n)] => EmptyOptionalLit(n), [_, NEListLit(xs)] => { NEOptionalLit(xs.into_iter().rev().next().unwrap()) } ), ListReverse => improved_slice_patterns::match_vec!(args; [_, EmptyListLit(n)] => EmptyListLit(n), [_, NEListLit(xs)] => { let mut xs = xs; xs.reverse(); NEListLit(xs) } ), ListIndexed => improved_slice_patterns::match_vec!(args; [_, EmptyListLit(t)] => { let mut kts = BTreeMap::new(); kts.insert( "index".into(), Now::from_whnf( WHNF::from_builtin(Natural) ), ); kts.insert("value".into(), t); EmptyListLit(Now::from_whnf(RecordType(kts))) }, [_, NEListLit(xs)] => { let xs = xs .into_iter() .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) } ), ListBuild => improved_slice_patterns::match_vec!(args; // fold/build fusion [_, WHNF::AppliedBuiltin(ListFold, args)] => { let mut args = args; if args.len() >= 2 { args.remove(1) } else { // Do we really need to handle this case ? unimplemented!() } }, [t, g] => g .app(WHNF::from_builtin(List).app(t.clone())) .app(ListConsClosure(Now::from_whnf(t.clone()), None)) .app(EmptyListLit(Now::from_whnf(t))), ), ListFold => improved_slice_patterns::match_vec!(args; // fold/build fusion [_, WHNF::AppliedBuiltin(ListBuild, args)] => { let mut args = args; if args.len() >= 2 { args.remove(1) } else { unimplemented!() } }, [_, EmptyListLit(_), _, _, nil] => nil, [_, NEListLit(xs), _, cons, nil] => { let mut v = nil; for x in xs.into_iter().rev() { v = cons.clone().app(x.normalize()).app(v); } v } ), OptionalBuild => improved_slice_patterns::match_vec!(args; // fold/build fusion [_, WHNF::AppliedBuiltin(OptionalFold, args)] => { let mut args = args; if args.len() >= 2 { args.remove(1) } else { unimplemented!() } }, [t, g] => g .app(WHNF::from_builtin(Optional).app(t.clone())) .app(OptionalSomeClosure(Now::from_whnf(t.clone()))) .app(EmptyOptionalLit(Now::from_whnf(t))), ), OptionalFold => improved_slice_patterns::match_vec!(args; // fold/build fusion [_, WHNF::AppliedBuiltin(OptionalBuild, args)] => { let mut args = args; if args.len() >= 2 { args.remove(1) } else { unimplemented!() } }, [_, EmptyOptionalLit(_), _, _, nothing] => { nothing }, [_, NEOptionalLit(x), _, just, _] => { just.app(x.normalize()) } ), NaturalBuild => improved_slice_patterns::match_vec!(args; // fold/build fusion [WHNF::AppliedBuiltin(NaturalFold, args)] => { let mut args = args; if args.len() >= 1 { args.remove(0) } else { unimplemented!() } }, [g] => g .app(WHNF::from_builtin(Natural)) .app(NaturalSuccClosure) .app(NaturalLit(0)), ), NaturalFold => improved_slice_patterns::match_vec!(args; // fold/build fusion [WHNF::AppliedBuiltin(NaturalBuild, args)] => { let mut args = args; if args.len() >= 1 { args.remove(0) } else { unimplemented!() } }, [NaturalLit(0), _, _, zero] => zero, [NaturalLit(n), t, succ, zero] => { let fold = WHNF::from_builtin(NaturalFold) .app(NaturalLit(n - 1)) .app(t) .app(succ.clone()) .app(zero); succ.app(fold) }, ), _ => Err(args), }; match ret { Ok(x) => x, Err(args) => AppliedBuiltin(b, args), } } #[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