summaryrefslogtreecommitdiff
path: root/cil/ocamlutil/alpha.ml
diff options
context:
space:
mode:
Diffstat (limited to 'cil/ocamlutil/alpha.ml')
-rwxr-xr-xcil/ocamlutil/alpha.ml156
1 files changed, 156 insertions, 0 deletions
diff --git a/cil/ocamlutil/alpha.ml b/cil/ocamlutil/alpha.ml
new file mode 100755
index 0000000..6a1ea01
--- /dev/null
+++ b/cil/ocamlutil/alpha.ml
@@ -0,0 +1,156 @@
+module H = Hashtbl
+module E = Errormsg
+open Pretty
+
+let debugAlpha (prefix: string) = false
+(*** Alpha conversion ***)
+let alphaSeparator = "___"
+let alphaSeparatorLen = String.length alphaSeparator
+
+(** For each prefix we remember the next integer suffix to use and the list
+ * of suffixes, each with some data assciated with the newAlphaName that
+ * created the suffix. *)
+type 'a alphaTableData = int * (string * 'a) list
+
+type 'a undoAlphaElement =
+ AlphaChangedSuffix of 'a alphaTableData ref * 'a alphaTableData (* The
+ * reference that was changed and
+ * the old suffix *)
+ | AlphaAddedSuffix of string (* We added this new entry to the
+ * table *)
+
+(* Create a new name based on a given name. The new name is formed from a
+ * prefix (obtained from the given name by stripping a suffix consisting of
+ * the alphaSeparator followed by only digits), followed by alphaSeparator
+ * and then by a positive integer suffix. The first argument is a table
+ * mapping name prefixes to the largest suffix used so far for that
+ * prefix. The largest suffix is one when only the version without suffix has
+ * been used. *)
+let rec newAlphaName ~(alphaTable: (string, 'a alphaTableData ref) H.t)
+ ~(undolist: 'a undoAlphaElement list ref option)
+ ~(lookupname: string)
+ ~(data: 'a) : string * 'a =
+ alphaWorker ~alphaTable:alphaTable ~undolist:undolist
+ ~lookupname:lookupname ~data:data true
+
+
+(** Just register the name so that we will not use in the future *)
+and registerAlphaName ~(alphaTable: (string, 'a alphaTableData ref) H.t)
+ ~(undolist: 'a undoAlphaElement list ref option)
+ ~(lookupname: string)
+ ~(data: 'a) : unit =
+ ignore (alphaWorker ~alphaTable:alphaTable ~undolist:undolist
+ ~lookupname:lookupname ~data:data false)
+
+
+and alphaWorker ~(alphaTable: (string, 'a alphaTableData ref) H.t)
+ ~(undolist: 'a undoAlphaElement list ref option)
+ ~(lookupname: string) ~(data:'a)
+ (make_new: bool) : string * 'a =
+ let prefix, suffix, (numsuffix: int) = splitNameForAlpha ~lookupname in
+ if debugAlpha prefix then
+ ignore (E.log "Alpha worker: prefix=%s suffix=%s (%d) create=%b. "
+ prefix suffix numsuffix make_new);
+ let newname, (olddata: 'a) =
+ try
+ let rc = H.find alphaTable prefix in
+ let max, suffixes = !rc in
+ (* We have seen this prefix *)
+ if debugAlpha prefix then
+ ignore (E.log " Old max %d. Old suffixes: @[%a@]" max
+ (docList
+ (fun (s, l) -> dprintf "%s" (* d_loc l *) s)) suffixes);
+ (* Save the undo info *)
+ (match undolist with
+ Some l -> l := AlphaChangedSuffix (rc, !rc) :: !l
+ | _ -> ());
+
+ let newmax, newsuffix, (olddata: 'a), newsuffixes =
+ if numsuffix > max then begin
+ (* Clearly we have not seen it *)
+ numsuffix, suffix, data,
+ (suffix, data) :: suffixes
+ end else begin
+ match List.filter (fun (n, _) -> n = suffix) suffixes with
+ [] -> (* Not found *)
+ max, suffix, data, (suffix, data) :: suffixes
+ | [(_, l) ] ->
+ (* We have seen this exact suffix before *)
+ if make_new then
+ let newsuffix = alphaSeparator ^ (string_of_int (max + 1)) in
+ max + 1, newsuffix, l, (newsuffix, data) :: suffixes
+ else
+ max, suffix, data, suffixes
+ | _ -> E.s (E.bug "Cil.alphaWorker")
+ end
+ in
+ rc := (newmax, newsuffixes);
+ prefix ^ newsuffix, olddata
+ with Not_found -> begin (* First variable with this prefix *)
+ (match undolist with
+ Some l -> l := AlphaAddedSuffix prefix :: !l
+ | _ -> ());
+ H.add alphaTable prefix (ref (numsuffix, [ (suffix, data) ]));
+ if debugAlpha prefix then ignore (E.log " First seen. ");
+ lookupname, data (* Return the original name *)
+ end
+ in
+ if debugAlpha prefix then
+ ignore (E.log " Res=: %s \n" newname (* d_loc oldloc *));
+ newname, olddata
+
+(* Strip the suffix. Return the prefix, the suffix (including the separator
+ * and the numeric value, possibly empty), and the
+ * numeric value of the suffix (possibly -1 if missing) *)
+and splitNameForAlpha ~(lookupname: string) : (string * string * int) =
+ let len = String.length lookupname in
+ (* Search backward for the numeric suffix. Return the first digit of the
+ * suffix. Returns len if no numeric suffix *)
+ let rec skipSuffix (i: int) =
+ if i = -1 then -1 else
+ let c = Char.code (String.get lookupname i) - Char.code '0' in
+ if c >= 0 && c <= 9 then
+ skipSuffix (i - 1)
+ else (i + 1)
+ in
+ let startSuffix = skipSuffix (len - 1) in
+
+ if startSuffix >= len (* No digits at all at the end *) ||
+ startSuffix <= alphaSeparatorLen (* Not enough room for a prefix and
+ * the separator before suffix *) ||
+ (* Suffix starts with a 0 and has more characters after that *)
+ (startSuffix < len - 1 && String.get lookupname startSuffix = '0') ||
+ alphaSeparator <> String.sub lookupname
+ (startSuffix - alphaSeparatorLen)
+ alphaSeparatorLen
+ then
+ (lookupname, "", -1) (* No valid suffix in the name *)
+ else
+ (String.sub lookupname 0 (startSuffix - alphaSeparatorLen),
+ String.sub lookupname (startSuffix - alphaSeparatorLen)
+ (len - startSuffix + alphaSeparatorLen),
+ int_of_string (String.sub lookupname startSuffix (len - startSuffix)))
+
+
+let getAlphaPrefix ~(lookupname:string) : string =
+ let p, _, _ = splitNameForAlpha ~lookupname:lookupname in
+ p
+
+(* Undoes the changes as specified by the undolist *)
+let undoAlphaChanges ~(alphaTable: (string, 'a alphaTableData ref) H.t)
+ ~(undolist: 'a undoAlphaElement list) =
+ List.iter
+ (function
+ AlphaChangedSuffix (where, old) ->
+ where := old
+ | AlphaAddedSuffix name ->
+ if debugAlpha name then
+ ignore (E.log "Removing %s from alpha table\n" name);
+ H.remove alphaTable name)
+ undolist
+
+let docAlphaTable () (alphaTable: (string, 'a alphaTableData ref) H.t) =
+ let acc : (string * (int * (string * 'a) list)) list ref = ref [] in
+ H.iter (fun k d -> acc := (k, !d) :: !acc) alphaTable;
+ docList ~sep:line (fun (k, (d, _)) -> dprintf " %s -> %d" k d) () !acc
+