packages feed

hoq-0.1.0.0: src/Normalization.hs

module Normalization
    ( NF(..), nf
    , nfType, nfScope
    ) where

import Control.Monad
import Data.Traversable

import Syntax.Term

data NF = NF | HNF | WHNF deriving Eq

nf :: Eq a => NF -> Term a -> Term a
nf mode e = go e []
  where
    go (App a b)          ts = go a (b:ts)
    go e@Var{}            ts = apps e (nfs mode ts)
    go e@Universe{}       _  = e
    go (Pi a b lvl)       _  | mode == NF = Pi (nfType NF a) (nfScope b) lvl
    go e@Pi{}             _  = e
    go e@Interval         _  = e
    go e@(ICon _)         _  = e
    go (PCon Nothing)     [] = PCon Nothing
    go (PCon Nothing)  (e:_) = PCon $ Just   $ if mode == NF then nf NF e else e
    go (PCon (Just e))    _  = PCon $ Just   $ if mode == NF then nf NF e else e
    go (Con c lc n [] es) [] = Con c lc n [] $ nfs mode es
    go (Con c lc n [] es) ts = Con c lc n [] $ nfs mode (es ++ ts)
    go (DataType d e es)  [] = DataType d e  $ nfs mode es
    go (DataType d e es)  ts = DataType d e  $ nfs mode (es ++ ts)
    go (Path h ma es)     [] = Path h (if mode == NF then fmap (nf NF) ma else ma) $ nfs mode es
    go (Path h ma es)     ts = Path h (if mode == NF then fmap (nf NF) ma else ma) $ nfs mode (es ++ ts)
    go (Lam (Scope1 v t)) [] = Lam $ Scope1 v $ if mode == WHNF then t else nf mode t
    go (Lam (Scope1 _ s)) (t:ts) = go (instantiate1 t s) ts
    go (FunSyn _ term)    ts = go term ts
    go (Con c lc n conds es) ts =
        let es' = if null ts then es else es ++ ts in
        case instantiateCases conds es' of
            Just (r,ts') -> go r ts'
            Nothing      -> Con c lc n conds (if mode == NF then map (nf NF) es' else es')
    go fc@(FunCall _ _ []) ts = apps fc (nfs mode ts)
    go fc@(FunCall _ _ clauses) ts = case instantiateCases clauses ts of
        Just (r,ts') -> go r ts'
        Nothing      -> apps fc (nfs mode ts)
    go (At a b e1 e2) ts = case (nf WHNF e1, nf WHNF e2) of
        (_, ICon ILeft)      -> go a ts
        (_, ICon IRight)     -> go b ts
        (PCon (Just t1), t2) -> go t1 (t2:ts)
        (t1, t2)             -> apps (At (go a []) (go b []) (go t1 []) (go t2 [])) (nfs mode ts)
    go (Coe es) ts = case es ++ ts of
        es'@(e1:e2:e3:e4:es'') ->
            let e1' = nf WHNF e1
                e2' = nf NF e2
                e4' = nf NF e4
            in case (e2' == e4' || isStationary e1', e2' == ICon ILeft && e4' == ICon IRight,
                                                     e2' == ICon IRight && e4' == ICon ILeft, e1') of
                (True, _, _, _) -> go e3 es''
                (_, b1, b2, Iso [t1,t2,t3,t4,t5,t6]) | b1 || b2 -> go (App (if b1 then t3 else t4) e3) es''
                (_, b1, b2, _) | b1 || b2 -> case nf NF $ App (fmap Free e1') (Var Bound) of
                    Iso [t1,t2,t3,t4,t5,t6, Var Bound] -> case sequenceA $ Iso [t1,t2,t3,t4,t5,t6] of
                        Free (Iso [t1',t2',t3',t4',t5',t6']) -> go (App (if b1 then t3' else t4') e3) es''
                        _ -> Coe (nfs mode es')
                    _ -> Coe (nfs mode es')
                _ -> Coe (nfs mode es')
        es' -> Coe (nfs mode es')
    go (Iso es) ts = case map (nf WHNF) (es ++ ts) of
        t1:t2:t3:t4:t5:t6: ICon ILeft  : _ -> go t1 []
        t1:t2:t3:t4:t5:t6: ICon IRight : _ -> go t2 []
        _                                  -> Iso $ nfs mode (es ++ ts)
    go (Squeeze es) ts = case map (nf WHNF) (es ++ ts) of
        ICon ILeft  : _ : _ -> ICon ILeft
        ICon IRight : j : _ -> if mode == WHNF then j else nf mode j
        _ : ICon ILeft  : _ -> ICon ILeft
        i : ICon IRight : _ -> if mode == WHNF then i else nf mode i
        es'                 -> Squeeze $ nfs mode (es ++ ts)

nfType :: Eq a => NF -> Type a -> Type a
nfType mode (Type t lvl) = Type (nf mode t) lvl

nfScope :: Eq a => Scope s Term a -> Scope s Term a
nfScope (ScopeTerm t) = ScopeTerm (nf NF t)
nfScope (Scope v   s) = Scope v (nfScope s)

isStationary :: Eq a => Term a -> Bool
isStationary t = case sequenceA (nf NF $ App (fmap Free t) $ Var Bound) of
    Free _ -> True
    Bound  -> False

nfs :: Eq a => NF -> [Term a] -> [Term a]
nfs NF terms = map (nf NF) terms
nfs _  terms = terms

instantiatePat :: Eq a => [Pattern c] -> Scope b Term a -> [Term a] -> Maybe (Term a, [Term a])
instantiatePat [] (ScopeTerm term) terms = Just (term, terms)
instantiatePat (PatternVar  _ : pats) (Scope _ scope) (term:terms) = instantiatePat pats (instantiateScope term scope) terms
instantiatePat (PatternI con : pats) scope (term:terms) = case nf WHNF term of
    ICon i | i == con -> instantiatePat pats scope terms
    _ -> Nothing
instantiatePat (Pattern (PatternCon con _ _ _) pats1 : pats) scope (term:terms) = case nf WHNF term of
    Con i _ n _ terms1 | i == con -> instantiatePat (pats1 ++ pats) scope (terms1 ++ terms)
    _ -> Nothing
instantiatePat _ _ _ = Nothing

instantiateCases :: Eq a => [([Pattern c], Closed (Scope b Term))] -> [Term a] -> Maybe (Term a, [Term a])
instantiateCases clauses terms = msum $ map (\(pats, Closed scope) -> instantiatePat pats scope terms) clauses