{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE GADTs #-}
module Data.HDiff.Patch where
import Control.Monad.State
import Control.Monad.Except
import Data.Type.Equality
import qualified Data.Set as S
import qualified Data.Map as M
import Data.Functor.Const
import Generics.MRSOP.Util
import Generics.MRSOP.Base
import Generics.MRSOP.Holes
import Generics.MRSOP.HDiff.Holes
import Data.Exists
import Data.HDiff.MetaVar
import Data.HDiff.Change
import Data.HDiff.Change.Apply
type RawPatch ki codes = Holes ki codes (CChange ki codes)
type Patch ki codes ix = Holes ki codes (CChange ki codes) ('I ix)
patchEq :: (EqHO ki) => RawPatch ki codes at -> RawPatch ki codes at -> Bool
patchEq p q = and $ holesGetHolesAnnWith' (uncurry' go) $ holesLCP p q
where
go :: (EqHO ki) => RawPatch ki codes at -> RawPatch ki codes at -> Bool
go c d = changeEq (distrCChange c) (distrCChange d)
patchIsCpy :: (EqHO ki) => RawPatch ki codes at -> Bool
patchIsCpy = and . holesGetHolesAnnWith' isCpy
patchMaxVar :: RawPatch ki codes at -> Int
patchMaxVar = flip execState 0 . holesMapM localMax
where
localMax r@(CMatch vars _ _)
= let m = (1+) . maybe 0 id . S.lookupMax $ S.map (exElim metavarGet) vars
in modify (max m) >> return r
withFreshNamesFrom :: RawPatch ki codes at
-> RawPatch ki codes at
-> RawPatch ki codes at
withFreshNamesFrom p q = holesMap (changeAdd (patchMaxVar q + 1)) p
where
changeAdd :: Int -> CChange ki codes at -> CChange ki codes at
changeAdd n (CMatch vs del ins)
= CMatch (S.map (exMap (metavarAdd n)) vs)
(holesMap (metavarAdd n) del)
(holesMap (metavarAdd n) ins)
patchCost :: RawPatch ki codes at -> Int
patchCost = sum . holesGetHolesAnnWith' go
where
go :: CChange ki codes at -> Int
go (CMatch _ d i) = holesSize d + holesSize i
apply :: (TestEquality ki , EqHO ki , ShowHO ki, IsNat ix)
=> Patch ki codes ix
-> Fix ki codes ix
-> Either String (Fix ki codes ix)
apply patch x0
= holesZipRep patch (NA_I x0)
>>= holesMapM (uncurry' termApply)
>>= holes2naM Right
>>= return . unNA_I
where
unNA_I :: NA f g ('I i) -> g i
unNA_I (NA_I x) = x
composes :: (ShowHO ki , EqHO ki , TestEquality ki)
=> RawPatch ki codes at
-> RawPatch ki codes at
-> Bool
composes qr0 pq0 = and $ holesGetHolesAnnWith' getConst
$ holesMap (uncurry' go) $ holesLCP qr0 pq0
where
go :: (ShowHO ki , EqHO ki , TestEquality ki)
=> RawPatch ki codes at
-> RawPatch ki codes at
-> Const Bool at
go qr pq =
let cqr = distrCChange qr
cpq = distrCChange pq
in Const $ applicableTo (cCtxDel cqr) (cCtxIns cpq)
applicableTo :: (ShowHO ki , EqHO ki , TestEquality ki)
=> Holes ki codes (MetaVarIK ki) ix
-> Holes ki codes (MetaVarIK ki) ix
-> Bool
applicableTo pat = either (const False) (const True)
. runExcept
. go M.empty M.empty pat
where
go :: (ShowHO ki , EqHO ki , TestEquality ki)
=> Subst ki codes (MetaVarIK ki)
-> Subst ki codes (MetaVarIK ki)
-> Holes ki codes (MetaVarIK ki) ix
-> Holes ki codes (MetaVarIK ki) ix
-> Except (ApplicationErr ki codes (MetaVarIK ki))
(Subst ki codes (MetaVarIK ki) , Subst ki codes (MetaVarIK ki))
go l r (Hole _ var) ex = (,r) <$> substInsert' "l" l var ex
go l r pa (Hole _ var) = (l,) <$> substInsert' "r" r var pa
go l r (HOpq _ oa) (HOpq _ ox)
| oa == ox = return (l , r)
| otherwise = throwError (IncompatibleOpqs oa ox)
go l r pa@(HPeel _ ca ppa) x@(HPeel _ cx px) =
case testEquality ca cx of
Nothing -> throwError (IncompatibleTerms pa x)
Just Refl -> getConst <$>
cataNPM (\(pa' :*: px') (Const (vl , vr))
-> fmap Const $ go vl vr pa' px')
(return $ Const $ (l ,r))
(zipNP ppa px)
substInsert' :: (ShowHO ki , EqHO ki , TestEquality ki)
=> String
-> Subst ki codes (MetaVarIK ki)
-> MetaVarIK ki ix
-> Holes ki codes (MetaVarIK ki) ix
-> Except (ApplicationErr ki codes (MetaVarIK ki)) (Subst ki codes (MetaVarIK ki))
substInsert' _ s var new = case M.lookup (metavarGet var) s of
Nothing -> return $ M.insert (metavarGet var)
(Exists $ new) s
Just (Exists old) -> case testEquality old new of
Nothing -> throwError IncompatibleTypes
Just Refl -> case old `specializesTo` new of
Just res -> return $ M.insert (metavarGet var) (Exists $ res) s
Nothing -> throwError (FailedContraction (metavarGet var) old new)
where
specializesTo :: (EqHO ki)
=> Holes ki codes (MetaVarIK ki) ix
-> Holes ki codes (MetaVarIK ki) ix
-> Maybe (Holes ki codes (MetaVarIK ki) ix)
specializesTo m n = fmap holesJoin
$ holesMapM (uncurry' go)
$ holesLCP m n
go :: Holes ki codes (MetaVarIK ki) ix
-> Holes ki codes (MetaVarIK ki) ix
-> Maybe (Holes ki codes (MetaVarIK ki) ix)
go (Hole _ _) r = Just r
go l (Hole _ _) = Just l
go _ _ = Nothing