(* Copyright (c) 2009-2015 David C.J. Matthews This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *) (* Derived from the STRUCTURES module: Copyright (c) 2000-9 Cambridge University Technical Services Limited Title: Module Structure and Operations. Author: Dave Matthews, Cambridge University Computer Laboratory Copyright Cambridge University 1985 *) functor COPIER( structure STRUCTVALS : STRUCTVALSIG; structure TYPETREE : TYPETREESIG structure UNIVERSALTABLE: sig type universal = Universal.universal type univTable type 'a tag = 'a Universal.tag val univEnter: univTable * 'a tag * string * 'a -> unit; val univLookup: univTable * 'a tag * string -> 'a option; val univFold: univTable * (string * universal * 'a -> 'a) * 'a -> 'a; end; structure UTILITIES: sig val splitString: string -> { first:string,second:string } end sharing STRUCTVALS.Sharing = TYPETREE.Sharing sharing type UNIVERSALTABLE.univTable = STRUCTVALS.univTable ) :COPIERSIG = struct open STRUCTVALS TYPETREE UNIVERSALTABLE UTILITIES open Universal; (* for tag record selectors *) type tsvEnv = { enterType: string * typeConstrSet -> unit, enterStruct: string * structVals -> unit, enterVal : string * values -> unit }; (* Type constructor cache. This maps typeIDs in the copied signature to type constructors. More importantly, it identifies a type constructor that carries that type ID so that when we copy the values the string name is appropriate. *) (* Generate new entries for all the elements of the signature. As well as copying the signature it also keeps track of addresses used in the signature for values. This is needed because when we're constructing a signature we need to know the maximum address used. This is used to two cases only: when we have a named signature with possible sharing or "where types" or when including a signature. Really these cases should renumber the value entries. *) fun localCopySig(sourceTab, resEnv, mapTypeId, singleLevel, strName, newMap, cacheTail): unit = let fun buildTypeCache(sourceTab, strName, mapTypeId, buildDatatypes, initialCache, cacheTail) = let (* Process sub-directories first. That way they will be further down the list. *) fun foldSubStructs(dName, dVal, rest) = if tagIs structVar dVal then let val Struct { signat = Signatures { tab, typeIdMap, ...}, ...} = tagProject structVar dVal in buildTypeCache(tab, strName ^ dName ^ "." (* Add structure names. *), composeMaps(typeIdMap, mapTypeId), buildDatatypes, initialCache, rest) end else rest (* Then the types within this structure. *) fun foldTypes(_, dVal, rest) = if tagIs typeConstrVar dVal then let val TypeConstrSet(tcon, _) = tagProject typeConstrVar dVal fun makeName s = strName ^ s fun copyId(TypeId{idKind=Bound{ offset, ...}, ...}) = SOME(mapTypeId offset) | copyId _ = NONE in (* On the first pass we build datatypes, on the second type abbreviations using the copied datatypes. *) case tcIdentifier tcon of TypeId{idKind=TypeFn(args, equiv), access, description, ...} => if buildDatatypes then rest (* Not on this pass. *) else (* Build a new entry whether the typeID has changed or not. *) let val copiedEquiv = copyType(equiv, fn x => x, fn tcon => copyTypeConstrWithCache(tcon, copyId, fn x => x, makeName, initialCache)) val copiedId = TypeId{idKind=TypeFn(args, copiedEquiv), access=access, description=description} in makeTypeConstructor(makeName(tcName tcon), args, copiedId, tcLocations tcon) :: rest end | id => if not buildDatatypes then rest (* Not on this pass. *) else ( case copyId id of NONE => rest (* Skip (or add to cache?) *) | SOME newId => makeTypeConstructor (makeName(tcName tcon), tcTypeVars tcon, newId, tcLocations tcon) :: rest ) end else rest in univFold(sourceTab, foldTypes, univFold(sourceTab, foldSubStructs, cacheTail)) end (* Process datatypes. While processing these we make new entries for every datatype even if they are already in the cache. That way we end up with the last entry in the list being the most local and that's the one we want to use for type abbreviations and values. *) val datatypeCache = buildTypeCache(sourceTab, strName, mapTypeId, true, (* Datatypes *) [], cacheTail) (* Now add any type abbreviations. These can refer to datatypes we added in the previous pass but don't reuse type abbreviations we add elsewhere. *) val typeCache = buildTypeCache(sourceTab, strName, mapTypeId, false, (* Type abbreviations. *)datatypeCache, datatypeCache) fun copyTypeCons (tcon : typeConstrs) : typeConstrs = let fun copyId(TypeId{idKind=Bound{ offset, ...}, ...}) = SOME(mapTypeId offset) | copyId _ = NONE in copyTypeConstrWithCache (tcon, copyId, fn x => x, fn s => strName ^ s, typeCache) end fun copyTyp (t : types) : types = copyType (t, fn x => x, (* Don't bother with type variables. *) copyTypeCons) in univFold (sourceTab, fn (dName: string, dVal: universal, _) => if tagIs structVar dVal then let val Struct { signat, name=structName, access, locations=structLocs, ...} = tagProject structVar dVal val Signatures { name=sigName, tab, typeIdMap, firstBoundIndex, locations=sigLocs, ...} = signat val newSig = if singleLevel then (* Just compose the maps. *) makeSignature(sigName, tab, firstBoundIndex, sigLocs, composeMaps(typeIdMap, mapTypeId), []) else (* Recursive copy. *) let (* Make a new sub-structure. *) val newTab = makeSignatureTable (); (* Copy everything into the new signature. *) val () = localCopySig (tab, { enterType = fn (s,v) => univEnter (newTab, typeConstrVar, s, v), enterStruct = fn (s,v) => univEnter (newTab, structVar, s, v), enterVal = fn (s,v) => univEnter (newTab, valueVar, s, v) }, composeMaps(typeIdMap, mapTypeId), false, strName ^ dName ^ ".", newMap, typeCache) in (* If we're copying it all set the resulting map to the new map. *) makeSignature(sigName, newTab, firstBoundIndex, sigLocs, newMap, []) end val newStruct = Struct { name = structName, signat = newSig, access = access, locations = structLocs} in #enterStruct resEnv (dName, newStruct) end (* structures *) else if tagIs typeConstrVar dVal then let val TypeConstrSet(oldConstr, tcConstructors) = tagProject typeConstrVar dVal val newConstr = copyTypeCons oldConstr; (* Copy the value constructors for a datatype. *) fun copyValueConstr( v as Value{name, typeOf, class, access, locations, references, instanceTypes, ...}) = let (* Copy its type and make a new constructor if the type has changed. *) val newType = copyTyp typeOf; in if not (identical (newType, typeOf)) then Value{name=name, typeOf=newType, class=class, access=access, locations = locations, references = references, instanceTypes=instanceTypes} else v end; val copiedConstrs = map copyValueConstr tcConstructors in #enterType resEnv (dName, TypeConstrSet(newConstr, copiedConstrs)) end (* Finally the values and exceptions. *) else if tagIs valueVar dVal then let val v as Value {typeOf, class, name, access, locations, references, instanceTypes, ...} = tagProject valueVar dVal; val newType = copyTyp typeOf; (* Can save creating a new object if the address and type are the same as they were. *) val res = if not (identical (newType, typeOf)) then Value {typeOf=newType, class=class, name=name, instanceTypes=instanceTypes, access=access,locations=locations, references = references} else v in #enterVal resEnv (name, res) end else (), () ) end (* fullCopySig *) (* Exported versions of these. *) (* Open a structure or include a signature. *) fun openSignature(Signatures{ tab, typeIdMap, ...}, resEnv, strName) = localCopySig(tab, resEnv, typeIdMap, true (* One level. *), strName, typeIdMap, []) and fullCopyDatatype(oldConstr:typeConstrSet, mapTypeId, strName) = let val sigSpace = makeSignatureTable() val Env { enterType, ...} = makeEnv sigSpace val () = enterType(tcName(tsConstr oldConstr), oldConstr) val resType = ref NONE val resEnv = { enterType = fn (_, tc) => resType := SOME tc, enterStruct = fn (s, _) => raise Misc.InternalError ("enterStruct "^s), enterVal = fn (s, _) => raise Misc.InternalError ("enterVal "^s) } val () = localCopySig(sigSpace, resEnv, mapTypeId, true, strName, fn _ => raise Subscript, []) in valOf(! resType) end fun replaceMap(Signatures{tab=sourceTab, name = sourceName, locations, ...}, mapTypeId: int -> typeId, min, boundIds, newMap): signatures = let (* Make a new signature. *) val tab = makeSignatureTable (); val tsvEnv = { enterType = fn (s,v) => univEnter (tab, typeConstrVar, s, v), enterStruct = fn (s,v) => univEnter (tab, structVar, s, v), enterVal = fn (s, v) => univEnter (tab, valueVar, s, v) } (* Copy everything into the new signature. *) val () = localCopySig(sourceTab, tsvEnv, mapTypeId, false, "", newMap, []) in makeSignature(sourceName, tab, min, locations, newMap, boundIds) end (* replaceMap *) (* Find the maximum run-time offset used for a value or structure in a signature. This excludes type IDs. *) fun getNextRuntimeOffset(Signatures{tab, ...}): int = let fun getOffset(_, dVal, m) = if tagIs valueVar dVal then case tagProject valueVar dVal of Value { access = Formal addr, ...} => Int.max(addr+1, m) | _ => m else if tagIs structVar dVal then case tagProject structVar dVal of Struct{access=Formal addr, ...} => Int.max(addr+1, m) | _ => m else if tagIs typeConstrVar dVal then let fun getConstrOffset(Value { access = Formal addr, ...}, m) = Int.max(addr+1, m) | getConstrOffset(_, m) = m in List.foldl getConstrOffset m (tsConstructors (tagProject typeConstrVar dVal)) end else m in univFold(tab, getOffset, 0) end structure Sharing = struct type signatures = signatures type typeConstrSet = typeConstrSet type structVals = structVals type values = values type typeId = typeId type valAccess = valAccess type types = types type univTable = univTable end end;