DIRECTORY
Basics: TYPE USING [DIVMOD, LongDivMod],
BasicTime: TYPE USING [GMT, Now, Period],
Commander: TYPE USING [Handle, Register],
CommandUtil:
TYPE
USING [
PairList, Echo, Failed, GetNth, ListLength, Parse, SetExtension],
CompilerOps:
TYPE
USING [
LetterSwitches, StreamId, Transaction,
AppendHerald, DefaultSwitches, DoTransaction, Punt, Start, Stop],
FileIO: TYPE USING [StreamFromOpenFile],
FileParms: TYPE USING [BindingProc, nullActual],
FileParmOps: TYPE USING [ClearAList, Finalize, Initialize, SetAList],
FS: TYPE USING [Close, EnumerateForInfo, InfoProc, nullOpenFile, OpenFile],
IO: TYPE USING [card, Close, int, Put, PutChar, PutF, PutRope, rope, SetLength, STREAM, time, UserAborted],
OSMiscOps: TYPE USING [DeleteFile, FindFile],
Rope: TYPE USING [Fetch, Find, Length, ROPE, Substr],
TimeStamp: TYPE USING [Null],
UnsafeStorage: TYPE USING [NewUZone, FreeUZone];
StreamHandle: TYPE = IO.STREAM;
log: StreamHandle ← NIL;
useLog: BOOL; -- for compiler.log for error reporting
SetTypescript: PROC = {IF log = NIL THEN log ← NewOutputStream["Compiler.Log"]};
NewLine: PROC = {log.PutRope["\n"]};
NewOutputStream:
PROC [s: Rope.
ROPE]
RETURNS [stream: StreamHandle] = {
file: FS.OpenFile;
file ← OSMiscOps.FindFile[s, write];
stream ← FileIO.StreamFromOpenFile[file];
IO.SetLength[stream, 0]};
NewInputStream:
PROC [s: Rope.
ROPE]
RETURNS [file: FS.OpenFile, stream: StreamHandle, createTime: BasicTime.GMT] = {
GetCreated: FS.InfoProc = TRUSTED {createTime ← created; RETURN[continue: TRUE]};
file ← OSMiscOps.FindFile[s, read];
FS.EnumerateForInfo[s, GetCreated];
stream ← FileIO.StreamFromOpenFile[file]};
WriteHerald:
PROC [s: StreamHandle, id: Rope.
ROPE] = {
CompilerOps.AppendHerald[s];
IO.PutRope[s, " (Pilot Version)/n"];
IF id # NIL THEN {IO.Put[s, IO.rope[id], IO.rope[" -- "]]};
IO.Put[s, IO.time[], IO.rope["/n"]]};
WriteTime:
PROC [time:
LONG
CARDINAL] = {
hr, min, sec: CARDINAL;
[min, sec] ← Basics.LongDivMod[time, 60];
[hr, min] ← Basics.DIVMOD[min, 60];
SELECT
TRUE
FROM
hr # 0 => log.PutF["%d:%02d:%02d", IO.card[hr], IO.card[min], IO.card[sec]];
min # 0 => log.PutF["%d:%02d", IO.card[min], IO.card[sec]];
ENDCASE => log.PutF["%d", IO.card[sec]]};
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];
IO.PutChar[errorStream, '\n]}};
MakeErrorName:
PROC [root: Rope.
ROPE]
RETURNS [Rope.
ROPE] = {
RETURN [CommandUtil.SetExtension[root, "errlog"]]};
GetStream:
PROC [id: CompilerOps.StreamId]
RETURNS [s:
IO.
STREAM] = {
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};
Initialize:
PROC = {
created: BasicTime.GMT;
[sourceFile, sourceStream, created] ← NewInputStream[sourceName];
parms.sourceStream ← sourceStream;
parms.source.version ← TimeStamp.Null;
parms.source.version.time ← LOOPHOLE[created]};
Finalize:
PROC [started:
BOOL] = {
IF objectStream # NIL THEN IO.Close[objectStream];
IF sourceStream # NIL THEN IO.Close[sourceStream];
IF errorStream # NIL AND errorStream # log THEN IO.Close[errorStream];
objectStream ← sourceStream ← errorStream ← NIL;
IF sourceFile # FS.nullOpenFile THEN FS.Close[sourceFile];
sourceFile ← FS.nullOpenFile;
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;
IO.PutRope[log, " on "];
IO.PutRope[log, rootName]; IO.PutRope[log, ".errlog"]};
WriteClosing:
PROC [cmdr: Commander.Handle, startTime: BasicTime.
GMT] = {
OPEN IO;
elapsed: BasicTime.GMT;
Put[log, rope[sourceName], rope[" -- "]];
elapsed ← LOOPHOLE[BasicTime.Period[BasicTime.Now[], startTime]];
IF parms.nErrors # 0
THEN {
errors ← TRUE;
Put[log, rope["aborted, "], int[parms.nErrors], rope["errors"]];
IF parms.nWarnings # 0
THEN {
warnings ← TRUE;
Put[log, rope[" and "], int[parms.nWarnings], rope[" warnings"]]};
WriteErrlogName[];
Put[log, rope[", time: "], time[elapsed]]}
ELSE {
Put[log, rope["source tokens: "], int[parms.sourceTokens]];
Put[log, rope[", time: "], time[elapsed]];
IF parms.objectBytes # 0
THEN {
Put[log, rope["\n code bytes: "], int[parms.objectBytes]];
Put[log, rope[", links: "], int[parms.linkCount]];
Put[log, rope[", frame size: "], int[parms.objectFrameSize]];
IF parms.matched THEN PutChar[log, '.]};
IF parms.nWarnings # 0
THEN {
warnings ← TRUE; NewLine[];
Put[log, int[parms.nWarnings], rope[" warnings"]];
WriteErrlogName[]}};
write to the Commander output file
IF parms.nErrors = 0
THEN cmdr.out.PutRope["no errors"]
ELSE cmdr.out.Put[IO.int[parms.nErrors], IO.rope[" errors"]];
IF parms.nWarnings # 0
THEN cmdr.out.Put[IO.rope[", "], IO.int[parms.nWarnings], IO.rope[" warnings"]];
cmdr.out.PutRope["/n"]};
SELECT TRUE FROM
userAbort => aborted,
parms.nErrors # 0 => IF parms.nWarnings # 0 THEN errorsAndWarnings ELSE errors,
parms.nWarnings # 0 => warnings,
ENDCASE => ok;
StopCompiler:
PROC [cmdr: Commander.Handle, startTime: BasicTime.
GMT] = {
elapsed: BasicTime.GMT;
cmdr.out.PutRope["End of compilation/n"];
elapsed ← LOOPHOLE[BasicTime.Period[BasicTime.Now[], startTime]];
IF moduleCount > 1
THEN
IO.Put[log, IO.rope["\nTotal elapsed time: "], IO.time[elapsed]];
NewLine[]; IO.Close[log]; log ← NIL};
parms: REF CompilerOps.Transaction = NEW[CompilerOps.Transaction];
standardDefaults: CompilerOps.LetterSwitches = CompilerOps.DefaultSwitches[];
switchDefaults: CompilerOps.LetterSwitches;
sourceName, objectName, errorName: Rope.ROPE ← NIL;
rootName: Rope.ROPE ← NIL;
sourceFile: FS.OpenFile ← FS.nullOpenFile;
sourceStream, objectStream, errorStream: StreamHandle ← NIL;
errors, warnings: BOOL ← FALSE;
moduleCount: CARDINAL;
SetRoot:
PROC [s: Rope.
ROPE]
RETURNS [root: Rope.
ROPE] = {
dotIndex: INT ← Rope.Find[s, "."];
RETURN[
IF dotIndex > -1
THEN Rope.Substr[s, 0, dotIndex]
ELSE s]};
* * * * * * M A I N B O D Y C O D E * * * * * *
Compile:
SAFE
PROC [cmdr: Commander.Handle] =
TRUSTED {
StartPass:
PROC [pass:
CARDINAL]
RETURNS [goOn:
BOOL] = {
cmdr.out.PutRope[" ."];
userAbort ← cmdr.in.UserAborted[];
IF userAbort THEN cmdr.out.PutRope[" aborted."];
RETURN [~userAbort]};
userAbort: BOOL; -- set by ^DEL
compilerStartTime, moduleStartTime: BasicTime.GMT;
scratchZone: UNCOUNTED ZONE ← UnsafeStorage.NewUZone[];
switchDefaults ← CompilerOps.DefaultSwitches[];
parms.fileParms ← FileParmOps.Initialize[scratchZone];
CompilerOps.Start[scratchZone];
compilerStartTime ← BasicTime.Now[];
moduleCount ← 0;
userAbort ← FALSE;
do the compilation
SetTypescript[];
WriteHerald[log, NIL];
errors ← warnings ← FALSE;
DO
first: BOOL ← TRUE;
args, results: CommandUtil.PairList;
switches: Rope.ROPE ← NIL;
localPause: BOOL;
sense: BOOL;
BEGIN
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[command: cmdr.commandLine
! CommandUtil.Failed => {GO TO badSyntax}];
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 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:
INT
IN [0..switches.Length[])
DO
c: CHAR = switches.Fetch[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};
sourceName ← CommandUtil.SetExtension[sourceName, "mesa"];
parms.source.locator ← sourceName;
IF CommandUtil.ListLength[results] # 0
THEN {
objectName ← CommandUtil.GetNth[list: results, n: 0, delete: TRUE];
results ← NIL}
ELSE objectName ← rootName;
parms.objectName ← CommandUtil.SetExtension[objectName, "bcd"];
parms.objectFile ← FS.nullOpenFile;
moduleCount ← moduleCount + 1;
feedback
cmdr.out.Put[IO.rope["Compiling: "], IO.rope[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; cmdr.out.PutChar['/]};
IF sd THEN cmdr.out.PutChar['-];
cmdr.out.PutChar['c]};
ENDLOOP;
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: "$", formalType: NIL, binder: BindPattern];
END;
NewLine[]; moduleStartTime ← BasicTime.Now[];
CompilerOps.DoTransaction[parms ! CompilerOps.Punt => {GO TO punt}];
Finalize[TRUE];
FileParmOps.ClearAList[];
WriteClosing[cmdr, moduleStartTime];
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};
noSource => {
IO.Put[log, IO.rope[" -- source not found\n"], IO.time[]];
errors ← TRUE; parms.nErrors ← 1;
args ← NIL};
badSemantics => {
objectName ← NIL; errors ← TRUE;
IO.PutRope[log, " -- Illegal command"]};
END;
NewLine[];
IF userAbort
THEN {
IO.PutRope[log, "\n... command aborted\n"]; GO TO truncateList};
IF (errors OR warnings) AND localPause THEN GO TO truncateList;
REPEAT
badSyntax => {
IO.PutRope[log, "\n-- Illegal syntax"]; errors ← TRUE};
truncateList => switchDefaults['p] ← TRUE;
punt => {Finalize[TRUE]; WriteClosing[cmdr, moduleStartTime]; NewLine[]};
ENDLOOP;
StopCompiler[cmdr, compilerStartTime];
CompilerOps.Stop[];
FileParmOps.Finalize[];
UnsafeStorage.FreeUZone[scratchZone]};
RETURN [SELECT TRUE FROM
userAbort => aborted,
errors => errors,
warnings => warnings,
ENDCASE => ok]};
Commander.Register["Compiler", Compile, "Compile a list of modules."];
}.