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
|
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 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 Subst = Subst [(VarIndex, Expr)]
ppSubst :: Subst -> Doc
ppSubst (Subst [] ) = text "{}"
ppSubst (Subst s@(_:_)) = semiBracesSpaced (map ppVarBinding s)
where ppVarBinding (v, e) = ppVarIndex v <+> rarrow <+> ppExp e
emptySubst :: Subst
emptySubst = Subst []
singleSubst :: VarIndex -> Expr -> Subst
singleSubst v e = Subst [(v, e)]
mkSubst :: [VarIndex] -> [Expr] -> Subst
mkSubst vs es = toSubst (zip vs es)
toSubst :: [(VarIndex, Expr)] -> Subst
toSubst = Subst
dom :: Subst -> [VarIndex]
dom (Subst s) = map fst s
rng :: Subst -> [Expr]
rng (Subst s) = map snd s
restrict :: [VarIndex] -> Subst -> Subst
restrict vs (Subst s) = Subst [ s' | s'@(v, _) <- s, v `elem` vs ]
without :: [VarIndex] -> Subst -> Subst
without vs (Subst s) = Subst [ s' | s'@(v, _) <- s, v `notElem` vs ]
compose :: Subst -> Subst -> Subst
compose s1 s2 =
let Subst t1 = fmapSubst (subst s1) s2
Subst t2 = without (dom s2) s1
in Subst (t1 ++ t2)
combine :: Subst -> Subst -> Maybe Subst
combine s1@(Subst ve1) s2@(Subst ve2)
| clash s1 s2 = Nothing
| otherwise = Just $ toSubst $ nub $ ve1 ++ ve2
clash :: Subst -> Subst -> Bool
clash (Subst s1) (Subst s2)
= not $ null [ () | (v1, e1) <- s1, (v2, e2) <- s2, v1 == v2 && e1 /= e2 ]
lookupSubst :: VarIndex -> Subst -> Expr
lookupSubst v s = findSubstWithDefault (Var v) v s
findSubstWithDefault :: Expr -> VarIndex -> Subst -> Expr
findSubstWithDefault def v (Subst s) = case lookup v s of
Nothing -> def
Just e -> e
fmapSubst :: (Expr -> Expr) -> Subst -> Subst
fmapSubst f (Subst s) = Subst [ (v, f e) | (v, e) <- s ]
substSingle :: VarIndex -> Expr -> Expr -> Expr
substSingle v s e = subst (singleSubst v s) e
varSubst :: [VarIndex] -> [VarIndex] -> Expr -> Expr
varSubst xs ys e = subst (mkSubst xs (map Var ys)) e
isVariableRenaming :: Subst -> Bool
isVariableRenaming = all isVar . rng
isDetSubst :: Expr -> Subst -> Bool
isDetSubst e (Subst s) = all isDet s
where isDet (v, e') = isConstrTerm e' || count v (freeVarsDup e) <= 1
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
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)
|