-- RCompImpl.mesa -- last edit by Schmidt, May 4, 1982 1:00 pm -- last edit by Satterthwaite, January 31, 1983 10:19 am -- Pilot 6.0/ Mesa 7.0 DIRECTORY CompilerOps: TYPE USING [ AppendHerald, DefaultSwitches, DoTransaction, LetterSwitches, Start, Stop, StreamId, Transaction], CWF: TYPE USING [FWF0, FWF1, FWF2, FWF3, FWFC, FWFCR, SetCode, WF0, WF1, WF2], Dir: TYPE USING [DepSeq, FileInfo], Directory: TYPE USING [DeleteFile, Error, Handle, Lookup, UpdateDates], File: TYPE USING [Capability, read], FileParms: TYPE USING [ ActualId, BindingProc, Name, nullActual, nullSymbolSpace, SymbolSpace], FileStream: TYPE USING [Create], Heap: TYPE USING [systemZone], Inline: TYPE USING [DIVMOD, LongDivMod], IO: TYPE USING[Handle, PutF, PutChar, rope], LongString: TYPE USING [ AppendChar, AppendSubString, EqualString, EquivalentString, SubStringDescriptor], MDComp: TYPE USING [SetVersAndModulename], MDDB: TYPE USING [GetBcdDepSeq, GetSrcDepSeq], MDMain: TYPE USING [DebugWP], MDModel: TYPE USING [ EraseCacheEntry, FoldInParms, GetFileInfo, GetSrcCreate, LISTSymbol, LocForType, LOCSymbol, LookupFileInfo, MODELSymbol, STRINGSymbol, SymbolSeq, TYPESymbol], MDUtil: TYPE USING [AcquireMsgLock, IOConfirm, ReleaseMsgLock], RComp: TYPE USING [], Rope: TYPE USING [Text], Runtime: TYPE USING [IsBound, RunConfig], Stream: TYPE USING [Delete, Handle, PutChar], Subr: TYPE USING [AbortMyself, NewFile, NewStream, Write], Time: TYPE USING [Current], TimeStamp: TYPE USING [Null], TypeScript: TYPE USING[TS, UserAbort], ViewerClasses: TYPE USING [Viewer], ViewerOps: TYPE USING [CreateViewer, FindViewer, OpenIcon, RestoreViewer, SetNewFile], WindowManager: TYPE USING [WaitCursor, UnWaitCursor]; RCompImpl: PROGRAM IMPORTS CompilerOps, CWF, Directory, FileStream, Heap, Inline, IO, LongString, MDComp, MDDB, MDMain, MDModel, MDUtil, Runtime, Stream, Subr, Time, TypeScript, ViewerOps, WindowManager EXPORTS RComp = { -- MDS Usage! sourcesh: Stream.Handle ← NIL; -- source input file logsh: Stream.Handle ← NIL; -- "Compiler.Log" ttyTypeScript: TypeScript.TS ← NIL; msgout: IO.Handle ← NIL; good, warn, err: CARDINAL ← 0; compilerStarted: BOOL ← FALSE; timeCompilerStarted: LONG CARDINAL ← 0; -- endof MDS Compile: PUBLIC PROC[symbolseq: MDModel.SymbolSeq, sploc: MDModel.LOCSymbol, tryreplacement: BOOL, oldbcdfilename: LONG STRING, spmodel: MDModel.MODELSymbol, confirm: REF BOOL, typeScript: TypeScript.TS, ttyin, ttyout, msgwindow: IO.Handle] RETURNS[errors, warnings, replaceable, declined: BOOL] = { t: CompilerOps.Transaction; cap: File.Capability; splist: MDModel.LISTSymbol ← sploc.parmlist; onestarttime: LONG CARDINAL; dontconfirm: BOOL = (IF confirm = NIL THEN FALSE ELSE ~(confirm↑)); loadedOk: BOOL; fi: Dir.FileInfo ← NIL; oldBcdDepSeq: Dir.DepSeq ← NIL; -- inherits spmodel, sploc, symbolseq -- splist is initailized once DirectoryBinding: PROC[ formalId, formalType: FileParms.Name, defaultLocator: LONG STRING, binder: FileParms.BindingProc] = { typename: STRING ← [40]; LongString.AppendSubString[typename, @formalType]; WHILE splist ~= NIL AND ~ISTYPE[splist.first, MDModel.TYPESymbol] DO splist ← splist.rest; ENDLOOP; WITH splist.first SELECT FROM sptype: MDModel.TYPESymbol => { sptypeloc: MDModel.LOCSymbol; bcdFileName: STRING ← [40]; fiInner: Dir.FileInfo; IF ~LongString.EqualString[sptype.typeName, typename] THEN { CWF.WF2["Error - %s not in correct parameter order (should be %s).\n"L, typename, sptype.typeName]; RETURN}; sptypeloc ← sptype.LocForType[]; IF sptypeloc = NIL THEN { CWF.WF1["Error - %s has no value.\n"L, typename]; RETURN}; fiInner ← sptypeloc.GetFileInfo[]; IF fiInner.bcdVers = TimeStamp.Null THEN MDComp.SetVersAndModulename[sptypeloc]; binder[[ version: fiInner.bcdVers, locator: [base: fiInner.bcdFileName, offset: 0, length: fiInner.bcdFileName.length]]]; splist ← splist.rest}; ENDCASE => CWF.WF1["Error - %s cannot be found on parameter list.\n"L, typename]}; -- called after DirectoryBinding, unless it is a hidden Directory parameter -- or is the old bcd in replacement mode DirectoryAcquire: PROC[type: LongString.SubStringDescriptor, actual: FileParms.ActualId] RETURNS [ss: FileParms.SymbolSpace] = { depseq: Dir.DepSeq; typename: STRING ← [40]; bcdFileName: STRING ← [40]; fiInner: Dir.FileInfo; { LongString.AppendSubString[bcdFileName, @actual.locator]; IF bcdFileName[bcdFileName.length-1] = '. THEN bcdFileName.length ← bcdFileName.length - 1; IF LongString.EquivalentString[oldbcdfilename, bcdFileName] THEN { IF oldBcdDepSeq = NIL THEN ERROR; RETURN[oldBcdDepSeq.symbolSpace]}; ss ← FileParms.nullSymbolSpace; LongString.AppendSubString[typename, @type]; FOR plist: MDModel.LISTSymbol ← sploc.parmlist, plist.rest UNTIL plist = NIL DO WITH plist.first SELECT FROM sptype: MDModel.TYPESymbol => { sptypeloc: MDModel.LOCSymbol = sptype.LocForType[]; IF sptypeloc ~= NIL AND (fiInner ← sptypeloc.GetFileInfo[]) ~= NIL AND fiInner.bcdVers = actual.version AND LongString.EqualString[fiInner.bcdFileName, bcdFileName] THEN GOTO foundIt}; ENDCASE => NULL; ENDLOOP; -- compiler can discover hidden definitions and not call DirectoryBinding, -- so we must be prepared to add it at this point CWF.FWF3[ MDMain.DebugWP, "Looking up directory entry (type %s, file %s) for %s.\n"L, typename, bcdFileName, fi.bcdFileName]; fiInner ← MDModel.LookupFileInfo[bcdFileName, actual.version]; IF fiInner = NIL THEN { CWF.WF2["Error - cannot find %s of %v in model.\n"L, bcdFileName, @actual.version]; RETURN[FileParms.nullSymbolSpace]}; EXITS foundIt => NULL; }; depseq ← MDDB.GetBcdDepSeq[fiInner, 0]; IF depseq = NIL THEN { CWF.FWF1[MDMain.DebugWP, "DirectoryAcquire: Can't open %s.\n"L, bcdFileName]; RETURN}; IF actual.version = TimeStamp.Null THEN CWF.FWF1[ MDMain.DebugWP, "DirectoryAcquire: Version of %s is null.\n"L, bcdFileName] ELSE IF actual.version ~= depseq.bcdVers THEN CWF.FWF3[ MDMain.DebugWP, "DirectoryAcquire: Versions don't match %s: cache says %v, compiler wants %v\n"L, bcdFileName, @depseq.bcdVers, @actual.version]; IF depseq.symbolSpace = FileParms.nullSymbolSpace THEN ERROR; RETURN[depseq.symbolSpace]}; DeleteBadBcd: PROC = { IF t.objectName ~= NIL THEN Directory.DeleteFile[t.objectName]; t.objectName ← NIL; MDModel.EraseCacheEntry[fi: fi, src: FALSE]}; Cleanup: PROC = { IF t.sourceStream ~= NIL THEN Stream.Delete[t.sourceStream]; t.sourceStream ← sourcesh ← NIL}; { ENABLE UNWIND => {DeleteBadBcd[]; Cleanup[]}; explicitSortSwitch: BOOL ← FALSE; srcDepSeq: Dir.DepSeq; msgout ← msgwindow; ttyTypeScript ← typeScript; errors ← warnings ← declined ← TRUE; replaceable ← FALSE; t.sourceStream ← NIL; t.objectName ← NIL; fi ← sploc.GetFileInfo[]; IF AskTheUser[fi.srcFileName, ttyin, ttyout, dontconfirm] THEN RETURN; declined ← FALSE; -- make sure the compiler is loaded, etc. IF ~compilerStarted THEN { loadedOk ← StartBatchCompile[]; IF ~loadedOk THEN RETURN}; -- set up Transaction record contents t.op ← IF tryreplacement THEN $replace ELSE $compile; t.source ← [ version: [net: 0, host: 0, time: MDModel.GetSrcCreate[fi]], locator: [base: fi.srcFileName, offset: 0, length: fi.srcFileName.length]]; cap ← Directory.UpdateDates[fi.srcCap, File.read]; sourcesh ← t.sourceStream ← FileStream.Create[cap]; t.fileParms ← [DirectoryBinding, DirectoryAcquire, DirectoryRelease, DirectoryForget]; t.switches ← CompilerOps.DefaultSwitches[]; srcDepSeq ← MDDB.GetSrcDepSeq[fi, t.source.version.time]; IF ~srcDepSeq.isdefns THEN { -- switches only matter for implementors FOR plist: MDModel.LISTSymbol ← sploc.parmlist, plist.rest WHILE plist ~= NIL DO WITH plist.first SELECT FROM spstr: MDModel.STRINGSymbol => [t.switches, explicitSortSwitch] ← MDModel.FoldInParms[spstr.strval]; ENDCASE => NULL; ENDLOOP; IF ~explicitSortSwitch THEN t.switches['s] ← FALSE}; IF tryreplacement THEN { IF fi.bcdVers = TimeStamp.Null THEN ERROR; t.pattern ← [ version: fi.bcdVers, locator: [base: oldbcdfilename, offset: 0, length: oldbcdfilename.length]]; oldBcdDepSeq ← fi.bcdDepSeq; -- will get old BCD! IF oldBcdDepSeq = NIL THEN ERROR; -- if there is old bcd, and the user did not specify explicitly /s or /-s -- then sort as the old bcd was sorted IF ~explicitSortSwitch THEN t.switches['s] ← oldBcdDepSeq.switches['s]} ELSE t.pattern ← FileParms.nullActual; t.objectName ← fi.bcdFileName; t.objectFile ← Subr.NewFile[fi.bcdFileName, Subr.Write, 10]; t.debugPass ← LAST[CARDINAL]; t.getStream ← LogGetStream; t.startPass ← CompilerPass; PrintStartOne[@t]; onestarttime ← Time.Current[]; -- these are here to hide them from the user t.switches['d] ← TRUE; -- debugging t.switches['g] ← FALSE; -- log is always Compiler.Log MDUtil.AcquireMsgLock[]; -- actually call the Compiler! CompilerOps.DoTransaction[@t ! UNWIND => MDUtil.ReleaseMsgLock[]]; MDUtil.ReleaseMsgLock[]; PrintStopOne[@t, onestarttime]; replaceable ← tryreplacement AND t.matched; errors ← t.nErrors # 0; warnings ← t.nWarnings # 0; IF errors THEN err ← err + 1; IF warnings THEN warn ← warn + 1; IF ~errors AND ~warnings THEN good ← good + 1; IF ~errors THEN fi.bcdVers ← t.objectVersion ELSE DeleteBadBcd[]; Cleanup[]; }}; StopBatchCompile: PUBLIC PROC RETURNS[nOk, nWarn, nErr: CARDINAL] = { log: ViewerClasses.Viewer; IF ~compilerStarted THEN RETURN[0, 0, 0]; -- noop call; compiler not running IF good # 0 THEN CWF.FWF1[LogWP, " %u successful; "L, @good]; IF warn # 0 THEN CWF.FWF1[LogWP, " %u w/warnings; "L, @warn]; IF err # 0 THEN CWF.FWF1[LogWP, " %u w/errors; "L, @err]; timeCompilerStarted ← Time.Current[] - timeCompilerStarted; CWF.FWF1[LogWP, "\nTotal elapsed time %y.\n"L, @timeCompilerStarted]; Stream.Delete[logsh]; logsh ← NIL; CompilerOps.Stop[]; compilerStarted ← FALSE; log ← ViewerOps.FindViewer["Compiler.Log"]; IF log ~= NIL THEN ViewerOps.RestoreViewer[log]; IF warn > 0 OR err > 0 THEN { IF log ~= NIL THEN ViewerOps.OpenIcon[log] ELSE CreateANewViewer["Compiler.log"]}; msgout ← NIL; RETURN[good, warn, err]}; -- local procedures CreateANewViewer: PROC [name: Rope.Text] = { viewer: ViewerClasses.Viewer; WindowManager.WaitCursor[]; viewer ← ViewerOps.CreateViewer[ flavor: $Text, info: [name: name, file: LOOPHOLE[name], iconic: FALSE, column: left]]; MDUtil.AcquireMsgLock[]; msgout.PutF["\nCreated Viewer: %s\n", IO.rope[name] ! UNWIND => {MDUtil.ReleaseMsgLock[]}]; MDUtil.ReleaseMsgLock[]; ViewerOps.SetNewFile[viewer]; WindowManager.UnWaitCursor[]}; StartBatchCompile: PROC RETURNS[loadedOk: BOOL] = { herald: STRING ← [100]; good ← warn ← err ← 0; logsh ← NIL; loadedOk ← LoadCompiler[]; timeCompilerStarted ← Time.Current[]; IF ~loadedOk THEN RETURN; Directory.DeleteFile["Compiler.Log"L ! Directory.Error => {CONTINUE}]; [] ← LogGetStream[log]; -- creates new log CompilerOps.AppendHerald[herald]; CWF.WF2["%s\n%lt\n"L, herald, @timeCompilerStarted]; CWF.FWF2[LogWP, "%s\n%lt\n"L, herald, @timeCompilerStarted]; CompilerOps.Start[Heap.systemZone]; compilerStarted ← TRUE}; AskTheUser: PROC[filename: LONG STRING, ttyin, ttyout: IO.Handle, dontconfirm: BOOL] RETURNS[declined: BOOL ← TRUE] = { ch: CHAR; -- ask the user if he really wants it compiled CWF.WF1["Compile %s ... "L, filename]; ch ← IF dontconfirm THEN 'y ELSE MDUtil.IOConfirm['y, ttyin, ttyout]; IF ch = 'q THEN { CWF.WF0["Quit.\n"L]; SIGNAL Subr.AbortMyself}; IF ch = 'y THEN { declined ← FALSE; CWF.WF0["Yes.\n"L]} ELSE CWF.WF0["No.\n"L]}; DirectoryRelease: PROC[ss: FileParms.SymbolSpace] = {}; DirectoryForget: PROC[actual: FileParms.ActualId] = {}; PrintStartOne: PROC[t: POINTER TO CompilerOps.Transaction] = { swstr: STRING ← [30]; CWF.FWF1[MsgWP, "Compiling: %s"L, t.source.locator.base]; CWF.FWF1[LogWP, "\nCommand: %s"L, t.source.locator.base]; ProduceDifferentialSwitches[swstr, t.switches]; CWF.FWF1[LogWP, "%s\n"L, swstr]; CWF.FWF0[MsgWP, swstr]}; ProduceDifferentialSwitches: PROC[swstr: LONG STRING, sw: CompilerOps.LetterSwitches] = { standardSwitches: CompilerOps.LetterSwitches ← CompilerOps.DefaultSwitches[]; first: BOOL ← TRUE; swstr.length ← 0; FOR c: CHAR IN ['a .. 'z] DO sd: BOOL = (IF c = 'p THEN FALSE ELSE standardSwitches[c]); IF sw[c] ~= sd THEN { IF first THEN {first ← FALSE; LongString.AppendChar[swstr, '/]}; IF sd THEN LongString.AppendChar[swstr, '-]; LongString.AppendChar[swstr, c]}; ENDLOOP}; PrintStopOne: PROC[ t: POINTER TO CompilerOps.Transaction, oneStartTime: LONG CARDINAL] = { -- first MsgSW IF t.nErrors > 0 THEN CWF.FWF1[MsgWP, "%u errors"L, @t.nErrors] ELSE CWF.FWF0[MsgWP, "no errors"L]; IF t.nWarnings > 0 THEN CWF.FWF1[MsgWP, ", %u warnings"L, @t.nWarnings]; CWF.FWFCR[MsgWP]; -- now log CWF.FWF1[LogWP, "%s -- "L, t.source.locator.base]; IF t.nErrors > 0 THEN { CWF.FWF1[LogWP, " aborted, %u errors"L, @t.nErrors]; IF t.nWarnings > 0 THEN CWF.FWF1[LogWP, " and %u warnings"L, @t.nWarnings]; oneStartTime ← Time.Current[] - oneStartTime; CWF.FWF1[LogWP, ", time: %y.\n\n"L, @oneStartTime]} ELSE { oneStartTime ← Time.Current[] - oneStartTime; CWF.FWF2[LogWP, "source tokens: %u, time: %y"L, @t.sourceTokens, @oneStartTime]; IF t.objectBytes > 0 THEN CWF.FWF3[LogWP, "\n code bytes: %u, links: %u, global frame words: %u"L, @t.objectBytes, @t.linkCount, @t.objectFrameSize]; IF t.nWarnings > 0 THEN CWF.FWF1[LogWP, "\n%u warnings"L, @t.nWarnings]; CWF.FWF0[LogWP, "\n\n"L]}}; LoadCompiler: PROC RETURNS[success: BOOL ← TRUE] = { cap: File.Capability; success ← TRUE; IF Runtime.IsBound[CompilerOps.Start] THEN RETURN; -- already loaded CWF.WF0["Loading Compiler ... "L]; { ENABLE ANY => { CWF.WF0["failed.\n"L]; GOTO out}; cap ← Directory.Lookup["compiler.bcd"L]; Runtime.RunConfig[file: cap, offset: 1, codeLinks: TRUE]; CWF.WF0["done.\n"L]; EXITS out => success ← FALSE; }}; LogGetStream: PROC[sid: CompilerOps.StreamId] RETURNS[sh: Stream.Handle] = { IF sid = source THEN RETURN[sourcesh]; -- temporary IF sid ~= log THEN ERROR; IF logsh = NIL THEN logsh ← Subr.NewStream["Compiler.Log"L, Subr.Write]; sh ← logsh}; CompilerPass: PROC[p: CARDINAL] RETURNS[goOn: BOOL] = { goOn ← ~TypeScript.UserAbort[ttyTypeScript]; CWF.FWFC[MsgWP, '.]}; MsgWP: PROC[ch: CHAR] = {msgout.PutChar[ch]}; LogWP: PROC[ch: CHAR] = {logsh.PutChar[ch]}; CWFYRoutine: PROC[uns: LONG POINTER, form: LONG STRING, wp: PROC[CHAR]] = { time: LONG CARDINAL = LOOPHOLE[uns, LONG POINTER TO LONG CARDINAL]↑; hr, min, sec: CARDINAL; [min, sec] ← Inline.LongDivMod[time, 60]; [hr, min] ← Inline.DIVMOD[min, 60]; IF hr > 0 THEN CWF.FWF3[wp, "%u:%02u:%02u"L, @hr, @min, @sec] ELSE IF min > 0 THEN CWF.FWF2[wp, "%u:%02u"L, @min, @sec] ELSE CWF.FWF1[wp, "%u"L, @sec]}; CWF.SetCode['y, CWFYRoutine]; }.