Interface.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
last modified by Satterthwaite, May 12, 1983 5:16 pm
Last Edited by: Paul Rovner, December 4, 1983 11:57 am
Last Edited by: Maxwell, January 26, 1984 9:05:33 am PST
Last Edited by: Spreitzer, May 16, 1984 7:45:09 pm PDT
Last Edited by: Kupfer, July 26, 1984 9:45:46 am PDT
DIRECTORY
Ascii: TYPE USING [Lower],
BasicTime: TYPE USING [GMT, Now, Period, ToNSTime],
Commander: TYPE USING [Handle, Register],
CommandUtil:
TYPE
USING [
PairList, Echo, Failed, GetNth, GetRootName, ListLength, Parse, SetExtension],
CompilerOps:
TYPE
USING [
LetterSwitches, StreamId, Transaction,
AppendHerald, DefaultSwitches, DoTransaction, Punt, Start, Stop],
FileNames: TYPE USING [CurrentWorkingDirectory, GetShortName],
FileParms: TYPE USING [BindingProc, nullActual],
FileParmOps: TYPE USING [ClearAList, Finalize, Initialize, SetAList],
FileViewerOps: TYPE USING [ShowLog, WaitUntilSaved],
FileViewerOpsExtras: TYPE USING [AttachErrorLog, RemoveErrorLog],
FS: TYPE USING [ExpandName, GetName, nullOpenFile, OpenFile, OpenFileFromStream, StreamOpen, FileInfo, Error, Delete],
IO: TYPE USING [Close, EndOfStream, Error, GetTokenRope, IDProc, int, Put, PutChar, PutRope, rope, STREAM, time, RIS],
Process: TYPE USING [GetPriority, Priority, priorityBackground, SetPriority],
ProcessExtras: TYPE USING [CheckForAbort],
Resource: TYPE USING [AbortProc, Acquire, Release],
Rope: TYPE USING [Cat, Fetch, Find, Length, Replace, ROPE, Substr],
TimeStamp: TYPE USING [Null],
UserProfile: TYPE USING [Boolean, Token];
Interface:
PROGRAM
IMPORTS
Ascii, BasicTime, Commander, CommandUtil, CompilerOps, FileNames, FileParmOps, FileViewerOps, FileViewerOpsExtras, FS,
IO, Process, ProcessExtras, Resource, Rope, UserProfile
= {
Outcome: TYPE = {ok, warnings, errors, aborted};
parms: REF CompilerOps.Transaction = NEW[CompilerOps.Transaction];
standardDefaults: CompilerOps.LetterSwitches = CompilerOps.DefaultSwitches[];
switchDefaults: CompilerOps.LetterSwitches;
sourceName, objectName, errorName, rootName, wDir: Rope.ROPE ← NIL;
sourceStream, objectStream, errorStream: IO.STREAM ← NIL;
useLog: BOOL; -- use compiler.log for error reporting
log: IO.STREAM ← NIL;
destroyLogOnSuccess: BOOLEAN;
createIconic: BOOLEAN;
blinkIfIconic: BOOLEAN;
viewSeparateLogs: BOOLEAN;
Compile:
SAFE PROC [cmd: Commander.Handle]
RETURNS [result: REF ← NIL, msg: Rope.ROPE ← NIL] ~ TRUSTED {
userAbort: BOOL ← FALSE; -- set by ^DEL, STOP
errors, warnings: BOOL ← FALSE;
priority: Process.Priority = Process.GetPriority[];
compilerStartTime, moduleStartTime: BasicTime.GMT;
moduleCount: CARDINAL ← 0;
complex: BOOL = SELECT cmd.procData.clientData FROM $Vanilla => FALSE, $Complex => TRUE, ENDCASE => ERROR;
compilerSwitches: Rope.ROPE ← UserProfile.Token["Compiler.Switches"];
cmdStream: IO.STREAM;
acquire the compiler's resource lock; await success
{
otherOwner: Rope.ROPE;
success: BOOL ← FALSE;
abortProc: Resource.AbortProc =
TRUSTED{
-- PROC [data: REF ANY] RETURNS[abort: BOOL];
abort ← FALSE;
ProcessExtras.CheckForAbort[! ABORTED => {abort ← TRUE; CONTINUE}];
};
[success, otherOwner] ← Resource.Acquire[
resource: $Compiler, owner: "Compiler", waitForIt: FALSE];
IF
NOT success
THEN {
cmd.out.PutRope[Rope.Cat["Waiting for ", otherOwner, " to finish..."]];
[success, ] ← Resource.Acquire
[resource: $Compiler, owner: "Compiler", waitForIt: TRUE, abortProc: abortProc];
IF
NOT success
THEN RETURN[$Failure, "ABORTED"]
ELSE cmd.out.PutRope["proceeding\n"];
};
};
get some parameters from the user's profile
destroyLogOnSuccess ← UserProfile.Boolean["Compiler.DestroyLogOnSuccess", TRUE];
createIconic ← UserProfile.Boolean["Compiler.IconicLogs", FALSE];
blinkIfIconic ← UserProfile.Boolean["Compiler.BlinkLogs", TRUE];
viewSeparateLogs ← UserProfile.Boolean["Compiler.ViewSeparateLogs", FALSE];
IF UserProfile.Boolean["Compiler.SeparateLogs",
FALSE]
THEN compilerSwitches ← Rope.Cat[compilerSwitches, "~g"];
IF compilerSwitches.Length[] # 0
THEN {
cmd.commandLine ← Rope.Cat[(IF complex THEN "/" ELSE "-"), compilerSwitches, " ", cmd.commandLine]
};
cmdStream ← IO.RIS[cmd.commandLine];
do the compilation
Process.SetPriority[Process.priorityBackground];
BEGIN
ENABLE
UNWIND => {Process.SetPriority[priority]; [] ← Resource.Release[$Compiler]};
StartPass:
PROC [pass:
CARDINAL]
RETURNS [goOn:
BOOL] = {
userAbort ← FALSE;
ProcessExtras.CheckForAbort[! ABORTED => {userAbort ← TRUE; CONTINUE}];
cmd.out.PutRope[" ." ! ABORTED => {userAbort ← TRUE; CONTINUE}];
IF userAbort THEN cmd.out.PutRope[" aborted."];
RETURN [~userAbort]};
compilerStartTime ← BasicTime.Now[];
switchDefaults ← CompilerOps.DefaultSwitches[];
parms.fileParms ← FileParmOps.Initialize[];
{
ENABLE
UNWIND => FileParmOps.Finalize[];
CompilerOps.Start[];
{
ENABLE
UNWIND => CompilerOps.Stop[];
wDir ← FileNames.CurrentWorkingDirectory[];
IF log = NIL THEN log ← FS.StreamOpen[fileName: "Compiler.log", accessOptions: $create];
{
ENABLE
UNWIND => {log.Close[]; log ←
NIL};
WriteHerald[log, NIL];
DO
first: BOOL;
args, results: CommandUtil.PairList;
switches: Rope.ROPE ← NIL;
localPause: BOOL;
sense: BOOL;
BEGIN
-- for EXITS clause
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 ← NIL;
IF complex
THEN [sourceName, args, results, switches] ← CommandUtil.Parse[cmdStream ! CommandUtil.Failed => GOTO badSyntax]
ELSE {
-- use a simple syntax for the command line
token: Rope.ROPE;
token ← cmdStream.GetTokenRope[IO.IDProc ! IO.EndOfStream => EXIT].token;
IF token.Length[] > 0
THEN
SELECT token.Fetch[0]
FROM
'- => switches ← token.Substr[1, token.Length[]-1];
ENDCASE => sourceName ← token};
IF sourceName = NIL AND switches = NIL THEN EXIT;
IO.PutRope[log, "\nCommand: "];
CommandUtil.Echo[log, sourceName, args, results, switches];
IF CommandUtil.ListLength[results] > 1 THEN GOTO badSemantics;
IF sourceName = NIL THEN GOTO globalSwitches;
rootName ← CommandUtil.GetRootName[
IF CommandUtil.ListLength[results] = 1
THEN CommandUtil.GetNth[results, 0]
ELSE FileNames.GetShortName[sourceName]];
IF switches #
NIL
THEN {
sense ← TRUE;
FOR i:
INT
IN [0..switches.Length[])
DO
c: CHAR = switches.Fetch[i];
SELECT c
FROM
'-, '~ => sense ← ~sense;
IN ['a..'z], IN ['A..'Z] => {parms.switches[Ascii.Lower[c]] ← sense; sense ← TRUE};
IN ['1..'5] => {parms.debugPass ← c-'0; sense ← TRUE};
ENDCASE;
ENDLOOP;
switches ← NIL;
};
sourceName ← CommandUtil.SetExtension[sourceName, "mesa"];
FileViewerOps.WaitUntilSaved[
IF Rope.Find[sourceName, ">"] < 0
AND Rope.Find[sourceName, "/"] < 0
THEN FS.ExpandName[sourceName, wDir].fullFName
ELSE sourceName,
cmd.out ! ABORTED => {userAbort ← TRUE; GOTO truncateList}];
parms.source.locator ← FileNames.GetShortName[sourceName];
IF CommandUtil.ListLength[results] # 0
THEN {
objectName ← CommandUtil.GetNth[list: results, n: 0, delete: TRUE];
results ← NIL;
}
ELSE objectName ← rootName;
objectName ← CommandUtil.SetExtension[objectName, "bcd"];
parms.objectName ← objectName;
parms.objectFile ← FS.nullOpenFile;
moduleCount ← moduleCount + 1;
feedback to user
IF beginItem # NIL THEN IF beginItem[sourceName, out] THEN LOOP;
cmd.out.Put[IO.rope["Compiling: "], IO.rope[rootName]];
first ← TRUE;
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; cmd.out.PutChar['/]};
IF sd THEN cmd.out.PutChar['-];
cmd.out.PutChar[c]};
ENDLOOP;
useLog ← parms.switches['g]; parms.switches['g] ← FALSE;
localPause ← parms.switches['p]; parms.switches['p] ← FALSE;
main line code
Initialize[ ! FS.Error => GOTO noSource];
sourceName ← FileNames.GetShortName[sourceName];
{
ENABLE
UNWIND => Finalize[userAbort];
FileParmOps.SetAList[args];
{
ENABLE
UNWIND => FileParmOps.ClearAList[];
BindPattern: FileParms.BindingProc = {
parms.pattern ← actual;
parms.op ← IF actual = FileParms.nullActual THEN compile ELSE replace};
parms.fileParms.Binding[formalId: "$", formalType: NIL, binder: BindPattern];
NewLogLine[];
moduleStartTime ← BasicTime.Now[];
CompilerOps.DoTransaction[parms
!
CompilerOps.Punt => {
FileParmOps.ClearAList[];
Finalize[userAbort];
GOTO punt;
};
ABORTED => {
userAbort ← TRUE;
FileParmOps.ClearAList[];
Finalize[userAbort];
GOTO truncateList;
};
];
};
-- end ENABLE UNWIND => FileParmOps.ClearAList[];
FileParmOps.ClearAList[];
};
-- end ENABLE UNWIND => Finalize[];
Finalize[userAbort];
SELECT WriteResults[cmd.out, moduleStartTime !
IO.Error =>
CONTINUE]
FROM
errors => errors ← TRUE;
warnings => warnings ← TRUE;
ENDCASE;
IF endItem # NIL THEN endItem[sourceName, out, outcome];
EXITS
globalSwitches => {
objectName ← NIL;
sense ← TRUE;
FOR i:
INT
IN [0..switches.Length[])
DO
c: CHAR = switches.Fetch[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 ← NIL;
args ← NIL;
};
noSource => {
log.Put[IO.rope[" -- source not found\n"], IO.time[]];
errors ← TRUE; parms.nErrors ← 1;
args ← NIL;
};
badSemantics => {
objectName ← NIL; errors ← TRUE;
log.PutRope[" -- Illegal command"];
args ← NIL;
};
END;
Here after completion of subcommand
sourceName ← rootName ← objectName ← errorName ← NIL;
parms.objectName ← NIL;
results ← NIL;
NewLogLine[];
IF userAbort THEN {log.PutRope["\n... command aborted\n"]; GOTO truncateList};
IF (errors OR warnings) AND localPause THEN GOTO truncateList;
REPEAT
badSyntax => {IO.PutRope[log, "\n-- Illegal syntax"]; errors ← TRUE};
truncateList => switchDefaults['p] ← TRUE;
punt => {
-- was Finalize[]; , but this is done by new UNWIND catch phrase
errors ← TRUE;
[] ← WriteResults[cmd.out, moduleStartTime ! IO.Error => CONTINUE];
NewLogLine[];
};
ENDLOOP;
Here after completion of all subcommands
}; -- end ENABLE UNWIND => log.Close[]
};
-- end ENABLE UNWIND => CompilerOps.Stop[]
CompilerOps.Stop[];
};
-- end ENABLE UNWIND => FileParmOps.Finalize[]
FileParmOps.Finalize[];
WriteClosing[cmd.out, compilerStartTime, moduleCount];
log.Close[]; log ← NIL;
IF userAbort OR errors OR warnings THEN result ← $Failure ELSE result ← $Success;
IF useLog
THEN FileViewerOps.ShowLog[
fileName: FS.ExpandName["Compiler.log", wDir].fullFName,
destroyIt: result = $Success AND destroyLogOnSuccess,
createIconic: createIconic,
blinkIfIconic: blinkIfIconic
];
END;
-- end catch phrase to release the resource and reset the process priority
Process.SetPriority[priority];
[] ← Resource.Release[$Compiler];
}; -- end Compile
should be in Rope:
ReplaceAll:
PROC [old, new, in: Rope.
ROPE]
RETURNS [changed: Rope.
ROPE] = {
olen: INT = old.Length[];
nlen: INT = new.Length[];
first: INT ← 0;
changed ← in;
WHILE (first ← Rope.Find[s1: changed, pos1: first, s2: old, case:
TRUE]) >= 0
DO
changed ← Rope.Replace[base: changed, start: first, len: olen, with: new];
first ← first + nlen;
ENDLOOP};
compiler sequencing
Initialize:
PROC = {
sourceStream ← FS.StreamOpen[sourceName];
parms.sourceStream ← sourceStream;
parms.source.version ← TimeStamp.Null;
parms.source.version.time ← BasicTime.ToNSTime[FS.FileInfo[name: sourceName, wDir: wDir].created]};
Finalize:
PROC [userAbort:
BOOL] = {
sourceFileName: Rope.ROPE;
IF objectStream # NIL THEN IO.Close[objectStream ! IO.Error => CONTINUE];
IF sourceStream #
NIL
THEN {
sourceFile: FS.OpenFile ← FS.OpenFileFromStream[sourceStream];
sourceFileName ← FS.GetName[sourceFile ! FS.Error => CONTINUE].fullFName;
IO.Close[sourceStream ! IO.Error => CONTINUE]};
IF errorStream #
NIL
AND errorStream # log
THEN IO.Close[errorStream ! IO.Error => CONTINUE];
objectStream ← sourceStream ← errorStream ← NIL;
IF sourceFileName #
NIL
THEN {
errorName = NIL iff useLog OR no errors or warnings, under normal circumstances
IF errorName =
NIL
THEN FileViewerOpsExtras.RemoveErrorLog[sourceFileName]
ELSE {
FileViewerOps.ShowLog[fileName: sourceFileName,
createIconic: createIconic, blinkIfIconic: blinkIfIconic];
IF viewSeparateLogs
THEN FileViewerOps.ShowLog[fileName: errorName, createIconic: createIconic, blinkIfIconic: blinkIfIconic]
ELSE FileViewerOpsExtras.AttachErrorLog[sourceFileName];
};
};
IF userAbort
OR parms.nErrors # 0
THEN {
IF objectName #
NIL
THEN {
DO
FS.Delete[name: objectName, wDir: wDir
! FS.Error => IF error.group = lock OR error.code = $unknownFile THEN EXIT];
ENDLOOP;
};
};
IF errorName =
NIL
THEN {
errlogFileName: Rope.ROPE ← CommandUtil.SetExtension[rootName, "errlog"];
DO
FS.Delete[name: errlogFileName, wDir: wDir
! FS.Error => IF error.group = lock OR error.code = $unknownFile THEN EXIT];
ENDLOOP;
};
};
WriteHerald:
PROC [s: IO.STREAM, id: Rope.
ROPE] = {
CompilerOps.AppendHerald[s];
IO.PutRope[s, " (Cedar 5 Version)\n"];
IF id # NIL THEN {IO.Put[s, IO.rope[id], IO.rope[" -- "]]};
IO.Put[s, IO.time[], IO.rope["\n"]]};
WriteResults:
PROC [out:
IO.
STREAM, startTime: BasicTime.
GMT]
RETURNS[outcome: Outcome] = {
OPEN IO;
elapsed: INT;
log.Put[rope[sourceName], rope[" -- "]];
elapsed ← BasicTime.Period[from: startTime, to: BasicTime.Now[]];
IF parms.nErrors # 0
THEN {
log.Put[rope["aborted, "], int[parms.nErrors], rope[" errors"]];
IF parms.nWarnings # 0
THEN {
log.Put[rope[" and "], int[parms.nWarnings], rope[" warnings"]]};
IF ~useLog THEN log.Put[IO.rope[" on "], IO.rope[wDir], IO.rope[errorName]];
log.Put[rope[", seconds: "], int[elapsed]]}
ELSE {
log.Put[rope["source tokens: "], int[parms.sourceTokens]];
log.Put[rope[", seconds: "], int[elapsed]];
IF parms.objectBytes # 0
THEN {
log.Put[rope["\n code bytes: "], int[parms.objectBytes]];
log.Put[rope[", links: "], int[parms.linkCount]];
log.Put[rope[", frame size: "], int[parms.objectFrameSize]];
IF parms.matched THEN PutChar[log, '.]};
IF parms.nWarnings # 0
THEN {
NewLogLine[];
log.Put[int[parms.nWarnings], rope[" warnings"]];
IF log # NIL AND ~useLog THEN log.Put[IO.rope[" on "], IO.rope[errorName]]}};
write to the Commander output file
out.PutRope[" "];
IF parms.nErrors = 0
THEN out.PutRope["no errors"]
ELSE out.Put[IO.int[parms.nErrors], IO.rope[" errors"]];
IF parms.nWarnings # 0
THEN out.Put[IO.rope[", "], IO.int[parms.nWarnings], IO.rope[" warnings"]];
out.PutRope[".\n"];
RETURN[SELECT
TRUE
FROM
parms.nErrors # 0 => errors,
parms.nWarnings # 0 => warnings,
ENDCASE => ok]};
WriteClosing:
PROC [out:
IO.
STREAM, startTime: BasicTime.
GMT, moduleCount:
CARDINAL] = {
elapsed: INT;
out.PutRope["End of compilation\n"];
elapsed ← BasicTime.Period[from: startTime, to: BasicTime.Now[]];
IF moduleCount > 1
THEN
IO.Put[log, IO.rope["\nTotal elapsed seconds: "], IO.int[elapsed]];
NewLogLine[]};
NewLogLine: PROC = {log.PutRope["\n"]};
special output stream control
GetStream:
PROC [id: CompilerOps.StreamId]
RETURNS [s:
IO.
STREAM] = {
SELECT id
FROM
source => RETURN[sourceStream];
object => {
IF objectStream = NIL THEN objectStream ← NewOutputStream[objectName];
RETURN[objectStream];
};
log => {IF errorStream = NIL THEN ErrorInit[]; RETURN[errorStream]};
ENDCASE => ERROR;
};
NewOutputStream:
PROC [fileName: Rope.
ROPE]
RETURNS [stream: IO.STREAM] = {
stream ← FS.StreamOpen[fileName: fileName, accessOptions: $create]};
ErrorInit:
PROC = {
IF errorStream =
NIL
THEN
IF useLog THEN errorStream ← log
ELSE {
errorName ← CommandUtil.SetExtension[rootName, "errlog"];
errorStream ← FS.StreamOpen[fileName: errorName, accessOptions: $create];
WriteHerald[errorStream, errorName];
errorStream.PutChar['\n]}};
START HERE
Commander.Register["Compile", Compile, "Compile {-switches} xxx* - A simple syntax for compiling a list of cedar modules. (See 'ComplexCompile' for another syntax). Some useful switches are:
-b => bounds checking (default: TRUE -- don't turn this off unless you know what you are doing),
-d => call debugger on compiler error (else just log error),
-g => log goes to compiler.log (-~g => log goes to foo.errlog),
-j => cross-jump (default: FALSE -- don't turn this on unless you know what you are doing),
-n => nil checking (default: TRUE -- don't turn this off unless you know what you are doing),
-u => check for uninitialized variables,
-w => log warning messages (default: TRUE)
Switches can be negated with '~' (i.e. '-~bj~n'). The complete list of switches is in Sequencer.DefaultSwitches. The defaults can be set in your user profile with Compiler.Switches.", $Vanilla];
Commander.Register["ComplexCompile", Compile, "ComplexCompile /j-b-n BcdTreeOps ← ProtoTreeOps[Literals: BcdSymbols, Symbols: BcdSymbols, Tree: BcdTree] . . . - Allows a parameterized compilation. Switches are marked with '/ and negated with '-. (See 'Compile' for more on switches.)", $Complex];
}.
CHANGE LOG
Edited on July 26, 1984 9:44:09 am PDT, by Kupfer
NIL out sourceName at the start of each subcommand. This fixes a bug where, say, an FS error in the middle of your compilation forces you to rollback in order to compile anything again. One wonders what else should be NIL'ed out as well.
changes to: Compile