-- Interface.mesa -- last modified by Satterthwaite, May 12, 1983 5:16 pm DIRECTORY CharIO: TYPE USING [NumberFormat, PutChar, PutDecimal, PutLine, PutNumber, PutString], CommandUtil: TYPE USING [ PairList, CommandObject, CommandPtr, CopyString, Echo, Failed, FreePairList, FreeString, GetNth, ListLength, Parse, SetExtension], CompilerOps: TYPE USING [ LetterSwitches, StreamId, Transaction, AppendHerald, DefaultSwitches, DoTransaction, Punt, Start, Stop], ExecOps: TYPE USING [Command, Outcome], Feedback: TYPE USING [Handle, Procs, ProcsHandle], File: TYPE USING [Capability, nullCapability], FileParms: TYPE USING [BindingProc, nullActual], FileParmOps: TYPE USING [ClearAList, Finalize, Initialize, SetAList], FileStream: TYPE USING [Create, GetLeaderProperties, SetLength], Heap: TYPE USING [Create, Delete], Inline: TYPE USING [DIVMOD, LongDivMod], IO: TYPE USING [UserAborted], OSMiscOps: TYPE USING [DeleteFile, FindFile], Stream: TYPE USING [Delete, Handle], String: TYPE USING [AppendDecimal], Strings: TYPE USING [String, SubStringDescriptor, AppendChar, AppendString], TemporarySpecialExecOps: TYPE USING [], Time: TYPE USING [Packed, AppendCurrent, Current], TimeStamp: TYPE USING [Null]; Interface: PROGRAM [] IMPORTS CharIO, CommandUtil, CompilerOps, FileStream, FileParmOps, Heap, Inline, IO, OSMiscOps, Stream, String, Strings, Time EXPORTS ExecOps, TemporarySpecialExecOps = { Command: TYPE = ExecOps.Command; StreamHandle: TYPE = Stream.Handle; -- feedback control feedback: Feedback.ProcsHandle; fbh: Feedback.Handle ← NIL; feedbackGoing: BOOL; userAbort: BOOL; -- set by ↑DEL useLog: BOOL; -- for compiler.log for error reporting -- command line input control commandObject: CommandUtil.CommandObject ← [pos: 0, len: 0, data: NIL]; commandPtr: CommandUtil.CommandPtr ← @commandObject; SetCommandInput: PROC [cmd: Command] = { commandObject.pos ← 0; commandObject.data ← cmd; FOR i: CARDINAL IN [0..CARDINAL.LAST] DO c: CHAR = commandObject.data[i]; IF c # '\011 AND c < '\040 THEN {commandObject.len ← i; RETURN}; ENDLOOP}; -- special output stream control log: StreamHandle ← NIL; GetCompilerLog: PUBLIC PROC RETURNS [StreamHandle] = {RETURN [log]}; SetTypescript: PROC = {IF log = NIL THEN log ← NewOutputStream["Compiler.Log"L]}; NewLine: PROC = {CharIO.PutChar[log, '\n]}; NewOutputStream: PROC [s: Strings.String] RETURNS [stream: StreamHandle] = { file: File.Capability; file ← OSMiscOps.FindFile[s, write]; stream ← FileStream.Create[file]; FileStream.SetLength[stream, 0]}; NewInputStream: PROC [s: Strings.String] RETURNS [StreamHandle] = { file: File.Capability = OSMiscOps.FindFile[s, read]; RETURN [FileStream.Create[file]]}; WriteHerald: PROC [s: StreamHandle, id: Strings.String] = { OPEN CharIO; herald: STRING ← [80]; CompilerOps.AppendHerald[herald]; Strings.AppendString[herald, " (Pilot Version)"L]; PutLine[s, herald]; IF ~feedbackGoing AND feedback.create # NIL THEN fbh ← feedback.create[system: "Compiler"L, herald: herald]; feedbackGoing ← TRUE; IF id # NIL THEN {PutString[s, id]; PutString[s, " -- "L]}; herald.length ← 0; Time.AppendCurrent[herald]; PutLine[s, herald]}; WriteTime: PROC [time: LONG CARDINAL] = { OPEN CharIO; hr, min, sec: CARDINAL; f: NumberFormat ← [base: 10, unsigned: TRUE, zerofill: FALSE, columns: 1]; W: PROC [t: CARDINAL] = { IF t # 0 OR f.zerofill THEN { PutNumber[log, t, f]; PutChar[log, ':]; f ← [base: 10, unsigned: TRUE, zerofill: TRUE, columns: 2]}}; [min, sec] ← Inline.LongDivMod[time, 60]; [hr, min] ← Inline.DIVMOD[min, 60]; W[hr]; W[min]; PutNumber[log, sec, f]}; ObjectInit: PROC = { IF objectStream = NIL THEN objectStream ← NewOutputStream[objectName]}; ErrorInit: PROC = { IF errorStream = NIL THEN IF useLog THEN errorStream ← log ELSE { errorName ← MakeErrorName[rootName]; errorStream ← NewOutputStream[errorName]; WriteHerald[errorStream, errorName]; CharIO.PutChar[errorStream, '\n]}}; MakeErrorName: PROC [root: Strings.String] RETURNS [Strings.String] = { RETURN [CommandUtil.SetExtension[ CommandUtil.CopyString[root, 2+("errlog"L).length], "errlog"L]]}; GetStream: PROC [id: CompilerOps.StreamId] RETURNS [s: Stream.Handle] = { SELECT id FROM source => s ← sourceStream; object => {IF objectStream = NIL THEN ObjectInit[]; s ← objectStream}; log => {IF errorStream = NIL THEN ErrorInit[]; s ← errorStream}; ENDCASE => ERROR; RETURN}; -- compiler sequencing Initialize: PROC = { parms.sourceStream ← sourceStream ← NewInputStream[sourceName]; parms.source.version ← TimeStamp.Null; parms.source.version.time ← FileStream.GetLeaderProperties[sourceStream].create}; Finalize: PROC [started: BOOL] = { IF objectStream # NIL THEN Stream.Delete[objectStream]; IF sourceStream # NIL THEN Stream.Delete[sourceStream]; IF errorStream # NIL AND errorStream # log THEN Stream.Delete[errorStream]; objectStream ← sourceStream ← errorStream ← NIL; IF parms.nErrors # 0 AND started THEN OSMiscOps.DeleteFile[objectName]; IF errorName = NIL THEN { errorName ← MakeErrorName[rootName]; OSMiscOps.DeleteFile[errorName]}}; WriteErrlogName: PROC = { IF useLog OR log = NIL THEN RETURN; CharIO.PutString[log, " on "L]; CharIO.PutString[log, rootName]; CharIO.PutString[log, ".errlog"L]}; WriteClosing: PROC [startTime: Time.Packed] = { OPEN CharIO; PutString[log, sourceName]; PutString[log, " -- "L]; IF parms.nErrors # 0 THEN { errors ← TRUE; PutString[log, "aborted, "L]; PutDecimal[log, parms.nErrors]; PutString[log, " errors"L]; IF parms.nWarnings # 0 THEN { warnings ← TRUE; PutString[log, " and "L]; PutDecimal[log, parms.nWarnings]; PutString[log, " warnings"L]}; WriteErrlogName[]; PutString[log, ", time: "L]; WriteTime[Time.Current[]-startTime]} ELSE { PutString[log, "source tokens: "L]; PutDecimal[log, parms.sourceTokens]; PutString[log, ", time: "L]; WriteTime[Time.Current[]-startTime]; IF parms.objectBytes # 0 THEN { PutString[log, "\n code bytes: "L]; PutDecimal[log, parms.objectBytes]; PutString[log, ", links: "L]; PutDecimal[log, parms.linkCount]; PutString[log, ", frame size: "L]; PutDecimal[log, parms.objectFrameSize]; IF parms.matched THEN PutChar[log, '.]}; IF parms.nWarnings # 0 THEN { warnings ← TRUE; NewLine[]; PutDecimal[log, parms.nWarnings]; PutString[log, " warnings"L]; WriteErrlogName[]}}; IF feedback.finishItem # NIL THEN { outcome: ExecOps.Outcome = SELECT TRUE FROM userAbort => aborted, parms.nErrors # 0 => IF parms.nWarnings # 0 THEN errorsAndWarnings ELSE errors, parms.nWarnings # 0 => warnings, ENDCASE => ok; msg: STRING ← [30]; IF parms.nErrors = 0 THEN Strings.AppendString[msg, "no"L] ELSE String.AppendDecimal[msg, parms.nErrors]; Strings.AppendString[msg, " errors"L]; IF parms.nWarnings # 0 THEN { Strings.AppendString[msg, ", "L]; String.AppendDecimal[msg, parms.nWarnings]; Strings.AppendString[msg, " warnings"L]}; feedback.finishItem[fbh, outcome, msg]}}; StopCompiler: PROC [startTime: Time.Packed] = { IF feedback.destroy # NIL THEN feedback.destroy[fbh, "End of compilation"L]; IF moduleCount > 1 THEN { CharIO.PutString[log, "\nTotal elapsed time: "L]; WriteTime[Time.Current[]-startTime]}; NewLine[]; Stream.Delete[log]; log ← NIL}; transaction: CompilerOps.Transaction; parms: POINTER TO CompilerOps.Transaction = @transaction; standardDefaults: CompilerOps.LetterSwitches = CompilerOps.DefaultSwitches[]; switchDefaults: CompilerOps.LetterSwitches; sourceName, objectName, errorName: Strings.String ← NIL; rootName: Strings.String ← NIL; sourceStream, objectStream, errorStream: StreamHandle ← NIL; errors, warnings: BOOL ← FALSE; moduleCount: CARDINAL; SetRoot: PROC [s: Strings.String] RETURNS [root: Strings.String] = { root ← CommandUtil.CopyString[s]; FOR i: CARDINAL IN [0..s.length) DO IF s[i] = '. THEN {root.length ← i; EXIT}; ENDLOOP}; -- * * * * * * M A I N B O D Y C O D E * * * * * * Compile: PUBLIC PROC [cmd: Command] RETURNS [outcome: ExecOps.Outcome] = { fProcs: Feedback.Procs ← []; -- all NIL RETURN [CompileUsingFeedback[cmd, @fProcs]]}; CompileUsingFeedback: PUBLIC PROC [cmd: Command, feedbackProcs: Feedback.ProcsHandle] RETURNS [ExecOps.Outcome] = { StartPass: PROC [pass: CARDINAL] RETURNS [goOn: BOOL] = { IF feedback.noteProgress # NIL THEN feedback.noteProgress[fbh, pass ! IO.UserAborted => {userAbort ← TRUE; CONTINUE}]; RETURN [~userAbort]}; compilerStartTime, moduleStartTime: Time.Packed; scratchZone: UNCOUNTED ZONE ← Heap.Create[initial: 8, increment: 8]; fbh ← NIL; feedbackGoing ← FALSE; switchDefaults ← CompilerOps.DefaultSwitches[]; parms.fileParms ← FileParmOps.Initialize[scratchZone]; CompilerOps.Start[scratchZone]; compilerStartTime ← Time.Current[]; moduleCount ← 0; userAbort ← FALSE; -- do the compilation SetCommandInput[cmd]; SetTypescript[]; feedback ← feedbackProcs; WriteHerald[log, NIL]; -- starts feedback stuff also errors ← warnings ← FALSE; DO args, results: CommandUtil.PairList; switches: Strings.String ← NIL; localPause: BOOL; sense: BOOL; BEGIN OPEN CharIO; parms.switches ← switchDefaults; parms.switches['p] ← FALSE; parms.debugPass ← CARDINAL.LAST; parms.getStream ← GetStream; parms.startPass ← StartPass; parms.objectBytes ← 0; parms.objectFrameSize ← 0; parms.linkCount ← 0; parms.nErrors ← 0; parms.nWarnings ← 0; parms.sourceTokens ← 0; [sourceName, args, results, switches] ← CommandUtil.Parse[ s: commandPtr, opX: 2+("mesa"L).length, resultX: 2+("bcd"L).length ! CommandUtil.Failed => {GO TO badSyntax}]; IF sourceName = NIL AND switches = NIL THEN EXIT; PutString[log, "\nCommand: "L]; CommandUtil.Echo[log, sourceName, args, results, switches]; IF CommandUtil.ListLength[results] > 1 THEN GO TO badSemantics; IF sourceName = NIL THEN GO TO globalSwitches; rootName ← SetRoot[IF CommandUtil.ListLength[results] = 1 THEN CommandUtil.GetNth[results, 0] ELSE sourceName]; IF switches # NIL THEN { sense ← TRUE; FOR i: CARDINAL IN [0..switches.length) DO c: CHAR = switches[i]; SELECT c FROM '-, '~ => sense ← ~sense; IN ['a..'z] => {parms.switches[c] ← sense; sense ← TRUE}; IN ['A..'Z] => { parms.switches[VAL['a.ORD+(c.ORD-'A.ORD)]] ← sense; sense ← TRUE}; IN ['1..'5] => {parms.debugPass ← c-'0; sense ← TRUE}; ENDCASE; ENDLOOP; switches ← CommandUtil.FreeString[switches]}; sourceName ← CommandUtil.SetExtension[sourceName, "mesa"L]; parms.source.locator ← [sourceName, 0, sourceName.length]; IF CommandUtil.ListLength[results] # 0 THEN { objectName ← CommandUtil.GetNth[list: results, n: 0, delete: TRUE]; results ← CommandUtil.FreePairList[results]} ELSE objectName ← CommandUtil.CopyString[rootName, 2+("bcd"L).length]; objectName ← CommandUtil.SetExtension[objectName, "bcd"L]; parms.objectName ← CommandUtil.CopyString[objectName]; parms.objectFile ← File.nullCapability; moduleCount ← moduleCount + 1; IF feedback.beginItem # NIL THEN { item: Strings.String ← CommandUtil.CopyString[NIL, rootName.length + 12 + 53]; first: BOOL ← TRUE; Strings.AppendString[item, "Compiling: "L]; Strings.AppendString[item, rootName]; FOR c: CHAR IN ['a..'z] DO sd: BOOL = (IF c = 'p THEN FALSE ELSE standardDefaults[c]); IF parms.switches[c] # sd THEN { IF first THEN {first ← FALSE; Strings.AppendChar[item, '/]}; IF sd THEN Strings.AppendChar[item, '-]; Strings.AppendChar[item, c]}; ENDLOOP; feedback.beginItem[fbh, item]; item ← CommandUtil.FreeString[item]}; useLog ← parms.switches['g]; parms.switches['g] ← FALSE; localPause ← parms.switches['p]; parms.switches['p] ← FALSE; Initialize[ ! ANY => {GOTO noSource}]; FileParmOps.SetAList[args]; -- pattern for replacement BEGIN BindPattern: FileParms.BindingProc = { parms.pattern ← actual; parms.op ← IF actual = FileParms.nullActual THEN compile ELSE replace}; parms.fileParms.Binding[ formalId: ["$"L, 0, 1], formalType: [NIL, 0, 0], binder: BindPattern]; END; NewLine[]; moduleStartTime ← Time.Current[]; CompilerOps.DoTransaction[parms ! CompilerOps.Punt => {GO TO punt}]; Finalize[TRUE]; FileParmOps.ClearAList[]; WriteClosing[moduleStartTime]; EXITS globalSwitches => { objectName ← NIL; sense ← TRUE; FOR i: CARDINAL IN [0..switches.length) DO c: CHAR = switches[i]; SELECT c FROM '-, '~ => sense ← ~sense; IN ['a..'z] => {switchDefaults[c] ← sense; sense ← TRUE}; IN ['A..'Z] => { switchDefaults[VAL['a.ORD+(c.ORD-'A.ORD)]] ← sense; sense ← TRUE}; ENDCASE => EXIT; ENDLOOP; switches ← CommandUtil.FreeString[switches]; args ← CommandUtil.FreePairList[args]}; noSource => { PutString[log, " -- source not found\n"L]; errors ← TRUE; parms.nErrors ← 1; WriteClosing[Time.Current[]]; args ← CommandUtil.FreePairList[args]}; badSemantics => { objectName ← NIL; errors ← TRUE; PutString[log, " -- Illegal command"L]; args ← CommandUtil.FreePairList[args]}; END; sourceName ← CommandUtil.FreeString[sourceName]; rootName ← CommandUtil.FreeString[rootName]; objectName ← CommandUtil.FreeString[objectName]; parms.objectName ← CommandUtil.FreeString[parms.objectName]; errorName ← CommandUtil.FreeString[errorName]; results ← CommandUtil.FreePairList[results]; NewLine[]; IF userAbort THEN { CharIO.PutString[log, "\n... command aborted\n"L]; GO TO truncateList}; IF (errors OR warnings) AND localPause THEN GO TO truncateList; REPEAT badSyntax => { CharIO.PutString[log, "\n-- Illegal syntax"L]; errors ← TRUE}; truncateList => switchDefaults['p] ← TRUE; punt => {Finalize[TRUE]; WriteClosing[moduleStartTime]; NewLine[]}; ENDLOOP; StopCompiler[compilerStartTime]; CompilerOps.Stop[]; FileParmOps.Finalize[]; Heap.Delete[scratchZone]; RETURN [SELECT TRUE FROM userAbort => aborted, errors => errors, warnings => warnings, ENDCASE => ok]}; }.