[/] [trunk/] [src/] [maketest/] [maketest.sml] - Blame information for rev 18

Line No. Rev Author Line
1 4 tbourke
(* $Id: maketest.sml 18 2007-11-20 03:29:43Z tbourke $ *)
2
 
3
(* TODO:
4
 *  * Tidy this whole file up. Try to effect more structure and reuse.
5
 *    Shift generic functions into xml/nta parse/parsed-nta.
6
 *)
7
 
8
local structure P    = ParsedNta
9
            and E    = UppaalParse.Expression
10
            and ECvt = UppaalParse.ExpressionCvt
11
            and Env  = UppaalParse.Environment
12
            and TF   = TransitionFlipper
13
in
14
 
15
structure MakeTest : MAKE_TEST
16
=
17
struct
18
  (* shortcuts over Atom and AtomSet *)
19 18 tbourke
  infix <+ <- ++ <\ \ =:= ; open Symbol
20 4 tbourke
 
21
  exception InvalidChannelId of symbol
22
  exception SilentTransition
23
  exception NoChannels
24
  exception BadSubscriptType of string * string
25
                        (* channel name, subscript type *)
26
 
27
 
28
  fun validChannelId env id = let
29
      fun isChannel (E.CHANNEL _)     = true
30
        | isChannel (E.ARRAY (ty, _)) = isChannel ty
31
        | isChannel _                 = false
32
 
33
    in case Env.findValType env id
34
         of SOME ty => isChannel ty
35
          | NONE    => false
36
    end
37
 
38
  fun makeTransition (src as P.LocId s, dst as P.LocId d, sids, g, synco) = let
39
      val _ = Util.debugVeryDetailed (fn()=>["new transition: ",Int.toString s,
40
                                             "->", Int.toString d, " "])
41
    in
42
          P.Transition {id=NONE, source=src, target=dst, select=(sids, NONE),
43
                        guard=(g, NONE), sync=(synco, NONE),
44
                        update=([], NONE), comments=(NONE, NONE),
45
                        position=NONE, color=Settings.errorColor(), nails=[]}
46
    end
47
 
48
  fun formInvariantTrans (errloc, locs) = let
49
      fun doLoc (P.Location {id, invariant=(e, _), ...}) =
50
          if E.equal (e, E.trueExpr)
51
          then NONE
52
          else SOME (makeTransition (id, errloc, [], E.negate e, NONE))
53
                (* After negation, we could consider splitting disjunct/clock
54
                 * combinations over multiple transitions... *)
55
    in List.mapPartial doLoc locs end
56
 
57
  fun formInverseTrans (errloc, env, chans) (locs, trans) = let
58
 
59
      fun filterChannels chanId = let
60
          fun f (P.Transition {sync=(NONE,_), ...},_) = raise SilentTransition
61
            | f (t as P.Transition {sync=(SOME (c,d,acts),_),
62
                                    select=(sids, _),
63
                                    guard=(guard, _), ...}, r as (is, os)) =
64
              if c =:= chanId
65
              then let val tr = {selectids=sids, actionsubs=acts, guard=guard}
66
                   in case d
67
                      of E.Input  => (tr::is, os)
68
                       | E.Output => (is, tr::os)
69
                   end
70
              else r
71
        in foldl f ([], []) end
72
 
73
      fun flipFailed (P.Location {id=P.LocId l, name=(nameo, _), ...},
74
                      chanId, errMsg) =
75
        let
76
          val name = case nameo of
77
                       NONE   => ("unnamed state (id" ^ Int.toString l ^ ")")
78
                     | SOME n => n
79
        in
80
          Util.warn  [errMsg];
81
          Util.abort ["while processing transitions on channel ",
82
                      Atom.toString chanId, " from ", name]
83
        end
84
 
85
      fun doLoc (location as P.Location {id=loc as P.LocId l,
86
                                         invariant=(inv, _), ...}) = let
87
 
88
          val _ =Util.debugIndent (Settings.Detailed,
89
                                fn()=>["processing location:", Int.toString l])
90
 
91
          fun makeTrans (chanId, dir) {selectids, actionsubs, guard} =
92
              if E.equal (guard, E.falseExpr)
93
              then NONE
94
              else let
95
                     val sync = SOME (chanId,  dir, actionsubs)
96
                   in
97
                     SOME (makeTransition (loc,errloc,selectids,guard,sync))
98
                   end
99
 
100
          fun flipChannel trans chanId = let
101
              val _ =Util.debugIndent(Settings.Detailed,
102
                                      fn()=>["chanId:",Atom.toString chanId])
103
              val acttys = valOf (TF.chanToSubRanges (env, chanId))
104
              fun negate trs = (TF.negateTransitions env (acttys, trs, inv))
105
 
106
              val (is, os) = filterChannels chanId trans
107
            in (List.mapPartial (makeTrans (chanId, E.Output))   (negate is))
108
                @ (List.mapPartial (makeTrans (chanId, E.Input)) (negate os))
109
               before (Util.debugOutdent (Settings.Detailed, fn()=>[]))
110
            end
111
            handle TF.FlipFailed s => flipFailed (location, chanId, s)
112
 
113
          fun startsHere (P.Transition {source=P.LocId s, ...}) = (s = l)
114
          val ts = List.filter startsHere trans
115
        in
116
          List.concat (map (flipChannel ts) chans)
117
          before (Util.debugOutdent (Settings.Detailed, fn()=>[]))
118
        end
119
 
120
    in List.concat (map doLoc locs) end
121
 
122
  fun channelSet transitions = let
123
      fun addSync (P.Transition {sync=(NONE, _), ...}          , s) = s
124
        | addSync (P.Transition {sync=(SOME (c, _, _), _), ...}, s) = s <+ c
125
    in foldl addSync AtomSet.empty transitions end
126
 
127
  fun channels (P.Template {transitions, ...})
128
          = AtomSet.listItems (channelSet transitions)
129
 
130 17 tbourke
  fun invertActionAndAddInvariant (env, invmap, dontFlipSet)
131 4 tbourke
      (P.Transition {id, source=source as P.LocId src, target,
132
                     guard=(g, gpos), sync=(sync, syncpos),
133
                     select, update, comments, position, color, nails}) = let
134
      val inv = valOf (IntBinaryMap.find (invmap, src))
135
      val g' = if E.equal (inv, E.trueExpr)
136
               then g
137
               else if E.equal (g, E.trueExpr)
138
                    then inv
139
                    else let
140
                           val simplified = TF.andexpr env (g, inv)
141
                         in case simplified of
142
                              (E.BinBoolExpr{bop=E.OrOp,...})=>E.andexpr(inv,g)
143
                            | _ => simplified
144
                         end
145
                         (* Not so tidy...
146
                          * TF.andexpr runs the whole shebang through
147
                          * fromDNF (toDNF _) which has the advantage of
148
                          * simplifying some invariant and guard
149
                          * combinations, but if the result involves
150
                          * ORs and clock variables, it may have to be split
151
                          * across multiple transitions, which we would rather
152
                          * avoid by using E.andexpr. *)
153
 
154
      val sync' = case sync of
155 17 tbourke
                    NONE             => NONE
156
                  | SOME (s,dir,idx) => if s <- dontFlipSet then sync
157
                                        else SOME (s,E.otherDirection dir,idx)
158 4 tbourke
    in
159
      P.Transition {id=id, source=source, target=target,
160
                    guard=(g', gpos), sync=(sync', syncpos),
161
                    select=select, update=update, comments=comments,
162
                    position=position, color=color, nails=nails}
163
    end
164
 
165
  fun stripInvariant (l as P.Location {id, position, color, name,
166
                                       invariant=(inv, invpos), comments,
167
                                       urgent, committed}) =
168
    if E.equal(inv, E.trueExpr)
169
    then l
170
    else P.Location {id=id, position=position, color=color, name=name,
171
                     invariant=(E.trueExpr, NONE), comments=comments,
172
                     urgent=urgent, committed=committed}
173
 
174
  fun maketest (channelIds,
175
                t as P.Template {declaration, locations, transitions, ...}) =
176
    let
177
      val _ = if null channelIds then raise NoChannels else ()
178
 
179
      val _ = case List.find (fn c=>not (validChannelId declaration c))
180
                  channelIds of
181
                 NONE => ()
182
               | SOME c=> raise InvalidChannelId c
183
 
184
      val cIdGiven = foldl AtomSet.add' AtomSet.empty channelIds
185
      val cIdUsed  = channelSet transitions
186
      val cMissing = AtomSet.difference (cIdUsed, cIdGiven)
187 17 tbourke
      val _ = if AtomSet.isEmpty cMissing then ()
188
              else Util.warn ("channels are missing from the testing list: "::
189
                              (map Atom.toString (AtomSet.listItems cMissing)))
190 4 tbourke
      val (tplate',err as P.Location {id=errLocId,...})= P.Location.new t "Err"
191
      val err = P.Location.updColor err (Settings.errorColor ())
192
 
193
      fun keyInv (P.Location {id=P.LocId i,invariant=(inv, _), ...}) = (i,inv)
194
      fun insertInv (l, m) = IntBinaryMap.insert' (keyInv l, m)
195
      val invMap = foldl insertInv IntBinaryMap.empty locations
196
 
197
      val invarianttrans = formInvariantTrans (errLocId, locations)
198
      val fliptrans = formInverseTrans (errLocId, declaration, channelIds)
199
                                       (locations, transitions)
200 17 tbourke
      val normtrans = map (invertActionAndAddInvariant
201
                           (declaration, invMap, cMissing)) transitions
202 4 tbourke
 
203
      (* Not quite per the Stoelinga definition. *)
204
      val errorloop = [P.Transition {id=NONE, source=errLocId, target=errLocId,
205
                                     select=([],NONE), guard=(E.trueExpr,NONE),
206
                                     sync=(NONE,NONE), update=([],NONE),
207
                                     comments=(NONE,NONE),   position=NONE,
208
                                     color=Settings.errorColor(), nails=[]}]
209
    in
210
      (errLocId, P.Location.map stripInvariant
211
                   (P.Template.updTransitions tplate'
212
                      (List.concat [invarianttrans, fliptrans,
213
                                    normtrans, errorloop])))
214
    end
215
 
216
  (********** Jensen extensions: ****************************************)
217
  (* This section was written very quickly. It would probably benefit from
218
   * restructuring and refactoring *)
219
 
220
  fun makeComparison map = let
221
    (* makeComparison env map expr
222
     *
223
     * Return an expression, for all v_i in domain(map):
224
     *    /\  (v_i == map(v_i))
225
     *      i
226
     *
227
     * This is the negation of the expression used in Jensen's expansion because
228
     * we add it as an invariant before `flipping'.
229
     *)
230
      fun doPair (v, v', e) = E.orexpr (e,
231
          E.RelExpr {left= E.VarExpr (E.SimpleVar (v, E.nopos)),
232
                     rel=E.NeOp,
233
                     right=E.VarExpr (E.SimpleVar (v', E.nopos)),
234
                     pos=E.nopos})
235
 
236
    in AtomMap.foldli doPair E.falseExpr map end
237
 
238
  fun updateEffects subst
239
        (P.Transition {id, source, target, select=(sels, selP),
240
                       guard=(g, gP), sync=(syn, synP), update=(upd, updP),
241
                       comments, position, color, nails}) =
242
    let
243
      fun drop (E.BoundId (nm, _, _), m) = #1 (AtomMap.remove (m, nm))
244
                                           handle LibBase.NotFound => m
245
      val doSubst = E.renameVars (foldl drop subst sels)
246
      fun doSync (nm, dir, subs) = (nm, dir, map doSubst subs)
247
    in
248
      P.Transition {id=id,
249
                    source=source,
250
                    target=target,
251
                    select=(sels, selP),
252
                    guard=(doSubst g, gP),
253
                    sync=(Option.map doSync syn, synP),
254
                    update=(map doSubst upd, updP),
255
                    comments=comments,
256
                    position=position,
257
                    color=color,
258
                    nails=nails}
259
    end
260
 
261
  fun substituteInvariant subst
262
     (P.Location {id, position, color, name, invariant=(inv, invP),
263
                  comments, urgent, committed}) =
264
      P.Location {id=id, position=position, color=color, name=name,
265
                  invariant=(E.renameVars subst inv, invP),
266
                  comments=comments, urgent=urgent, committed=committed}
267
 
268
  fun makeCheckTrans (guard, locs) dstLoc = let
269
      fun f (P.Location {id, ...}) = P.Transition {id=NONE,
270
                                       source=id, target=dstLoc,
271
                                       select=([], NONE), guard=(guard, NONE),
272
                                       sync=(NONE, NONE), update=([], NONE),
273
                                       comments=(NONE,NONE), position=NONE,
274
                                       color=Settings.errorColor(), nails=[]}
275
    in map f locs end
276
 
277
  fun makeVarCheckTrans vars (t as P.Template {name, parameter, initial,
278
                        declaration=decl, locations=locs, transitions=trans}) =
279
    let
280
      val (dupmap, decl') = Env.dupVariables vars (decl,SOME Env.TemplateScope)
281
    in
282
      if AtomMap.isEmpty dupmap then (t, fn _ =>[])
283
      else (P.Template {name=name, parameter=parameter, declaration=decl',
284
                        initial=initial,
285
                        locations=map (substituteInvariant dupmap) locs,
286
                        transitions=map (updateEffects dupmap) trans},
287
            makeCheckTrans (makeComparison dupmap, locs))
288
    end
289
 
290
  local
291
    exception BadSub of string
292
 
293
    val maxIntRange = (~32768, 32767)
294
    datatype index_var = ScalarIdx of symbol * E.unique
295
                       | IntIdx    of int * int
296
                       | IntExp    of E.expr * E.expr
297
 
298
    fun usable (ScalarIdx (_, q1), ScalarIdx (_, q2)) = (q1=q2)
299
      | usable (IntIdx (l1,u1), IntIdx (l2,u2))       = u1 < u2 andalso l1 > l2
300
      | usable (IntExp (l1,u1), IntExp (l2,u2))       = E.equal (l1,l2) andalso
301
                                                        E.equal (u1,u2)
302
      | usable _                                      = false
303
 
304
    fun typeToIndex (E.INT (NONE, _))        = IntIdx maxIntRange
305
      | typeToIndex (E.INT (SOME (E.IntCExpr l,E.IntCExpr u),_)) = IntIdx (l,u)
306
      | typeToIndex (E.INT (SOME (l, u), _)) = IntExp (l, u)
307
      | typeToIndex (E.NAME (nm, _, SOME (E.SCALAR (_, _, uniq))))
308
                                             = ScalarIdx (nm, uniq)
309
      | typeToIndex ty = raise BadSub (ECvt.Ty.toString ty)
310
      (* All scalars must be given as typedef-ed names (otherwise they
311
       * can't be redeclared). *)
312
 
313
    (* Could these be meta variables? *)
314
    fun indexToType (ScalarIdx (nm, uniq))    = E.NAME (nm, E.NoQual, NONE)
315
      | indexToType (IntIdx (l, u)) = E.INT (if (l,u)=maxIntRange then NONE
316
                                             else SOME (E.IntCExpr l,
317
                                                        E.IntCExpr u),E.NoQual)
318
      | indexToType (IntExp bounds) = E.INT (SOME bounds, E.NoQual)
319
 
320
    (* try to find variable in pool capable of covering reqty,
321
     * otherwise add a new one to vars/usednames. *)
322
    fun findVar (vars, pool, usednames, reqty) = let
323
        fun f (unused, []) = let val n = getNewName (Atom.atom "i",usednames)
324
                             in (n, (n,reqty)::vars, unused, usednames<+ n) end
325
          | f (unused, (n, ty)::vs) = if usable (ty, reqty)
326
                                      then (n, vars, unused @ vs, usednames)
327
                                      else f ((n,ty)::unused, vs)
328
          (* Ideally the narrowest such match would be sought... *)
329
      in f ([], pool) end
330
 
331
    (* try to find variables in pool capabale of covering all of
332
     * the given subscript types, adding them if necessary *)
333
    fun allocVars (vars,pool,usednames,subs,[]) = (vars, usednames, rev subs)
334
      | allocVars (vars,pool,usednames,subs,s::ss) = let
335
            val (nm,vars',pool',usednames') = findVar (vars,pool,
336
                                                       usednames,typeToIndex s)
337
          in allocVars (vars',pool',usednames',nm::subs,ss) end
338
 
339
    (* map a list of name/subscript-type pairs to a list of
340
     * name/index-variable pairs, updating the environment as appropriate *)
341
    fun allocateIndexVariables (env, chansAndTypes) = let
342
        fun f (vars, [], usednames, done) = (vars, done)
343
          | f (vars, (nm, subtys)::chans, usednames, done) = let
344
                val (vars', usednames', idxvars) = allocVars (vars,vars,
345
                                                              usednames,[],
346
                                                              subtys)
347
              in f (vars', chans, usednames', (nm, idxvars)::done) end
348
              handle BadSub ty => raise BadSubscriptType (Atom.toString nm, ty)
349
 
350
        fun addId ((nm, ity), env) = Env.addId Env.TemplateScope
351
                                               ((nm, indexToType ity), env)
352
        val (idxvars, chanList) = f ([], chansAndTypes, Env.usedIds env, [])
353
 
354
      in (foldl addId env idxvars, chanList) end
355
 
356
    fun newLoc (id, name, pos) = P.Location {id       =id,
357
                                             position =pos,
358
                                             color  =Settings.urgChanLocColor(),
359
                                             name     =(SOME name, NONE),
360
                                             invariant=(E.trueExpr, NONE),
361
                                             comments =(NONE, NONE),
362
                                             urgent   =false,
363
                                             committed=false}
364
 
365
    (* For each urgent channel:
366
     *  -create states l_c_in, l_c_out
367
     *  -connect them to l_u   (deadend state)
368
     *  -connect them to error (if a delay occurs)
369
     *
370
     * Produce a map from channel set names to:
371
     *   (input l_u_i, output l_u_i, index variables + subs)
372
     *)
373
    fun addUrgentLocs (errId, luId, clkNotZero, nextid, locnames, urgchans) =
374
      let
375
        fun f (locs, trans, cmap, nid, locNms, []) = (locs, trans, cmap,
376
                                                         nid, locNms)
377
          | f (locs, trans, cmap, nid, locNms, ((nm, subs), dir)::cs) = let
378
 
379
                val suffix = case dir of E.Input  => "_in" | E.Output => "_out"
380
 
381
                val locId = P.LocId nid
382
                val locNm = getNewName (Atom.atom (String.concat
383
                                ["l_", Atom.toString nm, suffix]), locNms)
384
                val loc = newLoc (locId, Atom.toString locNm, NONE)
385
 
386
                val sync=(nm, dir,
387
                          map (fn v=>E.VarExpr(E.SimpleVar (v,E.nopos))) subs)
388
                val to_lu = P.Transition {id=NONE,
389
                                          source=locId,
390
                                          target=luId,
391
                                          select=([], NONE),
392
                                          guard=(E.trueExpr, NONE),
393
                                          sync=(SOME sync, NONE),
394
                                          update=([], NONE),
395
                                          comments=(NONE,NONE),
396
                                          position=NONE,
397
                                          color=Settings.urgChanLocColor(),
398
                                          nails=[]}
399
 
400
                val to_err = P.Transition {id=NONE,
401
                                           source=locId,
402
                                           target=errId,
403
                                           select=([], NONE),
404
                                           guard=(clkNotZero, NONE),
405
                                           sync=(NONE, NONE),
406
                                           update=([], NONE),
407
                                           comments=(NONE, NONE),
408
                                           position=NONE,
409
                                           color=Settings.urgChanLocColor(),
410
                                           nails=[]}
411
 
412
                val {input,output,...} = Option.getOpt (AtomMap.find (cmap,nm),
413
                                        {input=errId, output=errId, subs=subs})
414
                val cmap = case dir of
415
                      E.Input  => AtomMap.insert (cmap, nm,
416
                                      {input=locId, output=output, subs=subs})
417
                    | E.Output => AtomMap.insert (cmap, nm,
418
                                      {input=input, output=locId, subs=subs})
419
              in
420
                f (loc::locs,
421
                   to_lu::to_err::trans,
422
                   cmap,
423
                   nid + 1,
424
                   locNms <+ locNm,
425
                   cs)
426
              end
427
      in f ([], [], AtomMap.empty, nextid, locnames, urgchans) end
428
 
429
    fun makeAssign (var, expr) = E.AssignExpr {
430
                                    var=E.VarExpr (E.SimpleVar (var, E.nopos)),
431
                                    aop=E.AssignOp, expr=expr, pos=E.nopos}
432
 
433
    fun remapDestination locMap (t as P.Transition {target=P.LocId dst, id,
434
                                          source, select, guard, sync, update,
435
                                          comments, position, color, nails}) =
436
        case IntBinaryMap.find (locMap, dst) of
437
          NONE => t
438
        | SOME dst' => P.Transition {target=P.LocId dst', id=id,
439
                                     source=source, select=select, guard=guard,
440
                                     sync=sync, update=update,
441
                                     comments=comments, position=position,
442
                                     color=color, nails=nails}
443
 
444
    (* Split any location that has outgoing transitions on urgent channels
445
     * into two parts: l, l_tau. Adding transitions:
446
     *    l -->  l_tau
447
     *    l -->* l_u_i
448
     *)
449
        (*val (slocs, strans, nextid, locNms) *)
450
    fun splitLocs (urgmap, nextid, clkReset, locNms, locs, trans) = let
451
 
452
        fun justUrgTransFromLoc id (t as P.Transition{source,sync=(syn,_),...})=
453
            source=id
454
            andalso case syn of
455
                      NONE => false
456
                    | SOME (nm,_,_) => Option.isSome (AtomMap.find (urgmap,nm))
457
 
458
        fun toUrgent src (P.Transition {sync=(NONE,_),...}) =
459
                                              raise Fail "toUrgent: bad call"
460
          | toUrgent src (P.Transition {select=(sel, _), guard=(g, _),
461
                                        sync=(SOME (c, dir, subs), _), ...}) =
462
          let
463
            val {input=inDst, output=outDst, subs=idxVars}
464
                                            = valOf (AtomMap.find (urgmap, c))
465
            val cacheIndexes = ListPair.map makeAssign (idxVars, subs)
466
          in
467
            UppaalParse.removeUnusedSelectIds
468
              (P.Transition {id=NONE,
469
                             source=src,
470
                             target=case dir of E.Input=>inDst | E.Output=>outDst,
471
                             select=(sel, NONE),
472
                             guard=(g, NONE),
473
                             sync=(NONE, NONE),
474
                             update=(clkReset::cacheIndexes, NONE),
475
                             comments=(NONE, NONE),
476
                             position=NONE,
477
                             color=Settings.urgChanLocColor(),
478
                             nails=[]})
479
          end
480
 
481
        fun splitLoc (nextid, locNms, locMap,
482
                      l as P.Location {id=lId as P.LocId lIdInt,
483
                                       name=(nm, _), position=lpos, ...}) =
484
            case List.filter (justUrgTransFromLoc lId) trans of
485
              [] => (nextid, locNms, locMap, ([l], []))
486
            | urgtrans => let
487
                  val prelId = P.LocId nextid
488
                  val prelNm = getNewName (Atom.atom
489
                                  (Option.getOpt (nm, "l") ^ "_u"), locNms)
490
 
491
                  val (shift_o, shift_u) = (Settings.splitShiftOld(),
492
                                            Settings.splitShiftNew())
493
                  val prel_pos = if isSome shift_o orelse isSome shift_u
494
                                 then lpos else NONE
495
 
496
                  val prel = newLoc (prelId, Atom.toString prelNm, prel_pos)
497
 
498
                  val tauT = P.Transition {id=NONE,
499
                                           source=prelId,
500
                                           target=lId,
501
                                           select=([], NONE),
502
                                           guard=(E.trueExpr, NONE),
503
                                           sync=(NONE, NONE),
504
                                           update=([], NONE),
505
                                           comments=(NONE, NONE),
506
                                           position=NONE,
507
                                           color=Settings.urgChanLocColor(),
508
                                           nails=[]}
509
 
510
                  val locMap' = IntBinaryMap.insert (locMap, lIdInt, nextid)
511
                  val utrans = map (toUrgent prelId) urgtrans
512
 
513
                  val l_sh=P.Location.shift (Settings.splitShiftOld()) l
514
                  val prel_sh=P.Location.shift(Settings.splitShiftNew()) prel
515
                in
516
                  (nextid+1, locNms <+ prelNm,
517
                   locMap', ([prel_sh,l_sh], tauT::utrans))
518
                end
519
 
520
        fun f (nextid, locNms, locMap, done, []) = (locMap, done)
521
          | f (nextid, locNms, locMap, done, l::ls) = let
522
            val (nextid', locNms', locMap', new) = splitLoc (nextid, locNms,
523
                                                             locMap, l)
524
          in f (nextid', locNms', locMap', new::done, ls) end
525
 
526
        fun concatPair (f, s, (rf, rs)) = (f @ rf, s @ rs)
527
 
528
        val (locMap, locs') = f (nextid, locNms, IntBinaryMap.empty, [], locs)
529
      in
530
        (locMap,
531
         ListPair.foldl concatPair ([], map (remapDestination locMap) trans)
532
                                   (ListPair.unzip locs'))
533
      end
534
  in
535
 
536
  fun addReadyChecks channels (errId, t as P.Template{name,parameter, initial,
537
                                                      declaration=decls,
538
                                                      locations=locs,
539
                                                      transitions=trans}) =
540
    let
541
      fun checkType (subs, E.CHANNEL{urgent=true, ...}) = SOME (rev subs)
542
        | checkType (subs, E.ARRAY(inner,E.Type s)) = checkType (s::subs,inner)
543
        | checkType (subs, E.NAME (_, _, SOME ty))  = checkType (subs, ty)
544
        | checkType _                               = NONE
545
 
546
      fun chanTypeToSubs c = Option.map (fn subs=>(c,subs))
547
                               (Option.mapPartial (fn t=> checkType ([], t))
548
                                                  (Env.findValType decls c))
549
 
550
      val (errTrans, nonErrTrans) = List.partition
551
                        (fn (P.Transition {target,...})=>target=errId) trans
552
 
553
      fun addUsedDirections (nm, subs) = let
554
          fun justDir (P.Transition {sync=(SOME (nm', dir, _), _),...}) =
555
                         if nm =:= nm' then SOME dir else NONE
556
            | justDir _ = NONE
557
          val dirs = List.mapPartial justDir nonErrTrans
558
          fun hasDir dir = if List.exists (fn d => d=dir) dirs
559
                           then [((nm, subs), dir)] else []
560
        in (hasDir E.Input) @ (hasDir E.Output) end
561
 
562
      val urgchans = List.mapPartial chanTypeToSubs channels
563
      val (decls, urgchanIdxs) = allocateIndexVariables (decls, urgchans)
564
      val urgchans = List.concat (map addUsedDirections urgchanIdxs)
565
    in
566
      if null urgchans then t
567
      else let
568
        val locNames = foldl (fn (P.Location{name=(SOME nm,_),...},s)
569
                                          => s <+ Atom.atom nm
570
                               | (_, s) => s) emptyset locs
571
        val (P.LocId nextid) = P.Location.newId t
572
 
573
        val (l_u_id, nextid) = (P.LocId nextid, nextid + 1)
574
        val l_u_nm = getNewName (Atom.atom "l_u", locNames)
575
        val l_u = newLoc (l_u_id, Atom.toString l_u_nm, NONE)
576
 
577
        (* Use any other clock, or add a new one *)
578
        fun findClock (nm, Env.VarEntry {ty=E.CLOCK, ref=false, ...}) = SOME nm
579
          | findClock _ = NONE
580
        val (urgclock, decls) = case Env.mapValues findClock decls of
581
              [] => let val nclk = Env.newId (Atom.atom "x_u", decls)
582
                    in
583
                      (nclk, Env.addId Env.TemplateScope((nclk,E.CLOCK),decls))
584
                    end
585
            | c::_ => (c, decls)
586
 
587
        val clkNotZero = E.RelExpr {
588
                            left=E.VarExpr (E.SimpleVar (urgclock, E.nopos)),
589
                            rel=E.GtOp,
590
                            right=E.IntCExpr 0, pos=E.nopos}
591
        val clkReset = E.AssignExpr {
592
                          var=E.VarExpr (E.SimpleVar (urgclock, E.nopos)),
593
                          aop=E.AssignOp, expr=E.IntCExpr 0, pos=E.nopos}
594
 
595
        val (ulocs, utrans, urgmap, nextid, locNames) = addUrgentLocs (errId,
596
                      l_u_id, clkNotZero, nextid, locNames <+ l_u_nm, urgchans)
597
 
598
        val (errLoc, nonErrLocs) = List.partition
599
                                     (fn (P.Location{id,...})=>id=errId) locs
600
        val (locMap, (slocs, strans)) = splitLocs (urgmap,nextid, clkReset,
601
                                             locNames, nonErrLocs, nonErrTrans)
602
 
603
        fun shiftInitial (P.LocId i) = P.LocId
604
                             (Option.getOpt (IntBinaryMap.find (locMap, i), i))
605
      in
606
        P.Template {name=name,
607
                    parameter=parameter,
608
                    initial=Option.map shiftInitial initial,
609
                    declaration=decls,
610
                    locations=l_u::List.concat [ulocs, slocs, errLoc],
611
                    transitions=List.concat [utrans, strans, errTrans]}
612
      end
613
    end
614
  end (* local *)
615
 
616
end
617
end (* local *)
618