1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
|
--- --------------------------------------------------------------------------
--- Computation of instance substitutions and generalizations.
---
--- @author Björn Peemöller
--- @version April 2015
--- --------------------------------------------------------------------------
module Instance (instance, instanceOf, msg) where
import List (find, nub)
import Maybe (isJust)
import Pretty (pPrint, ($$), text)
import State ( State, (<$>), (<*>), (>+), (>+=), concatMapS, getsS
, mapS, modifyS, returnS, runState)
import Utils (disjoint, sameLength)
import FlatCurry.Types
import FlatCurryGoodies ( branchExprs, branchPats, samePattern, freeVars
, isConstrTerm, maxVar, patVars, maximumVarIndex)
import FlatCurryPretty (ppExp, indent)
import Normalization (eqRen, renameExprSubst)
import Output (assert)
import Subst ( Subst, ppSubst, emptySubst, combine, compose, dom
, restrict, singleSubst, subst, toSubst, varSubst
, isDetSubst )
-- ---------------------------------------------------------------------------
-- Instance computation
-- ---------------------------------------------------------------------------
--- Is the first expression a strict instance of the second expression?
strictInstanceOf :: Expr -> Expr -> Bool
strictInstanceOf e1 e2 = instanceOf e1 e2 && not (instanceOf e2 e1)
--- Is the first expression a deterministic instance of the second expression?
detInstanceOf :: Expr -> Expr -> Bool
detInstanceOf e1 e2 = case instance e1 e2 of
Nothing -> False
Just s -> isDetSubst e2 s
--- Is the first expression an instance of the second expression?
instanceOf :: Expr -> Expr -> Bool
instanceOf e1 e2 = isJust (instance e1 e2)
--- `instance e1 e2` tries to compute a substitution `sigma`
--- such that $e1 = \sigma(e2)$.
--- If `e1` is an instance of `e2`, the function
--- returns `Just sigma`, if not then `Nothing` is returned.
---
--- Note that `instance` requires the expressions to share the same structure,
--- i.e., no normalization is considered.
instance :: Expr -> Expr -> Maybe Subst
instance e1 e2 = case instance' re1 re2 of
Nothing -> Nothing
Just s -> let s' = restrict (dom s2) $ compose s1 $ compose s s2
in assert (subst s re2 `eqRen` e1) "error in instance"
$ Just s'
where
(re1, r1) = renameExprSubst e1
(re2, r2) = renameExprSubst e2
s1 = toSubst [(x, Var y) | (y, x) <- r1]
s2 = toSubst [(x, Var y) | (x, y) <- r2]
--- We use the fact that variables free in the expression have a negative
--- variable index after `renameExprSubst`. Therefore, we can limit the
--- substitution to substitute variables with negative indizes only,
--- where the domain of the resulting substitution must not include locally
--- introduced variables.
instance' :: Expr -> Expr -> Maybe Subst
instance' ex1 ex2 = case (ex1, ex2) of
(Var x, Var y)
-- We create a substitution even for unbound variables because an unbound
-- variable may not be bound to another expression later.
| x == y -> Just $ if isUnboundVariable y
then singleSubst y ex1
else emptySubst
(_ , Var y)
| all isUnboundVariable
(y : freeVars ex1) -> Just (singleSubst y ex1)
(Lit x, Lit y)
| x == y -> Just emptySubst
(Comb c1 f1 es1, Comb c2 f2 es2)
| c1 == c2 && f1 == f2
&& sameLength es1 es2 -> instanceL es1 es2
(Let ds1 e1, Let ds2 e2)
| sameLength ds1 ds2 -> let (vs1, bs1) = unzip ds1
(vs2, bs2) = unzip ds2
in instanceL
(map (varSubst vs1 vs2) (e1 : bs1))
(e2 : bs2)
(Free vs1 e1, Free vs2 e2)
| sameLength vs1 vs2 -> instance' (varSubst vs1 vs2 e1) e2
(Or e1 f1, Or e2 f2) -> instanceL [e1,f1] [e2,f2]
(Case c1 e1 b1, Case c2 e2 b2)
| c1 == c2 && samePattern b1 b2 -> foldl union (instance' e1 e2)
(zipWith instanceBranch b1 b2)
(Typed e1 ty1, Typed e2 ty2)
| ty1 == ty2 -> instance' e1 e2
_ -> Nothing
--- Is a variable unbound in the entire expression?
isUnboundVariable :: VarIndex -> Bool
isUnboundVariable v = v < 0
--- Instance computation for two lists of expressions.
instanceL :: [Expr] -> [Expr] -> Maybe Subst
instanceL es1 es2 = foldl union (Just emptySubst) (zipWith instance' es1 es2)
--- Instance for branch expression.
instanceBranch :: BranchExpr -> BranchExpr -> Maybe Subst
instanceBranch (Branch p1 e1) (Branch p2 e2)
= instance' (varSubst (patVars p1) (patVars p2) e1) e2
--- Union of two substititutions with a check for a clashing substitution.
union :: Maybe Subst -> Maybe Subst -> Maybe Subst
union Nothing _ = Nothing
union (Just _ ) Nothing = Nothing
union (Just s1) (Just s2) = Subst.combine s1 s2
-- -----------------------------------------------------------------------------
-- Computation of the most specific generalizer (msg) of two expressions.
-- -----------------------------------------------------------------------------
--- `msg e e' = (g, sigma, theta)` returns the most specific generalization
--- `g` of `e` and `e'` and the substitutions `sigma` and `theta`,
--- such that $e = \sigma(g)$, and $e' = \theta(g)$.
--- A generalizer of two terms $e_1$, $e_2$ is a term $e$,
--- such that $\sigma(e) = e_1$ and $theta(e) = e_2$
--- ($e$ generalizes $e_1$ and $e_2$).
--- Furthermore, there is no other generalization $e'$ of $e_1$ and $e_2$,
--- such that $e' \neq e$ and $\tau(e) = e'$ ($e$ is most specific).
--- We do not rename the msg because otherwise the substitution may refer
--- to variables locally introduced in the msg.
msg :: Expr -> Expr -> (Expr, Subst, Subst)
msg e1 e2
= let (g, state) = runState (msg' (e1, e2))
(initState (maximumVarIndex [e1, e2] + 1))
l = msgSubst state
fv = freeVars g
s1 = toSubst [ (v, e) | (v, e, _) <- l, v `elem` fv]
s2 = toSubst [ (v, e) | (v, _, e) <- l, v `elem` fv]
in assertInstance e1 g s1 $ assertInstance e2 g s2 $ (g, s1, s2)
where
assertInstance e g s x = assert (eqRen (subst s g) e) (pPrint doc) x
where doc = indent (text "Expression" $$ ppExp e)
$$ indent (text "is no instance of" $$ ppExp g)
$$ indent (text "with substitution" $$ ppSubst s)
--- The Msg monad
type Msg a = State MsgState a
--- Internal state for msg computation
data MsgState = MsgState
{ msgFresh :: VarIndex
, msgSubst :: [(VarIndex, Expr, Expr)]
}
--- Initial state for msg computation
initState :: VarIndex -> MsgState
initState x = MsgState x []
--- Get a fresh variable
getFresh :: Msg VarIndex
getFresh = getsS msgFresh >+= \i ->
modifyS (\s -> s { msgFresh = i + 1 }) >+
returnS i
--- Create a new substitution for two expressions
newSubst :: Expr -> Expr -> Msg Expr
newSubst e1 e2 = getsS msgSubst >+= \sub ->
getFresh >+= \j ->
modifyS (\s -> s { msgSubst = (j, e1, e2) : sub }) >+
returnS (Var j)
--- Get the substitution for two expressions
getSubst :: Expr -> Expr -> Msg Expr
getSubst e1 e2
| all isConstrTerm [e1, e2]
= getsS msgSubst >+= \sub ->
case find (\(_, s1, s2) -> e1 == s1 && e2 == s2) sub of
Just (v, _, _) -> returnS (Var v)
Nothing -> newSubst e1 e2
| otherwise = newSubst e1 e2
--- `substsUseLocalVars vs es` checks whether one of the substitutions for the
--- variables in `es` substitutes any of the variables in `vs`.
--- This is used to guarantee that the substitution does not affect locally
--- introduced variables.
substsUseLocalVars :: [VarIndex] -> [Expr] -> Msg Bool
substsUseLocalVars vs es =
concatMapS substsFor (nub $ concatMap freeVars es) >+= \es' ->
returnS $ not $ disjoint vs (nub $ concatMap freeVars es')
where
substsFor v = getsS msgSubst >+= \sub ->
case find (\(x, _, _) -> x == v) sub of
Just (_ , e1, e2) -> returnS [e1, e2]
Nothing -> returnS []
--- Compute the most specific generalization of two expressions.
msg' :: (Expr, Expr) -> Msg Expr
msg' p@(ex1, ex2) = case p of
(Var x, Var y) | x == y
-> returnS ex1
(Lit x, Lit y) | x == y
-> returnS ex1
(Comb c1 f1 e1, Comb c2 f2 e2) | c1 == c2 && f1 == f2 && sameLength e1 e2
-> Comb c1 f1 <$> mapS msg' (zip e1 e2)
(Let ds1 e1, Let ds2 e2) | sameLength ds1 ds2
-> mapS msgVar (zip vs1 vs2) >+= \vs ->
let es1' = map (varSubst vs1 vs) es1
es2' = map (varSubst vs2 vs) es2 in
mapS msg' (zip es1' es2') >+= \es' ->
msg' (varSubst vs1 vs e1, varSubst vs2 vs e2) >+= \e' ->
substsUseLocalVars vs (e':es') >+= \used ->
if used then getSubst ex1 ex2 else returnS (Let (zip vs es') e')
where (vs1, es1) = unzip ds1
(vs2, es2) = unzip ds2
(Free xs e1, Free ys e2) | sameLength xs ys
-> mapS msgVar (zip xs ys) >+= \zs ->
msg' (varSubst xs zs e1, varSubst ys zs e2) >+= \e' ->
substsUseLocalVars zs [e'] >+= \used ->
if used then getSubst ex1 ex2 else returnS (Free zs e')
(Or x1 y1, Or x2 y2)
-> Or <$> msg' (x1, x2) <*> msg' (y1, y2)
(Case c1 e1 b1, Case c2 e2 b2) | c1 == c2 && samePattern b1 b2
-> msg' (e1, e2) >+= \e ->
mapS msgBranch (zip b1 b2) >+= \bs ->
let vs = nub $ concatMap patVars (branchPats bs) in
substsUseLocalVars vs (branchExprs bs) >+= \used ->
if used then getSubst ex1 ex2 else returnS $ Case c1 e bs
(Typed x1 ty1, Typed x2 ty2)
| ty1 == ty2 -> flip Typed ty1 <$> msg' (x1, x2)
(_ , _ ) -> getSubst ex1 ex2
--- Compute the most specific generalization of two branch expressions.
msgBranch :: (BranchExpr, BranchExpr) -> Msg BranchExpr
msgBranch (Branch p1 e1, Branch p2 e2) = case (p1, p2) of
(LPattern l , LPattern _) -> Branch (LPattern l) <$> msg' (e1, e2)
(Pattern c xs, Pattern _ ys) -> mapS msgVar (zip xs ys) >+= \zs ->
Branch (Pattern c zs) <$>
let e1' = varSubst xs zs e1
e2' = varSubst ys zs e2
in msg' (e1', e2')
_ -> error "Instance.msgBranch"
--- Compute the most specific generalization
--- of two locally introduced variables.
msgVar :: (VarIndex, VarIndex) -> Msg VarIndex
msgVar (x, y) = if x == y then returnS x else getFresh
|