[/] [trunk/] [src/] [maketest/] [transitionflipper.sml] - Rev 21

(* $Id: transitionflipper.sml 21 2007-12-04 12:38:09Z tbourke $ *)

(*TODO:
  * Check whether duplicate selectids can muck things up.
  * Remove unused select ids from final transitions (or initial transitions?).
    fun removeEarlierDuplicates l = let (* there can be only one *)
        fun p s (s', _) = not (s =:= s')
        fun f []                  = []
          | f ((x as (s, _))::xs) = x :: f (List.filter (p s) xs)
      in rev (f (rev l)) end

    fun unusedId used (E.BoundId (s, ty, _)) = if s <- used
                                               then SOME (s, ty) else NONE

    val sellist = removeEarlierDuplicates
                    (List.mapPartial (unusedId (!usednames)) presellist)

  * What happens if we call negate(negate tr)?
    Should this be forbidden by using types?

  * Should check whether any actselect indices are used in guard terms in a way
    that will create problems (restrict how actions are selected?).
      e.g. select i . (i > 4) / a[i]
                  ||negate
                  \/
           select i . (i <= r) / a[i]
     But how would this combine with other transitions? Does it work?
     This must first be understood properly.

  * What about action selectids with non-zero lower bounds?
        chan c[6];
        select i : int[2,5] . true / c[i]

    Either forbid, or the negation set has to include:
        select i : int [0,1] . true / c[i]

  * Need a mapping back from scalar sets to their typedef-ed names (possibly via
    uniqueid lookup?, or don't expand to begin with (would this break anything
    else?). See tests/testflip13, the selectid should come from 'S', not
    scalar[5].

 *)

structure TransitionFlipper :> TRANSITION_FLIPPER = let
  structure E       = Expression
        and Env     = Environment
        and ECVT    = ExpressionCvt
        and ATrans  = ActionTrans
        and ClkE    = ClockExpression
        and CETrans = ClkExprTrans
in struct
  exception FlipFailed of string

  (* shortcuts over Atom and AtomSet *)
  infix <+ <- ++ <\ \ =:= ; open Symbol

  type t = {selectids:  E.boundid list,
            actionsubs: E.expr list,
            guard:      E.expr}

  fun toString {selectids, actionsubs, guard} = let
      fun showActions []      = ""
        | showActions (e::es) = "[" ^ ECVT.Expr.toString e ^"]"^ showActions es

      fun boundToStr (E.BoundId (s, ty, _)) = Atom.toString s ^
                                              " : " ^ ECVT.Ty.toString ty
      val bindingList = (ListFormat.fmt {init="(", sep=", ",
                                         final=")", fmt=boundToStr})
    in
      "{select "  ^ bindingList selectids    ^ "\n"  ^
      " action: " ^ showActions actionsubs   ^ "\n " ^
                    ECVT.Expr.toString guard ^ " }"  ^ "\n"
    end

  fun andexpr env (e1, e2) = let
      val (ce1, ce1fa, used) = ClkE.fromExpr (emptyset, env, e1)
      val (ce2, ce2fa, used) = ClkE.fromExpr (used, env, e2)
    in
      ClkE.toExpr (ClkE.andexpr (ce1, ce2), ce1fa @ ce2fa)
    end

  fun negateTransitions env (subtypes, trans : t list, invariant) = let
      val _ = Util.debugSect (Settings.Outline, fn ()=>"negateTransitions:\n"
                                                       ::map toString trans)

      (* 1. Group like subscripts; rename subSelectIds; make others unique. *)
      val atrans = ATrans.ensureConsistency
                     (map (ATrans.fromTrans subtypes) trans)
      val otherATrans = ATrans.coverMissingChannels (subtypes, atrans,
                                                     invariant)
      val cetrans = map (CETrans.fromATrans env) atrans
      
      val _ = Util.debugSubsect (Settings.Outline,
                        fn ()=>"cover missing channels:\n"
                               ::map ATrans.toString otherATrans)

      (* 2. Paritition remaining transitions. *)
      val partitions = Partitions.makeList cetrans

      (* 3. Form partition expressions: just over freeSubscripts
            Grouping back into a single list of transitions. *)
      val cetrans = List.concat (List.map CETrans.formPartitionReps partitions)

      val _ = Util.debugSubsect (Settings.Detailed,
                fn ()=>"with partition reps:\n"::map CETrans.toString cetrans)

      (* 4. Convert the location invariant into a clock expression
       *    (this piece was hacked in later and could be tightened up. *)
      val (cinv, cinv_fall, _) = ClkE.fromExpr (CETrans.unionNames cetrans,
                                                env, invariant)
      val cetrans = if null cinv_fall then cetrans
                    else List.map (CETrans.addDisjointForalls cinv_fall)
                                  cetrans

      (* 5. Negate each, concatenating results. *)
      val ncetrans = List.concat (List.map (CETrans.negate cinv) cetrans)

      val _ = Util.debugSubsect (Settings.Detailed, fn ()=>"after negation:\n"
                                              ::map CETrans.toString ncetrans)

    in map ATrans.toTrans otherATrans @ map CETrans.toTrans ncetrans end
      handle CETrans.SelectIdConflictsWithForAll (sel, forall) => let
                fun showSym n = ListFormat.fmt {init=n ^ "(", final=")",
                                                sep=", ", fmt=Atom.toString}
              in
               raise FlipFailed ("select/forall conflict, " ^
                                 showSym "select" sel       ^
                                 showSym "forall" forall    )
              end

           | ClkE.NonClockTerm => raise FlipFailed "bad clock terms in guard"

           | ATrans.ActSubWithNonSimpleSelect e      => raise FlipFailed (
                  "channel subscript contains non-simple bound variable (" ^
                  ExpressionCvt.Expr.toString e ^ ")")

           | ATrans.ActSubWithBadType {expr, badty, goodty} => raise FlipFailed
                ("channel subscript select variable " ^
                 ExpressionCvt.Expr.toString expr  ^ " has type " ^
                   ExpressionCvt.Ty.toString badty ^ ", not "     ^
                   ExpressionCvt.Ty.toString goodty)
                  
           | ATrans.BadSubscriptCount                =>
                raise FlipFailed ("wrong number of channel subscripts")

           | ATrans.MixedSubscriptTypes (e1, e2) => raise FlipFailed
                ("non-consistent channel subscript dimensions (" ^
                 ExpressionCvt.Expr.toString e1 ^ ", " ^
                 ExpressionCvt.Expr.toString e2 ^ ")")

  fun chanToSubRanges (env, s) = let
      fun arrToList (E.CHANNEL _, r)              = SOME r
        | arrToList (E.ARRAY (ty, E.Type sub), r) = arrToList (ty, sub::r)
        | arrToList _                             = NONE
    in
      Option.composePartial (fn ty=> arrToList (ty, []), Env.findValType env) s
    end

end
end