implementation module PmAbcMagic import StdArray, StdEnum, StdFile, StdOverloaded, StdInt, StdMisc import StdBool, StdList import StdMaybe import UtilStrictLists from PmPath import MakeImpPathname, MakeDefPathname import PmFileInfo import UtilNewlinesFile import PmDirCache from PmTypes import :: LinkObjFileName, :: LinkLibraryName from StdLibMisc import :: Date{..}, :: Time{..} //import RWSDebug (->>) l r :== l //-- ABCOptions: Compiler options that are stored in the abc file :: ABCOptions = { abcDescriptors :: !Bool , abcTimeProfile :: !Bool , abcStrictnessAnalysis :: !Bool , abcGiveWarnings :: !Bool , abcExportLocalLabels :: !Bool , abcGenerateComments :: !Bool , abcReuseUniqueNodes :: !Bool , abcFusion :: !Bool , abcGenericFusion :: !Bool , abc64Bits :: !Bool , abcDynamics :: !Bool } DefaultABCOptions :: ABCOptions; DefaultABCOptions = { abcDescriptors = False , abcTimeProfile = False , abcStrictnessAnalysis = True , abcGiveWarnings = False , abcExportLocalLabels = False , abcGenerateComments = False , abcReuseUniqueNodes = False , abcFusion = False , abcGenericFusion = False , abc64Bits = False , abcDynamics = False } //-- abc file handling // // Extract the following info from the ABC file: // - Does it contain sequential code and stack layout info // - Is it a system file // - Compiler version it was generated by // - Compiler options it was generated with // DebugMask :== 0; DontReuseUniqueNodesMask :== 1; ParallelMask :== 2; NoDescriptorsBitOffset :== 3; StrictnessMask :== 4; NoTimeProfileMask :== 5; ExportLocalLabelsBitsOffset :== 6; WarningsMask :== 7; SystemMask :== 8; FusionOffset :== 9; Abc64BitsOffset :==10; DynamicsBitsOffset :==11; GenericFusionOffset :==12; MinimumNrOfOptions :== 9; NrOfOptions :== 13; ParseABCInfoAndDependencies :: !Pathname !DATE !ABCCache !Files -> (!((!Bool, !Bool, !Int, !ABCOptions),(!List Modulename, !Maybe ModuleDate, !List ModuleDate, !List LinkObjFileName, !List LinkLibraryName),!ABCCache),!Files) ParseABCInfoAndDependencies path date abccache files # (opened, file, files) = fopen path FReadData files | not opened = ((dummyCinf,dummyDinf,abccache),files) # (file,cInfo) = Read_Version_and_Options file # (file,dInfo) = ParseABCDependencies file # (_,files) = fclose file files #! maxi=abccache.maxi # (cache_index,abccache) = search_path_in_cache path maxi abccache | cache_index<0 #! abccache = AC_Add path date dInfo abccache = ((cInfo,dInfo,abccache),files) #! abccache = {abccache & cache.[cache_index]={c_path=path,c_date=date,c_info=dInfo}} = ((cInfo,dInfo,abccache),files) where dummyCinf = (False,False,-1,DefaultABCOptions) dummyDinf = dummyDepInfo /* everything in place to add abc_opts caching... need to check if cache size doesn't increase dramatically and destroys OK behaviour... */ GetABCCompiledInfo :: !Bool !Pathname !ABCCache !Files -> ((!Bool, !Bool, !Int, !ABCOptions, !ABCCache),!Files) GetABCCompiledInfo update path cache files | (not update) && in_cache = ((c_sys,c_seq,c_ver,c_opt,cache),files) # (opened, file, files) = fopen path FReadData files | not opened = ((False,False,-1,DefaultABCOptions,cache),files) // otherwise # (file,(sys, stack_seq,version,options)) = Read_Version_and_Options file (_,files) = fclose file files = ((sys, stack_seq,version,options,cache),files) where in_cache = False c_sys = False c_seq = False c_ver = -1 c_opt = DefaultABCOptions Read_Version_and_Options :: !*File -> (!*File,!(!Bool,!Bool,!Int,!ABCOptions)); Read_Version_and_Options file # (eof,file) = fend file | eof = (file,(False,False,-1,DefaultABCOptions)) # (str,file) = readLine file # (end_of_info,sys,stack_seq,version,abcOptions) = Find_Version_and_Options str; | end_of_info = (file,(sys,stack_seq,version,abcOptions)) = Read_Version_and_Options file where Find_Version_and_Options :: !String -> (!Bool,!Bool,!Bool,!Int,!ABCOptions); Find_Version_and_Options str | match_endinfo < len_str = (True,False,False,-1,DefaultABCOptions); | match_options - start`` < MinimumNrOfOptions = (True,False,False,-1,DefaultABCOptions); # version = SubStringToInt 0 start` (dec match_version) str; abcOptions = (StringToCompilerOptions start`` match_options str); stack_seq = ProjectABCFile start`` str; sys = SystemABCFile start`` str; = (True,sys,stack_seq,version,abcOptions); where len_str = size str; start = SkipSpaces 0 len_str str; stop_endinfo = start + 7; stop_comp = start + 4; match_endinfo = MatchS start stop_endinfo 8 len_str ".endinfo" str; match_comp = MatchS start stop_comp 5 len_str ".comp" str; start` = SkipSpaces match_comp len_str str; match_version = SkipDigits start` len_str str; start`` = SkipSpaces match_version len_str str; match_options = SkipBits start`` len_str str; StringToCompilerOptions :: !Int !Int !String -> ABCOptions; StringToCompilerOptions start end opt = { abcDescriptors = not (opt.[start+NoDescriptorsBitOffset] == '1'), abcTimeProfile = not (opt.[start+NoTimeProfileMask] == '1'), abcStrictnessAnalysis = opt .[start+StrictnessMask] == '1', abcGiveWarnings = opt .[start+WarningsMask] == '1', abcExportLocalLabels = opt .[start+ExportLocalLabelsBitsOffset] == '1', abcGenerateComments = opt .[start+DebugMask] == '1', abcReuseUniqueNodes = not (opt.[start+DontReuseUniqueNodesMask] == '1'), abcFusion = start+FusionOffset Bool; ProjectABCFile start opt = opt .[start+ParallelMask] == '0'; SystemABCFile :: !Int !String -> Bool; SystemABCFile start opt = opt .[start+SystemMask] == '1'; SubStringToInt :: !Int !Int !Int String -> Int; SubStringToInt acc start stop str | start > stop = acc; # curc = str.[start]; | isDigit curc # acc` = 10 * acc + toInt curc - toInt '0'; = SubStringToInt acc` (inc start) stop str; = acc; //-- :: ModuleDate :== DateTime //-- Cached parse :: *ABCCache = { cache :: !*{!ABCStuff} , curi :: !Int , maxi :: !Int } ABCCacheSize :== 512 // fixed cache size => yuch! :: ABCStuff = { c_path :: !Pathname , c_date :: !DATE , c_info :: !ABCInfo } :: ABCInfo :== ( !List Modulename // dep mods , !Maybe ModuleDate // mod date , !List ModuleDate // dep dates , !List LinkObjFileName // dep objs , !List LinkLibraryName // dep libs ) EmptyABCS = { c_path = "" , c_date = NoDate , c_info = ( Nil , Nothing , Nil , Nil , Nil ) } AC_Init :: ABCCache AC_Init = { cache = createArray ABCCacheSize EmptyABCS , maxi = -1 , curi = 0 } AC_Add :: !Pathname !DATE !ABCInfo !*ABCCache -> *ABCCache AC_Add path date info abccache #! c = abccache.cache #! i = abccache.curi #! m = abccache.maxi #! c = {c & [i] = newstuff} = { abccache & cache = c , curi = (inc i) rem ABCCacheSize , maxi = if (m < (dec ABCCacheSize)) i m } where newstuff :: ABCStuff newstuff = {c_path=path,c_date=date,c_info=info} ParseABCDependencies` :: !Pathname !DATE !*ABCCache !Files -> ((!Bool, !List Modulename, !Maybe ModuleDate, !List ModuleDate, !List LinkObjFileName, !List LinkLibraryName, !*ABCCache/*, !FileInfoCache*/), !Files); ParseABCDependencies` path abcdate abccache files = AC_Look path abccache files where AC_Look path abccache=:{maxi} files # date = abcdate # (cache_index,abccache) = search_path_in_cache path maxi abccache | cache_index<0 # (opened,file,files) = fopen path FReadData files | not opened # (b,c,d,e,f) = dummyDepInfo = ((False,b,c,d,e,f,abccache),files) # (file,(b,c,d,e,f)) = ParseABCDependencies file # (_,files) = fclose file files #! abccache = AC_Add path date (b,c,d,e,f) abccache = ((True,b,c,d,e,f,abccache),files) # ({c_date,c_info},abccache) = abccache!cache.[cache_index] | eqDate c_date date # (b,c,d,e,f)=c_info = ((True,b,c,d,e,f,abccache),files) # (opened,file,files) = fopen path FReadData files | not opened # (b,c,d,e,f) = dummyDepInfo = ((False,b,c,d,e,f,abccache),files) # (file,(b,c,d,e,f)) = ParseABCDependencies file # (_,files) = fclose file files #! abccache = {abccache & cache.[cache_index]={c_path=path,c_date=date,c_info=(b,c,d,e,f)}} = ((True,b,c,d,e,f,abccache),files) eqDate l r = l.exists == r.exists && l.yy == r.yy && l.mm == r.mm && l.dd == r.dd && l.DATE.h == r.DATE.h && l.m == r.m && l.s == r.s search_path_in_cache :: !{#Char} !Int !*ABCCache -> *(!Int,!*ABCCache) search_path_in_cache path n c | n < 0 = (-1,c) | c.cache.[n].c_path == path = (n,c) = search_path_in_cache path (dec n) c // Find the module depencies in the .abc files dummyDepInfo = (Nil, Nothing, Nil, Nil, Nil) ParseABCDependencies :: !*File -> (!*File, !(!List Modulename, !Maybe ModuleDate, !List ModuleDate, !List LinkObjFileName, !List LinkLibraryName)) ParseABCDependencies file = Parse_lines file dummyDepInfo where Parse_lines :: !*File !(!(List Modulename), !(Maybe ModuleDate), !(List ModuleDate), !(List LinkObjFileName), !(List LinkLibraryName)) -> (!*File, !(!List Modulename, !Maybe ModuleDate, !List ModuleDate, !List LinkObjFileName, !List LinkLibraryName)) Parse_lines file depInfo # (eof, file) = fend file | eof = (file, depInfo) # (line, file) = readLine file (depInfo, end_of_info) = Parse_line line depInfo | end_of_info = (file, depInfo) = Parse_lines file depInfo Parse_line :: !String !(!(List Modulename), !(Maybe ModuleDate), !(List ModuleDate), !(List LinkObjFileName), !(List LinkLibraryName)) -> (!(!List Modulename, !Maybe ModuleDate, !List ModuleDate, !List LinkObjFileName, !List LinkLibraryName), !Bool); Parse_line str (modnames, mmoddate, depdates, objnames, libnames) | match_endinfo < len_str = ((modnames, mmoddate, depdates, objnames, libnames), True) | match_depend < len_str && last_q_depdate < len_str = ((modname :! modnames, mmoddate, S2DT depdate :! depdates, objnames, libnames), False) | match_depend < len_str && last_q_depend < len_str = ((modname :! modnames, mmoddate, emptydate :! depdates, objnames, libnames), False) | match_impobj < len_str && last_q_impobj < len_str = ((modnames, mmoddate, depdates, objname :! objnames, libnames), False) | match_implib < len_str && last_q_implib < len_str = ((modnames, mmoddate, depdates, objnames, libname :! libnames), False) // | match_date < len_str && last_q_moddate < len_str // = ((modnames, Just (S2DT moddate), depdates, objnames, libnames), False) | match_module < len_str && last_q_moddate < len_str #! moddate = S2DT moddate = ((modnames, Just moddate, depdates, objnames, libnames), False) = ((modnames, mmoddate, depdates, objnames, libnames), False) where emptydate = ({year=0,month=0,day=0,dayNr=0},{hours=0,minutes=0,seconds=0}) len_str = size str start = SkipSpaces 0 len_str str stop_endinfo = start + 7; // dec (size ".endinfo") stop_depend = startplussix; // dec (size ".depend") stop_importobj = startplussix; // dec (size ".impobj") stop_importlib = startplussix; // dec (size ".implib") // stop_date = start + 4; // dec (size ".date") stop_module = startplussix; // dec (size ".module") startplussix = start + 6; match_endinfo = MatchS start stop_endinfo 8 len_str ".endinfo" str match_depend = MatchS start stop_depend 7 len_str ".depend" str match_impobj = MatchS start stop_importobj 7 len_str ".impobj" str match_implib = MatchS start stop_importlib 7 len_str ".implib" str // match_date = MatchS start stop_date 5 len_str ".date" str match_module = MatchS start stop_module 7 len_str ".module" str (last_q_depend, modname) = characters_between_apostrophes match_depend len_str str (last_q_impobj, objname) = characters_between_apostrophes match_impobj len_str str (last_q_implib, libname) = characters_between_apostrophes match_implib len_str str (last_q_depdate, depdate) = characters_between_apostrophes (inc last_q_depend) len_str str // (last_q_moddate, moddate) // = characters_between_apostrophes match_date len_str str (last_q_modname, mainmodname) = characters_between_apostrophes match_module len_str str (last_q_moddate, moddate) = characters_between_apostrophes (inc last_q_modname) len_str str characters_between_apostrophes :: !.Int !.Int !.String -> .(!Int, !String) characters_between_apostrophes after_keyword_pos len_str str = (last_q, str % (inc first_q, dec last_q)) where start` = SkipSpaces after_keyword_pos len_str str first_q = FindQuoteChar str len_str start` last_q = FindQuoteChar str len_str (inc first_q) FindChar :: !Char !.String !.Int !Int -> Int; FindChar c line linelen pos | pos >= linelen = pos; | c == line.[pos] = pos; = FindChar c line linelen (inc pos); // FindQuoteChar :: !String !Int !Int -> Int; FindQuoteChar str len pos :== FindChar '\"' str len pos; //-- /* ABC dates format: .date "module.icl modified date,time" .depend "modulename" "dcl modified date,time" */ DT2S :: !DateTime -> String DT2S (date,time) = "\"" +++ yyyymmddnhhmmss +++"\"" where yyyymmddnhhmmss = yyyy +++ mm +++ dd /*+++ nn*/ +++ hs +++ ms +++ ss yyyy = pad 4 date.year mm = pad 2 date.month dd = pad 2 date.day // nn = pad 1 date.dayNr hs = pad 2 time.hours ms = pad 2 time.minutes ss = pad 2 time.seconds pad n x # s = toString x # l = size s # p = {'0' \\ i <- [1..n-l]} = p+++s S2DT :: !String -> DateTime S2DT s = (date,time) where date = {year=yy,month=mm,day=dd,dayNr=nn} time = {hours=hs,minutes=ms,seconds=ss} yy = toInt (s%(0,3)) mm = toInt (s%(4,5)) dd = toInt (s%(6,7)) nn = 0//toInt (s%(8,8)) hs = toInt (s%(8,9)) //(s%(9,10)) ms = toInt (s%(10,11)) //(s%(11,12)) ss = toInt (s%(12,13)) //(s%(13,14)) // patch abc file of system module for change in profiling settings PatchSystemABC :: !Int !Bool !Pathname /*!Bool*/ !Bool !*Files -> (!Bool, !*Files) PatchSystemABC version doPatch abcPath /*memoryProfile*/ timeProfile files | not doPatch = (True, files) # (opened, file, files) = fopen abcPath FReadData files | not opened = (False, files) ->> (abcPath +++ " not opened\n") # (patched, file) = patchFile file # (closed, files) = fclose file files = (patched && closed, files) where patchFile :: *File -> (Bool, *File) patchFile file # (firstLine, file) = freadline file //readLine file # (patched, firstLine) = patchLine firstLine//(RemoveNewlineChar firstLine) // ??? RemoveNewlineChar ??? | not patched = (False, file) ->> (abcPath +++ " not patched\n") # (reopened, file) = freopen file FAppendData | not reopened = (False, file) ->> (abcPath +++ " not reopened\n") # (sought, file) = fseek file 0 FSeekSet | not sought = (False, file) ->> (abcPath +++ " not sought\n") # file = fwrites firstLine file = (True, file) patchLine :: {#Char} -> (Bool, {#Char}) patchLine line # (found, offset) = findOptionStringOffset line | found = (found, {copy line & /*[offset+NoMemoryProfileMask] = if memoryProfile '0' '1',*/ [offset+NoTimeProfileMask] = if timeProfile '0' '1'}) = (False, line) copy array = {el \\ el <-: array} findOptionStringOffset :: {#Char} -> (Bool, Int) findOptionStringOffset string = (offset+MinimumNrOfOptions < stringLength, offset) where versionString = toString version versionSize = size versionString offset # init_offset = SkipSpaces 0 stringLength string # comp_offset = MatchS init_offset (init_offset + 4) 5 stringLength ".comp" string # next_offset = SkipSpaces comp_offset stringLength string # vers_offset = MatchS next_offset (next_offset + versionSize - 1) versionSize stringLength versionString string = SkipSpaces vers_offset stringLength string stringLength = size string //== SkipSpaces :: !Int !Int !String -> Int; SkipSpaces i len str | i >= len || str.[i] <> ' ' = i; = SkipSpaces (inc i) len str; SkipBits :: !Int !Int !String -> Int; SkipBits i len str | i >= len || (c <> '0' && c <> '1') = i; = SkipBits (inc i) len str; where c = str.[i]; SkipDigits :: !Int !Int !String -> Int; SkipDigits i len str | i >= len || c < '0' || '9' < c = i; = SkipDigits (inc i) len str; where c = str.[i]; MatchS :: !Int !Int !Int !Int !String !String -> Int; MatchS start stop patlen strlen pat str | stop >= strlen = stop; | MatchS2 0 start patlen pat str = inc stop; = MatchS (inc start) (inc stop) patlen strlen pat str; where MatchS2 :: !Int !Int !Int !String !String -> Bool; MatchS2 patpos strpos patlen pat str | patpos >= patlen = True; | pat.[patpos] == str.[strpos] = MatchS2 (inc patpos) (inc strpos) patlen pat str; = False;