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
--- --------------------------------------------------------------------------
--- Substitutions on FlatCurry expressions.
---
--- @author  Björn Peemöller
--- @version April 2015
--- --------------------------------------------------------------------------
module Subst
  ( Subst, ppSubst, emptySubst, singleSubst, mkSubst, toSubst, dom, rng
  , restrict, without, compose, combine, lookupSubst, findSubstWithDefault
  , fmapSubst, substSingle, subst, varSubst, isVariableRenaming, isDetSubst
  ) where

import List             (nub)
import Text.Pretty      (Doc, (<+>), ($$), rarrow, pPrint, semiBracesSpaced, text)
import Utils            (disjoint)

import FlatCurry.Types
import FlatCurryGoodies (isConstrTerm, freeVarsDup, isVar, patVars)
import FlatCurryPretty  (ppVarIndex, ppExp, indent)
import Output           (assert)
import Utils            (count)

--- Data type for substitutions
data Subst = Subst [(VarIndex, Expr)]

--- Pretty printing of a substitution
ppSubst :: Subst -> Doc
ppSubst (Subst []     ) = text "{}"
ppSubst (Subst s@(_:_)) = semiBracesSpaced (map ppVarBinding s)
  where ppVarBinding (v, e) = ppVarIndex v <+> rarrow <+> ppExp e

--- empty substitution
emptySubst :: Subst
emptySubst = Subst []

--- substitute a variable by an expression
singleSubst :: VarIndex -> Expr -> Subst
singleSubst v e = Subst [(v, e)]

--- create a substitution from a list of variables and a list of expressions
mkSubst :: [VarIndex] -> [Expr] -> Subst
mkSubst vs es = toSubst (zip vs es)

--- create a substitution from a list of variable/expression pairs.
toSubst :: [(VarIndex, Expr)] -> Subst
toSubst = Subst

--- extract domain of a given substitution
dom :: Subst -> [VarIndex]
dom (Subst s) = map fst s

--- extract range of a given substitution
rng :: Subst -> [Expr]
rng (Subst s) = map snd s

--- Restrict a substititution to a given domain.
restrict :: [VarIndex] -> Subst -> Subst
restrict vs (Subst s) = Subst [ s' | s'@(v, _) <- s, v `elem` vs ]

--- Remove a given part of the domain from a substititution.
without :: [VarIndex] -> Subst -> Subst
without vs (Subst s) = Subst [ s' | s'@(v, _) <- s, v `notElem` vs ]

--- `compose s1 s2` composes the substitutions s1 and s2.
--- The result is `s1 . s2`, i.e., it has the same effect as `s1(s2(t))`
--- when applied to a term `t`.
compose :: Subst -> Subst -> Subst
compose s1 s2 =
  let Subst t1 = fmapSubst (subst s1) s2
      Subst t2 = without   (dom   s2) s1
  in  Subst (t1 ++ t2)

--- Try to combine two substitutions $\sigma$ and $\theta$.
--- This operation returns `Nothing` if there exists a conflicting substitution,
--- i.e., there exists a variable $x \in \dom(\sigma)$, $x \in \dom(\theta)$
--- such that $\sigma(x) \neq $\theta(x)$.
combine :: Subst -> Subst -> Maybe Subst
combine s1@(Subst ve1) s2@(Subst ve2)
  | clash s1 s2 = Nothing
  | otherwise   = Just $ toSubst $ nub $ ve1 ++ ve2

--- Do two substituions clash, i.e., do they define a different mapping
--- for at least one variable?
clash :: Subst -> Subst -> Bool
clash (Subst s1) (Subst s2)
  = not $ null [ () | (v1, e1) <- s1, (v2, e2) <- s2, v1 == v2 && e1 /= e2 ]

--- Lookup a substititution for a variable.
--- If there is no substitution given, the variable is substituted by itself.
lookupSubst :: VarIndex -> Subst -> Expr
lookupSubst v s = findSubstWithDefault (Var v) v s

--- Find a substititution for a variable with a given default value.
findSubstWithDefault :: Expr -> VarIndex -> Subst -> Expr
findSubstWithDefault def v (Subst s) = case lookup v s of
  Nothing -> def
  Just e  -> e

--- `fmap f sigma` applies `f` on all expressions in the range of sigma.
--- That is, if $\sigma = \{ v_1 \mapsto e_1 \dots v_n \mapsto e_n \}$, then
--- `fmap f sigma` equals \{ v_1 \mapsto f(e_1) \dots v_n \mapsto f(e_n) \}$
fmapSubst :: (Expr -> Expr) -> Subst -> Subst
fmapSubst f (Subst s) = Subst [ (v, f e) | (v, e) <- s ]

--- `substSingle v s e` substitutes `v` by `s` in `e`
substSingle :: VarIndex -> Expr -> Expr -> Expr
substSingle v s e = subst (singleSubst v s) e

--- Replace a list of variables by another list of variables
--- in the given expression.
varSubst :: [VarIndex] -> [VarIndex] -> Expr -> Expr
varSubst xs ys e = subst (mkSubst xs (map Var ys)) e

--- Is the substititution `sigma` a variable renaming, i.e., are the expressions
--- in `rng(sigma)` all variables?
isVariableRenaming :: Subst -> Bool
isVariableRenaming = all isVar . rng

--- Is the substititution `sigma` a deterministic substitution w.r.t `e`, i.e.,
--- are the expressions in `rng(sigma)` all either constructor terms or does
--- the variable occur at most once in e?
isDetSubst :: Expr -> Subst -> Bool
isDetSubst e (Subst s) = all isDet s
  where isDet (v, e') = isConstrTerm e' || count v (freeVarsDup e) <= 1

--- `subst sigma e = sigma(e)` substitutes all occurrences of variables
---  by corresponding expressions.
---  substitute all occurrences of $v_i$ by $e_i$ in $e$
---  if $s = \{ v_1 \mapsto e_1 \dots v_n \mapsto e_n \}$
---
--- Note: The substititution must not replace variables introduced in the
--- expression itself, either as a free variable, a let binding or a pattern
--- variable. This is guaranteed and checked as an assertion.
--- If the assertion is violated, an error is thrown.
subst :: Subst -> Expr -> Expr
subst s v@(Var         x) = findSubstWithDefault v x s
subst _ l@(Lit         _) = l
subst s (Comb    ct c es) = Comb ct c (map (subst s) es)
subst s f@(Free     vs e) = assert (disjoint vs (dom s))
                                   (errSubst "free variable" s f)
                          $ Free vs   (subst s e)
subst s (Or        e1 e2) = Or        (subst s e1) (subst s e2)
subst s c@(Case  ct e cs) = Case ct   (subst s e) (map substBran cs)
  where
  substBran (Branch p be) = assert (disjoint (patVars p) (dom s))
                                   (errSubst "pattern variable" s c)
                          $ Branch p (subst s be)
subst s l@(Let      bs e) = assert (disjoint (map fst bs) (dom s))
                                   (errSubst "let variable" s l)
                          $ Let (map substBinding bs) (subst s e)
  where substBinding (v, ve) = (v, subst s ve)
subst s (Typed     e ty) = Typed     (subst s e) ty

--- Error message thrown when a locally bound variable appears in the domain
--- of the substitution.
errSubst :: String -> Subst -> Expr -> String
errSubst what s e = pPrint $
     text "Sustitution for"              <+> text what
  $$ text "Could not apply substitution" <+> indent (ppSubst s)
  $$ text "to expression "               <+> indent (ppExp e)