| 1 |
4 |
tbourke |
(* $Id: clkexprtrans.sml 43 2008-05-09 06:44:05Z tbourke $ *)
|
| 2 |
|
|
|
| 3 |
|
|
(* TODO:
|
| 4 |
|
|
* - rename `paritition' to `constraint'
|
| 5 |
|
|
* more generally it is used to constrain the applicability of
|
| 6 |
|
|
* a set of transitions and is not affected by negation.
|
| 7 |
|
|
*)
|
| 8 |
|
|
|
| 9 |
|
|
structure ClkExprTrans :> CLK_EXPR_TRANS = let
|
| 10 |
|
|
structure E = Expression
|
| 11 |
|
|
and ClkE = ClockExpression
|
| 12 |
|
|
and AT = ActionTrans
|
| 13 |
|
|
and Env = Environment
|
| 14 |
|
|
and ECVT = ExpressionCvt
|
| 15 |
|
|
in struct
|
| 16 |
|
|
|
| 17 |
|
|
type expr = Expression.expr
|
| 18 |
|
|
and ty = Expression.ty
|
| 19 |
|
|
and symbol = Atom.atom
|
| 20 |
|
|
and symbolset = AtomSet.set
|
| 21 |
|
|
|
| 22 |
|
|
exception SelectIdConflictsWithForAll of symbol list * symbol list
|
| 23 |
|
|
|
| 24 |
18 |
tbourke |
infix <+ <- ++ <\ \ =:= ; open Symbol
|
| 25 |
4 |
tbourke |
|
| 26 |
|
|
(* Invariants:
|
| 27 |
|
|
- names contains all free names and those bound by select and forall
|
| 28 |
|
|
- the sets of names of the select and forall lists are disjoint
|
| 29 |
|
|
*)
|
| 30 |
|
|
datatype t = CETrans of {actselect: (symbol * E.ty) list,(* SelectSub ids *)
|
| 31 |
|
|
gselect: (symbol * E.ty) list,(* Guard selects *)
|
| 32 |
|
|
forall: (symbol * E.ty) list,(* in prenex form *)
|
| 33 |
|
|
partition: Expression.expr,
|
| 34 |
|
|
guard: ClkE.t,
|
| 35 |
|
|
action: ActionTrans.actionsub list,
|
| 36 |
|
|
names: symbolset}
|
| 37 |
|
|
|
| 38 |
|
|
fun addNameTypes xs = let
|
| 39 |
|
|
fun add ((n, _), s) = s <+ n
|
| 40 |
|
|
in foldl add emptyset xs end
|
| 41 |
|
|
|
| 42 |
|
|
local (*{{{1*)
|
| 43 |
|
|
fun showCheckedActions [] = ""
|
| 44 |
|
|
| showCheckedActions (AT.SelectSub s::es) = "[#" ^ Atom.toString s ^ "#]" ^
|
| 45 |
|
|
showCheckedActions es
|
| 46 |
|
|
| showCheckedActions (AT.FreeExprSub e::es) = "[" ^ ECVT.Expr.toString e ^ "]" ^
|
| 47 |
|
|
showCheckedActions es
|
| 48 |
|
|
fun symtyToStr (s, ty) = Atom.toString s ^ " : " ^ ECVT.Ty.toString ty
|
| 49 |
|
|
val symtyList = (ListFormat.fmt {init="(", sep=", ",
|
| 50 |
|
|
final=")", fmt=symtyToStr})
|
| 51 |
|
|
in (*}}}1*)
|
| 52 |
|
|
fun toString (CETrans {actselect, gselect, forall,
|
| 53 |
|
|
partition, guard, action, names}) = let
|
| 54 |
|
|
in
|
| 55 |
|
|
"{actselect: " ^ symtyList actselect ^ "\n" ^
|
| 56 |
|
|
" gselect: " ^ symtyList gselect ^ "\n" ^
|
| 57 |
|
|
" forall: " ^ symtyList forall ^ "\n" ^
|
| 58 |
|
|
" partition: " ^ ECVT.Expr.toString partition ^ "\n" ^
|
| 59 |
|
|
" guard: " ^ ClkE.toString guard ^ "\n" ^
|
| 60 |
|
|
" action: " ^ showCheckedActions action ^ "\n" ^
|
| 61 |
|
|
" names: " ^ AtomSet.foldl
|
| 62 |
|
|
(fn (s, str)=> str ^ " " ^ Atom.toString s) "" names ^
|
| 63 |
|
|
" }\n"
|
| 64 |
|
|
end
|
| 65 |
|
|
end (* local *)
|
| 66 |
|
|
|
| 67 |
|
|
(*
|
| 68 |
|
|
(*TODO: DEBUG ONLY! *)
|
| 69 |
|
|
fun showPartitions showitem partitions = let
|
| 70 |
|
|
fun showClass c = let in
|
| 71 |
|
|
TextIO.print " class[\n";
|
| 72 |
|
|
map showitem c;
|
| 73 |
|
|
TextIO.print " class]\n"
|
| 74 |
|
|
end
|
| 75 |
|
|
|
| 76 |
|
|
fun showPart p = let in
|
| 77 |
|
|
TextIO.print "partition[\n";
|
| 78 |
|
|
map showClass p;
|
| 79 |
|
|
TextIO.print "partition]\n"
|
| 80 |
|
|
end
|
| 81 |
|
|
in app showPart partitions end
|
| 82 |
|
|
*)
|
| 83 |
|
|
|
| 84 |
|
|
fun unionNames cetrans = let
|
| 85 |
|
|
fun f (CETrans {names, ...}, m) = m ++ names
|
| 86 |
|
|
in foldl f emptyset cetrans end
|
| 87 |
|
|
|
| 88 |
|
|
fun fromATrans preenv (AT.ActTrans {selectids=sellist,
|
| 89 |
|
|
actionsubs, guard, names}) =
|
| 90 |
|
|
let
|
| 91 |
|
|
val senv = List.foldl (Env.addId Env.SelectScope) preenv sellist
|
| 92 |
|
|
val (guard, forall, used) = ClkE.fromExpr (names, senv, guard)
|
| 93 |
|
|
(* Note: ClkE.fromExpr ensures:
|
| 94 |
|
|
* intersection(selectids, forall) = emptyset
|
| 95 |
|
|
* as names includes selectids.
|
| 96 |
|
|
*)
|
| 97 |
|
|
val actionNames = AT.addSelectSubNames actionsubs
|
| 98 |
|
|
|
| 99 |
|
|
fun categorize ([], acts, gs) = (acts, gs)
|
| 100 |
|
|
| categorize ((s as (n, _))::ss, acts, gs) =
|
| 101 |
|
|
if n <- actionNames then categorize (ss, s::acts, gs)
|
| 102 |
|
|
else categorize (ss, acts, s::gs)
|
| 103 |
|
|
|
| 104 |
|
|
val (actsels, gsels) = categorize (sellist, [], [])
|
| 105 |
|
|
in
|
| 106 |
|
|
CETrans {actselect=actsels,
|
| 107 |
|
|
gselect=gsels,
|
| 108 |
|
|
forall=forall,
|
| 109 |
|
|
partition=E.trueExpr,
|
| 110 |
|
|
guard=guard,
|
| 111 |
|
|
action=actionsubs,
|
| 112 |
|
|
names=used}
|
| 113 |
|
|
end
|
| 114 |
|
|
|
| 115 |
|
|
|
| 116 |
|
|
local (**)
|
| 117 |
43 |
tbourke |
fun toBoundId (s, ty) = E.BoundId (s, ty)
|
| 118 |
4 |
tbourke |
in (**)
|
| 119 |
|
|
fun toTrans (CETrans {actselect, gselect, forall,
|
| 120 |
|
|
partition, guard, action, ...}) = let
|
| 121 |
|
|
val preguard = ClkE.toExpr (guard, forall)
|
| 122 |
|
|
val guard = if E.equal (partition, E.trueExpr) then preguard
|
| 123 |
43 |
tbourke |
else E.BinBoolExpr {left=partition,bop=E.AndOp,right=preguard}
|
| 124 |
4 |
tbourke |
in
|
| 125 |
|
|
{selectids=map toBoundId (actselect @ gselect),
|
| 126 |
|
|
actionsubs=map AT.actionsubToExpr action,
|
| 127 |
|
|
guard=guard}
|
| 128 |
|
|
end
|
| 129 |
|
|
end (* local *)
|
| 130 |
|
|
|
| 131 |
|
|
fun rename (CETrans {actselect, gselect, forall, partition,
|
| 132 |
|
|
guard, action, names}, r as {old, new}) =
|
| 133 |
|
|
let
|
| 134 |
|
|
val _ = if new <- names
|
| 135 |
|
|
then raise Fail "rename: new name is already bound"
|
| 136 |
|
|
else ()
|
| 137 |
|
|
|
| 138 |
|
|
fun rsel (sv, sty) = if sv =:= old then (new, sty) else (sv, sty)
|
| 139 |
|
|
|
| 140 |
|
|
fun renameAction (a as AT.SelectSub n) = if n =:= old
|
| 141 |
|
|
then AT.SelectSub new else a
|
| 142 |
|
|
| renameAction (AT.FreeExprSub e) = AT.FreeExprSub (E.renameVar (r, e))
|
| 143 |
|
|
in
|
| 144 |
|
|
CETrans {actselect=map rsel actselect,
|
| 145 |
|
|
gselect=map rsel gselect,
|
| 146 |
|
|
forall=map rsel forall,
|
| 147 |
|
|
partition=E.renameVar (r, partition),
|
| 148 |
|
|
guard=ClkE.rename (r, guard),
|
| 149 |
|
|
action=map renameAction action,
|
| 150 |
18 |
tbourke |
names=(names <\ old) <+ new}
|
| 151 |
4 |
tbourke |
end
|
| 152 |
|
|
|
| 153 |
|
|
local (*{{{1*)
|
| 154 |
|
|
fun pairwiseIntersect ([], i) = i
|
| 155 |
|
|
| pairwiseIntersect (x::ys, i) = let
|
| 156 |
|
|
fun withEach (y,i) = AtomSet.union (AtomSet.intersection(x,y), i)
|
| 157 |
|
|
in pairwiseIntersect (ys, foldl withEach i ys) end
|
| 158 |
|
|
|
| 159 |
34 |
tbourke |
fun makeTrans cinv_fall
|
| 160 |
|
|
(actselects, potselects, potforalls, part, act)
|
| 161 |
|
|
andx =
|
| 162 |
|
|
let
|
| 163 |
4 |
tbourke |
fun combineAnd (t, e) = ClkE.And (e, ClkE.Term t)
|
| 164 |
|
|
fun makeAnd [] = ClkE.Term (ClkE.NonClock (E.falseExpr))
|
| 165 |
|
|
| makeAnd (x::xs) = List.foldl combineAnd (ClkE.Term x) xs
|
| 166 |
|
|
|
| 167 |
|
|
val e=makeAnd andx
|
| 168 |
|
|
val exprNames=ClkE.getFree e
|
| 169 |
34 |
tbourke |
val names=exprNames ++ AT.addActionNames act ++ addNameTypes cinv_fall
|
| 170 |
4 |
tbourke |
|
| 171 |
|
|
val forAlls=List.filter (fn (nm, _)=> nm<-exprNames) potforalls
|
| 172 |
34 |
tbourke |
in
|
| 173 |
|
|
(CETrans {actselect=actselects,
|
| 174 |
4 |
tbourke |
gselect=List.filter (fn (nm,_)=> nm<-names) potselects,
|
| 175 |
34 |
tbourke |
forall=forAlls @ cinv_fall,
|
| 176 |
4 |
tbourke |
partition=part,
|
| 177 |
|
|
guard=e,
|
| 178 |
|
|
action=act,
|
| 179 |
34 |
tbourke |
names=names},
|
| 180 |
|
|
addNameTypes (forAlls @ cinv_fall))
|
| 181 |
4 |
tbourke |
end
|
| 182 |
|
|
|
| 183 |
|
|
in (*}}}1*)
|
| 184 |
34 |
tbourke |
fun negate (cinv_fall, cinv)
|
| 185 |
|
|
(cet as CETrans {actselect, gselect, forall, guard,
|
| 186 |
|
|
partition, action, names}) =
|
| 187 |
19 |
tbourke |
let
|
| 188 |
|
|
val g' = ClkE.negate guard
|
| 189 |
34 |
tbourke |
val e = case cinv of
|
| 190 |
19 |
tbourke |
ClkE.Term (ClkE.NonClock (E.BoolCExpr true)) => g'
|
| 191 |
34 |
tbourke |
| _ => ClkE.And (cinv, g')
|
| 192 |
19 |
tbourke |
val dnf = ClkE.toDNF e
|
| 193 |
34 |
tbourke |
|
| 194 |
|
|
val _ = Util.debugVeryDetailed (fn()=>["* CETrans.negate before:\n",
|
| 195 |
|
|
toString cet])
|
| 196 |
|
|
|
| 197 |
|
|
(* Check the assumption of disjointness: *)
|
| 198 |
|
|
fun addAndCheck ((n, _), s) = if n <- s
|
| 199 |
|
|
then raise Fail "addDisjointForalls"
|
| 200 |
|
|
else s <+ n
|
| 201 |
|
|
val _ = foldl addAndCheck (ClkE.getFree g') cinv_fall
|
| 202 |
19 |
tbourke |
in
|
| 203 |
|
|
if ClkE.conflictExists (addNameTypes gselect, addNameTypes forall, dnf)
|
| 204 |
|
|
then raise SelectIdConflictsWithForAll (#1 (ListPair.unzip gselect),
|
| 205 |
|
|
#1 (ListPair.unzip forall))
|
| 206 |
|
|
else let
|
| 207 |
|
|
val (trans, fall)=ListPair.unzip (map
|
| 208 |
34 |
tbourke |
(makeTrans cinv_fall
|
| 209 |
|
|
(actselect, forall, gselect, partition, action))
|
| 210 |
|
|
dnf)
|
| 211 |
19 |
tbourke |
(* NB: forall and gselect are switched! (after having ensured
|
| 212 |
|
|
* names are disjoint, and `non-conflicting') *)
|
| 213 |
4 |
tbourke |
|
| 214 |
34 |
tbourke |
val _ = Util.debugVeryDetailed (fn()=>
|
| 215 |
|
|
"* CETrans.negate after (&& with invariant):\n"
|
| 216 |
|
|
::map toString trans)
|
| 217 |
|
|
|
| 218 |
19 |
tbourke |
val splitFAlls=pairwiseIntersect (fall, emptyset)
|
| 219 |
|
|
in
|
| 220 |
|
|
if AtomSet.isEmpty splitFAlls then trans
|
| 221 |
|
|
else (Util.warn ["forall bindings ",
|
| 222 |
|
|
ListFormat.fmt {init="(", sep=", ", final=")",
|
| 223 |
|
|
fmt=Atom.toString}
|
| 224 |
|
|
(AtomSet.listItems splitFAlls),
|
| 225 |
35 |
tbourke |
" are shared across disjuncts: possible split zones"];
|
| 226 |
19 |
tbourke |
[CETrans {actselect=actselect,
|
| 227 |
|
|
gselect=forall,
|
| 228 |
34 |
tbourke |
forall=gselect @ cinv_fall,
|
| 229 |
19 |
tbourke |
partition=partition,
|
| 230 |
|
|
guard=e,
|
| 231 |
|
|
action=action,
|
| 232 |
34 |
tbourke |
names=names ++ addNameTypes cinv_fall}])
|
| 233 |
19 |
tbourke |
(* An improvement would be to split where possible,
|
| 234 |
|
|
i.e. partition on shared foralls, transitive on overlap. *)
|
| 235 |
|
|
end
|
| 236 |
|
|
end
|
| 237 |
4 |
tbourke |
end (* local *)
|
| 238 |
|
|
|
| 239 |
|
|
local (*{{{1*)
|
| 240 |
|
|
fun equateActions ([], [], e) = e
|
| 241 |
|
|
| equateActions (AT.FreeExprSub e1::ss1, AT.FreeExprSub e2::ss2, e) =
|
| 242 |
|
|
if E.equal (e1, e2)
|
| 243 |
|
|
then equateActions (ss1, ss2, e)
|
| 244 |
|
|
else let
|
| 245 |
43 |
tbourke |
val eq = E.RelExpr {left=e1, rel=E.EqOp, right=e2}
|
| 246 |
4 |
tbourke |
val e' = if E.equal (e, E.trueExpr) then eq
|
| 247 |
43 |
tbourke |
else E.BinBoolExpr {left=e, bop=E.AndOp, right=eq}
|
| 248 |
4 |
tbourke |
in equateActions (ss1, ss2, e') end
|
| 249 |
|
|
|
| 250 |
|
|
| equateActions ((AT.SelectSub _)::ss1, (AT.SelectSub _)::ss2, e) =
|
| 251 |
|
|
equateActions (ss1, ss2, e)
|
| 252 |
|
|
|
| 253 |
|
|
| equateActions _ = raise Fail "equateActions: assumptions not met"
|
| 254 |
|
|
|
| 255 |
|
|
fun distinguishActions (ss1, ss2, e) = let
|
| 256 |
|
|
val ne = E.negate (equateActions (ss1, ss2, E.trueExpr))
|
| 257 |
|
|
in
|
| 258 |
|
|
if E.equal (e, E.trueExpr) then ne
|
| 259 |
43 |
tbourke |
else E.BinBoolExpr {left=e, bop=E.AndOp, right=ne}
|
| 260 |
4 |
tbourke |
end
|
| 261 |
|
|
|
| 262 |
|
|
fun equateClass (x::xs, e) = let
|
| 263 |
|
|
fun equate (x', e) = equateActions (x, x', e)
|
| 264 |
|
|
in foldl equate e xs end
|
| 265 |
|
|
|
| 266 |
|
|
(* Form an expression that equates all members of the same class.
|
| 267 |
|
|
e.g. given: [ [ [e1, e2], [f1, f2], [g1, g2] ],
|
| 268 |
|
|
[ [h1, h2], [i1, i2] ] ]
|
| 269 |
|
|
|
| 270 |
|
|
returns: (e1 == f1) && (e2 == f2)
|
| 271 |
|
|
&& (e1 == g1) && (e2 == g2)
|
| 272 |
|
|
&& (h1 == i1) && (h2 == i2) *)
|
| 273 |
|
|
val equateClasses = foldl equateClass E.trueExpr
|
| 274 |
|
|
|
| 275 |
|
|
(* Form an expression that distinguish classes across a partition,
|
| 276 |
|
|
e.g. given: [ [ [e1, e2], [f1, f2], [g1, g2] ],
|
| 277 |
|
|
[ [h1, h2], [i1, i2] ]
|
| 278 |
|
|
[ [j1, j2], [k1, k2] ] ]
|
| 279 |
|
|
|
| 280 |
|
|
returns: e && !((e1 == h1) && (e2 == h2))
|
| 281 |
|
|
&& !((e1 == j1) && (e2 == j2))
|
| 282 |
|
|
&& !((h1 == j1) && (h2 == j2)) *)
|
| 283 |
|
|
fun distinguishClasses ([c], e) = e
|
| 284 |
|
|
| distinguishClasses ((cRep::_)::cs, e) = let
|
| 285 |
|
|
fun distinguish (oRep::_, e) = distinguishActions (cRep, oRep, e)
|
| 286 |
|
|
in
|
| 287 |
|
|
distinguishClasses (cs, foldl distinguish e cs)
|
| 288 |
|
|
end
|
| 289 |
|
|
|
| 290 |
|
|
(* Given a partition of action subscripts, form an expression that
|
| 291 |
|
|
distinguishes the partition from others. *)
|
| 292 |
|
|
fun formPartitionExpr part = distinguishClasses (part, equateClasses part)
|
| 293 |
|
|
|
| 294 |
|
|
fun projActions partition = let
|
| 295 |
|
|
fun projAction (CETrans {action, ...}) = action
|
| 296 |
|
|
in map (fn class=> map projAction class) partition end
|
| 297 |
|
|
|
| 298 |
|
|
fun markPartition (CETrans {actselect, gselect, forall, partition,
|
| 299 |
|
|
guard, action, names}, partexpr) =
|
| 300 |
|
|
CETrans {actselect=actselect, gselect=gselect, forall=forall,
|
| 301 |
|
|
partition=partexpr, guard=guard, action=action, names=names}
|
| 302 |
|
|
|
| 303 |
|
|
fun renameConflicts ren args = let
|
| 304 |
|
|
(* args: names_to_check * [] * expr_to_rename_in * conflict_set * usednames *)
|
| 305 |
|
|
fun doit ([], done, g, conflicts, used) = (rev done, g, used)
|
| 306 |
|
|
| doit ((s as (n, ty))::ss, done, g, conflicts, used) =
|
| 307 |
|
|
if n <- conflicts
|
| 308 |
|
|
then let val n' = getNewName (n, used)
|
| 309 |
|
|
val g' = ren ({old=n, new=n'}, g)
|
| 310 |
|
|
in
|
| 311 |
|
|
doit (ss, (n', ty)::done, g', conflicts, used <+ n')
|
| 312 |
|
|
end
|
| 313 |
|
|
else doit (ss, s::done, g, conflicts, used)
|
| 314 |
|
|
in doit args end
|
| 315 |
|
|
|
| 316 |
|
|
fun mergeTrans partexpr
|
| 317 |
|
|
(CETrans {actselect=asel1, gselect=gsel1, forall=fa1,
|
| 318 |
|
|
partition=p1, guard=g1, action=act1, names=n1},
|
| 319 |
|
|
CETrans {actselect=asel2, gselect=gsel2, forall=fa2,
|
| 320 |
|
|
partition=p2, guard=g2, action=act2, names=n2}) =
|
| 321 |
|
|
let
|
| 322 |
|
|
val asel1names = addNameTypes asel1
|
| 323 |
|
|
val clash = asel1names ++ addNameTypes gsel1 ++ addNameTypes fa1
|
| 324 |
|
|
val (gsel2', g2', names') = renameConflicts ClkE.rename
|
| 325 |
|
|
(gsel2, [], g2, clash, n1++n2)
|
| 326 |
|
|
val (fa2', g2', names') = renameConflicts ClkE.rename
|
| 327 |
|
|
(fa2, [], g2', clash, names')
|
| 328 |
|
|
in
|
| 329 |
|
|
CETrans {actselect=asel1
|
| 330 |
|
|
@ List.filter (fn (n,_)=> not (n<- asel1names)) asel2,
|
| 331 |
|
|
(* probably unnecessary after ATrans.ensureConsistency *)
|
| 332 |
|
|
gselect=gsel1 @ gsel2',
|
| 333 |
|
|
forall=fa1 @ fa2',
|
| 334 |
|
|
partition=partexpr,
|
| 335 |
|
|
guard=ClkE.Or (g1, g2'),
|
| 336 |
|
|
action=act1,
|
| 337 |
|
|
names=names'}
|
| 338 |
|
|
end
|
| 339 |
|
|
|
| 340 |
|
|
in (*}}}1*)
|
| 341 |
|
|
(* Map each class in a partition to a single transition.
|
| 342 |
|
|
All transitions are stamped with the same partition expression *)
|
| 343 |
|
|
fun formPartitionReps [[]] = []
|
| 344 |
|
|
| formPartitionReps trpart = let
|
| 345 |
|
|
val partexpr = formPartitionExpr (projActions trpart)
|
| 346 |
|
|
val mergeT = mergeTrans partexpr
|
| 347 |
|
|
fun mergeClass (c::cs) = foldl mergeT (markPartition (c, partexpr)) cs
|
| 348 |
|
|
in map mergeClass trpart end
|
| 349 |
|
|
end (* local *)
|
| 350 |
|
|
|
| 351 |
|
|
end
|
| 352 |
|
|
end
|
| 353 |
|
|
|