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 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
|
--- ----------------------------------------------------------------------------
--- This module implements normalization of FlatCurry programs and expressions.
--- This includes
--- * $\alpha$-conversion (renaming of variables)
--- * flattening (lifting nested applications to let bindings)
--- * Ordering of let declarations in the order in which the bound
--- variables occur in the expression.
---
--- Furthermore, a compression of expressions is implemented which simplifies
--- expressions by
--- * Removing unused variable bindings
--- * Removing failing non-deterministic branches
--- * Substituting variable bindings which are not shared
--- * Removing failing alternatives in case expression
---
--- @author Björn Peemöller
--- @version April 2015
--- ----------------------------------------------------------------------------
module Normalization
( -- Normalization
eqNorm, eqRen, normalizeExpr, normalizeFreeExpr, simplifyExpr
-- Generation of fresh variants
, freshResultant, freshRule
-- Renaming
, renameResultant, renameFuncDecl, renameFreeExpr, renameExpr, renameExprSubst
) where
import Function (second)
import List (intersect, mapAccumL, partition)
import Utils (count, sameLength)
import FlatCurry.Types
import FlatCurryGoodies ( addPartCallArg, eqPattern, failedExpr, freeVars
, freeVarsDup, maxVar, isLit, topSQ, getSQ
, func, isConsCall, isPartCall, isVar, maxVarIndex
, maximumVarIndex, isSQ, isFailed, findBranch
, mkCase, mkFree, mkLet, mkOr, patVars, sq
, prelApply, prelCond, prelCond', trExpr)
import Output (assert)
import PevalBase (Resultant)
-- import SCC (scc)
import Subst (mkSubst, singleSubst, subst, varSubst)
-- -----------------------------------------------------------------------------
-- Normalization
-- -----------------------------------------------------------------------------
eqNorm :: Expr -> Expr -> Bool
eqNorm x y = normalizeExpr x == normalizeExpr y
--- Normalization of an expression which may contain free variables.
--- This includes:
--- 1) Compression of the expression (see below).
--- 1) The order of let-bound variables and free variables is changed
--- to the order of the occurences of the bindings in the expression.
--- 1) Unbound variables are renumbered from -1 downwards, bound variables
--- from 1 upwards in the order of their occurence.
normalizeExpr :: Expr -> Expr
normalizeExpr = renameExpr . orderDecls
--- Like `normalizeExpr`, but unbound variables in the expression
--- retain their indizes.
normalizeFreeExpr :: Expr -> Expr
normalizeFreeExpr = renameFreeExpr . orderDecls
--- Renaming of an expression such that afterwards the variables defined in the
--- expression are numbered from max(1, maxFreeVar + 1) upwards
--- and the variables free in the expression retain their indizes.
renameFreeExpr :: Expr -> Expr
renameFreeExpr e = freshExpr (maxVar (freeVars e) + 1) e
--- Renaming of an expression such that the variables defined in the
--- expression are numbered from max(1, maxFreeVar + 1) upwards
--- and the variables free in the expression from -1 downwards.
--- The resulting substitution contains the mapping from the former unbound
--- variables to the latter unbound variables.
renameExprSubst :: Expr -> (Expr, [(VarIndex, VarIndex)])
renameExprSubst e
= assert (uncurry (flip varSubst) (unzip sub) e' `eqRen` e) "renameExprSubst"
(e', sub)
where
e' = snd $ rnExpr sub (newVars $ maxVar fvs + 1) e
fvs = freeVars e
sub = zip fvs [-1, -2 ..]
-- -----------------------------------------------------------------------------
-- Fresh variants and renaming
-- -----------------------------------------------------------------------------
--- Create a fresh variant of a resultant by numbering all variables
--- from `i` onwards.
freshResultant :: VarIndex -> Resultant -> Resultant
freshResultant i ((f, vs), e) = ((f, vs'), e')
where (Rule vs' e') = snd $ rnRule (newVars i) (Rule vs e)
--- Create a fresh variant of a pattern by numbering all variables
--- from `i` onwards.
freshPattern :: VarIndex -> Pattern -> Pattern
freshPattern i p = snd (rnPattern (newVars i) p)
--- Create a fresh variant of a rule by numbering all variables
--- from `i` onwards.
freshRule :: VarIndex -> Rule -> Rule
freshRule i = snd . rnRule (newVars i)
--- Create a fresh variant of an expression by numbering all local variables
--- from `i` onwards.
freshExpr :: VarIndex -> Expr -> Expr
freshExpr i = snd . rnExpr [] (newVars i)
--- Renaming of a resultant such that the variables are numbered
--- from 1 onwards, starting with the function's parameters.
renameResultant :: Resultant -> Resultant
renameResultant = freshResultant 1
--- Renaming of a function declaration such that the variables are numbered
--- from 1 onwards, starting with the function's parameters.
renameFuncDecl :: FuncDecl -> FuncDecl
renameFuncDecl = snd . rnFunc (newVars 1)
--- Are two expression equal up to a renaming of local variables?
eqRen :: Expr -> Expr -> Bool
eqRen x y = renameExpr x == renameExpr y
--- Renaming of an expression such that afterwards the variables defined in the
--- expression are numbered from 1 upwards and the variables free in the
--- expression are numbered from -1 downwards.
renameExpr :: Expr -> Expr
renameExpr e = snd $ rnExpr (zip (freeVars e) [-1, -2 ..]) (newVars 1) e
-- -----------------------------------------------------------------------------
-- Generation of variables
-- -----------------------------------------------------------------------------
--- Create an infinite list of new variables.
newVars :: VarIndex -> [VarIndex]
newVars x = [ x .. ]
takeVars :: [VarIndex] -> [a] -> ([VarIndex], [VarIndex])
takeVars fresh [] = (fresh, [])
takeVars (f:fs) (_:os) = let (fs', os') = takeVars fs os in (fs', f : os')
takeVars [] (_:_) = error "Renaming.takeVars: no more fresh variables"
-- -----------------------------------------------------------------------------
-- Implementation of renaming
-- -----------------------------------------------------------------------------
type Renaming a = [VarIndex] -> a -> ([VarIndex], a)
--- Renaming of a 'Prog', i.e., all variables occurring in the 'Prog'
--- get renamed to @v1@, @v2@, ... consistently ($\alpha$-conversion).
rnProg :: Renaming Prog
rnProg xs (Prog m is ty fs os) = (xs1, Prog m is ty fs' os)
where (xs1, fs') = mapAccumL rnFunc xs fs
--- Renaming of a function declaration.
rnFunc :: Renaming FuncDecl
rnFunc xs (Func f a v ty r) = second (Func f a v ty) (rnRule xs r)
--- Renaming of a function rule.
rnRule :: Renaming Rule
rnRule xs (Rule vs e) = let (xs1, vs') = takeVars xs vs
(xs2, e' ) = rnExpr (zip vs vs') xs1 e
in (xs2, Rule vs' e')
rnRule xs (External s) = (xs, External s)
--- Renaming of an expression.
rnExpr :: [(VarIndex, VarIndex)] -> Renaming Expr
rnExpr ren xs (Var x) = case lookup x ren of
Nothing -> (xs, Var x)
Just w -> (xs, Var w)
rnExpr _ xs l@(Lit _) = (xs, l)
rnExpr ren xs (Comb ct qn es)
= let (xs1, es') = mapAccumL (rnExpr ren) xs es
in (xs1, Comb ct qn es')
rnExpr ren xs (Free vs e)
= let (xs1, vs') = takeVars xs vs
ren1 = zip vs vs' ++ filter ((`notElem` vs) . fst) ren
(xs2, e' ) = rnExpr ren1 xs1 e
in (xs2, Free vs' e')
rnExpr ren xs (Let ds e)
= let (vs , es ) = unzip ds
(xs1, vs') = takeVars xs ds
ren1 = zip vs vs' ++ filter ((`notElem` vs) . fst) ren
(xs2, es') = mapAccumL (rnExpr ren1) xs1 es
(xs3, e' ) = rnExpr ren1 xs2 e
in (xs3, Let (zip vs' es') e')
rnExpr ren xs (Or e1 e2)
= let (xs1, e1') = rnExpr ren xs e1
(xs2, e2') = rnExpr ren xs1 e2
in (xs2, Or e1' e2')
rnExpr ren xs (Case ct e bs)
= let (xs1, e' ) = rnExpr ren xs e
(xs2, bs') = mapAccumL (rnBranchExpr ren) xs1 bs
in (xs2, Case ct e' bs')
rnExpr ren xs (Typed e ty)
= let (xs1, e') = rnExpr ren xs e
in (xs1, Typed e' ty)
rnBranchExpr :: [(VarIndex, VarIndex)] -> Renaming BranchExpr
rnBranchExpr ren ys (Branch (Pattern p zs) be)
= let (ys1, zs') = takeVars ys zs
ren1 = zip zs zs' ++ filter ((`notElem` zs) . fst) ren
(ys2, be') = rnExpr ren1 ys1 be
in (ys2, Branch (Pattern p zs') be')
rnBranchExpr ren ys (Branch l@(LPattern _) be)
= let (ys1, be') = rnExpr ren ys be
in (ys1, Branch l be')
rnPattern :: Renaming Pattern
rnPattern xs (Pattern p ys) = second (Pattern p) (takeVars xs ys)
rnPattern xs l@(LPattern _) = (xs, l)
-- -----------------------------------------------------------------------------
-- Compression of an expression
-- -----------------------------------------------------------------------------
--- Order the bindings of locally introduced variables in the order
--- of their occurence in the expression. If any variable is unused,
--- it is removed.
orderDecls :: Expr -> Expr
orderDecls = trExpr Var Lit Comb oFree Or Case Branch oLet Typed
where
oFree vs e = let vs' = filter (`elem` vs) (freeVars e)
in assert (sameLength vs vs') "orderDecls"
mkFree vs' e
-- $ foldr (\v e' -> mkFree [v] e') e vs'
oLet ds e = mkLet (extract ds (freeVars e)) e
-- foldr mkLet e (scc (\(v, _) -> [v]) (\(_, b) -> freeVars b) ds)
extract [] _ = []
extract (_:_) [] = error "Normalization.orderDecls"
extract ds@(_:_) (v:vs) = case break ((== v) . fst) ds of
(_ , [] ) -> extract ds vs
(ds1, (_, e'):ds2) -> (v, e') : extract (ds1 ++ ds2) (vs ++ freeVars e')
--- Simplification of FlatCurry expressions. This consists of the following steps:
--- * Locally introduces free variables that are not used are removed.
--- * `Or` applied to `failed` is reduced to the alternative expression.
--- * If `Or` is applied to two calls to `cond` sharing the same first
--- condition, the condition is lifted above the `Or` construct.
--- * If `Or` is applied to two `case` expressions scrutinizing the same
--- expression, the `case` expression is lifted upwards and the corresponding
--- branches are combined using `Or`.
--- * Case branches directly calling `failed` are removed. If all branches
--- are removed, the expression is reduced to `failed` instead.
--- * Evaluation annotations `SQ e` are moved downwards until a potentially
--- evaluable expression in encountered.
--- * Let-bindings which refer to another variable or are only used once
--- are inserted and removed afterwards.
--- * Unused let-bindings are removed.
simplifyExpr :: Expr -> Expr
simplifyExpr = trExpr Var Lit cComb cFree cOr mkCase Branch cLet Typed
where
-- compression of annotations, see FlatCurryGoodies for sq
cComb ct f es = let call = Comb ct f es in case getSQ call of
Just e -> sq e
_ -> call
-- compression of free variables: remove unused variables
cFree vs e = mkFree (vs `intersect` freeVars e) e
-- compression of non-determinism
cOr e1 e2 = case (e1, e2) of
(Comb FuncCall f [a, b], Comb FuncCall g [c, d])
| all (`elem` [prelCond, prelCond']) [f, g]
&& a == c -> simplifyExpr $ func prelCond [a, Or b d]
(Case ct1 e1' bs1, Case ct2 e2' bs2)
| ct1 == ct2 && e1' == e2' -> simplifyExpr $ Case ct1 e1'
(mergeBranches bs1 bs2)
_ | isSQ e1 || isSQ e2 -> simplifyExpr (topSQ (Or e1 e2))
| otherwise -> mkOr e1 e2
mergeBranches [] bs2 = bs2
mergeBranches (b@(Branch p1 e1):bs1) bs2
= case break (\(Branch p' _) -> eqPattern p1 p') bs2 of
(_ , [] ) -> b : mergeBranches bs1 bs2
(bs21, Branch p2 e2 : bs22) ->
let p' = freshPattern (maximumVarIndex [e1, e2]) p1
e' = Or (varSubst (patVars p1) (patVars p') e1)
(varSubst (patVars p2) (patVars p') e2)
in Branch p' (simplifyExpr e') : mergeBranches bs1 (bs21 ++ bs22)
-- compression of let-declarations
cLet ds e
| ds == ds' && e == e' = mkLet ds' e'
| otherwise = cLet ds' e'
where
(ds', e') = cLet' [] ds
cLet' bs0 [] = (bs0, e)
cLet' bs1 (b : bs2)
| isInlineable b = (map (second replace) (bs1 ++ bs2), replace e)
| otherwise = cLet' (bs1 ++ [b]) bs2
where
isInlineable bd = uncurry isSimple bd || not (isShared bd)
isShared (v, _ ) = count v (concatMap freeVarsDup (e : map snd ds)) > 1
replace = simplifyExpr . subst (uncurry singleSubst b)
isSimple v ve = case ve of
Var x -> x /= v -- do not replace recursive bindings
-- such as let ones = 1 : ones in ones
Lit _ -> True
Comb ct _ es -> (ct == ConsCall || isPartCall ct) && all (isSimple v) es
_ -> isFailed ve
-- Compression of case expressions: When the scrutinized expression is either
-- a literal or a constructor call, the respective branch is searched for.
-- If such a branch exists, the expressions reduces to the branch's
-- right-hand-side, otherwise the expression reduces to `failed`.
cCase ct e bs = case e of
Lit l -> case findBranch (LPattern l) bs of
Nothing -> failedExpr
Just ( _, be) -> be
Comb ConsCall c es -> case findBranch (Pattern c []) bs of
Nothing -> failedExpr
Just (xs, be) -> simplifyExpr (unfold xs es be)
_ -> mkCase ct e bs
--- Simple unfolding
unfold :: [VarIndex] -> [Expr] -> Expr -> Expr
unfold vs es e = mkLet (zip vs' es) e'
where Rule vs' e' = freshRule (maximumVarIndex es + 1) (Rule vs e)
-- -----------------------------------------------------------------------------
-- Flattening
-- -----------------------------------------------------------------------------
--- A flattening takes an additional list of fresh variables
type Flattening a = [VarIndex] -> a -> ([VarIndex], a)
--- Flatten a program
flattenProg :: Flattening Prog
flattenProg xs (Prog m is ty fs os) = (xs1, Prog m is ty fs' os)
where (xs1, fs') = mapAccumL flattenFunc xs fs
--- Flatten a function
flattenFunc :: Flattening FuncDecl
flattenFunc xs (Func f a v ty r) = second (Func f a v ty) (flattenRule xs r)
--- Flatten a rule
flattenRule :: Flattening Rule
flattenRule xs (Rule vs e) = second (Rule vs) (flattenExpr xs e)
flattenRule xs (External s) = (xs, External s)
--- Flatten an expression
flattenExpr :: Flattening Expr
flattenExpr xs v@(Var _) = (xs, v)
flattenExpr xs l@(Lit _) = (xs, l)
flattenExpr xs (Comb ct qn es) = let (xs1, ds, es') = splitArgs xs es
in (xs1, flatLet ds (Comb ct qn es'))
flattenExpr xs (Free vs e) = let (xs1, e' ) = flattenExpr xs e
in (xs1, Free vs e')
flattenExpr xs (Let ds e) = let (xs1, ds') = mapAccumL flatD xs ds
(xs2, e' ) = flattenExpr xs1 e
in (xs2, flatLet ds' e')
where flatD ys (v, ve) = let (ys1, ve') = flattenExpr ys ve
in (ys1, (v, ve'))
flattenExpr xs (Or e1 e2) = let (xs1, e1') = flattenExpr xs e1
(xs2, e2') = flattenExpr xs1 e2
in (xs2, Or e1' e2')
flattenExpr xs (Case ct e bs) = let (xs1, e' ) = flattenExpr xs e
(xs2, bs') = mapAccumL flatB xs1 bs
in (xs2, Case ct e' bs')
where flatB ys (Branch p be) = let (ys1, be') = flattenExpr ys be
in (ys1, Branch p be')
flattenExpr xs (Typed e ty) = let (xs1, e' ) = flattenExpr xs e
in (xs1, Typed e' ty)
--- @splitArgs xs es = (xs', ds, es')@ replaces all non-variable
--- expressions in @es@ by new variables subsequently taken from @xs@,
--- and generates the bindings @ds@ for those lifted expressions.
--- That is, @es'@ consists of variables only, originating either from @es@
--- or from @xs@, such that $es'[ds] = es$, i.e., replacing the extracted
--- bindings again should yield the original list.
--- @xs'@ is xs without the extracted variables.
splitArgs :: [VarIndex] -> [Expr] -> ([VarIndex], [(VarIndex, Expr)], [Expr])
splitArgs xs [] = (xs, [], [])
splitArgs xs (e:es) = case e of
Var _ -> let (xs', ds, es') = splitArgs xs es
in (xs', ds, e : es')
_ -> let (x' : xs', e') = flattenExpr xs e
(xs2, ds, es') = splitArgs xs' es
in (xs2, (x', e') : ds, Var x' : es')
--- @flatLet ds e@ lifts nested let-declarations in ds to the top-level.
--- In addition, if `e` has the form `Let ds' e'`, the bindings `ds'` are
--- also lifted upwards.
flatLet :: [(VarIndex, Expr)] -> Expr -> Expr
flatLet decls ex = case liftDecls decls of
[] -> ex
ds' -> case ex of
Let ds'' e' -> Let (ds' ++ ds'') e'
_ -> Let ds' ex
where
liftDecls [] = []
liftDecls ((x, d) : ds) = case d of
Let ds1 e -> ds1 ++ (x, e) : liftDecls ds
_ -> (x, d) : liftDecls ds
|