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 *) (* The number of decimal digits that can fit in a 31-bit signed int *) let maxSuffixLength = 9 let maxSuffix = (* "999999999" *) try let maxSuffixStr = String.make maxSuffixLength '9' in int_of_string maxSuffixStr with Failure _ -> E.s (E.bug "You appear to be using the Alpha module on a computer where int is represented with fewer than 31 bits. Go to alpha.ml and change maxSuffixLength to a smaller number.") (* 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 up to maxSuffixLength * 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 begin if max >= maxSuffix then E.s (E.unimp ("Encountered a variable name containing ___ " ^^"and many digits. This could cause overflow " ^^"in the Alpha renaming module.")); let newsuffix = alphaSeparator ^ (string_of_int (max + 1)) in max + 1, newsuffix, l, (newsuffix, data) :: suffixes end else max, suffix, data, suffixes | _ -> E.s (E.bug "Alpha.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 *) || (* If the suffix has length >= maxSuffixLength, treat it as no suffix at all. This ensures we only call int_of_string with values that will fit in an int. *) (len - startSuffix > maxSuffixLength) || 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