ExpungeOpensImpl.mesa
Copyright (C) 1984, Xerox Corporation. All rights reserved.
Michael Plass, March 14, 1985 9:46:35 am PST
DIRECTORY
Ascii USING [CR, LF, SP, TAB],
BasicTime USING [GMT],
Commander USING [CommandProc, Handle, Register],
CommandTool USING [DoCommand],
FS USING [Delete, EnumerateForNames, Error, GetInfo, GetName, OpenFile, OpenFileFromStream, StreamOpen],
IO USING [Close, EndOf, EndOfStream, GetBlock, GetIndex, GetTokenRope, PutBlock, PutChar, PutRope, RIS, SetIndex, STREAM],
Rope USING [Concat, Equal, Fetch, Find, FromProc, Length, ROPE, Substr];
ExpungeOpensImpl: CEDAR PROGRAM
IMPORTS IO, FS, Rope, Commander, CommandTool
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
Scanner: TYPE ~ REF ScannerRep;
ScannerRep: TYPE ~ RECORD [
stream: IO.STREAM,
tokenKind: Kind,
inputBuffer: REF TEXT,
locationOfBufferStart: INT,
tokenStart: NAT,
tokenLength: NAT
];
inputBufferSize: NAT ← 16*1024;
CreateScanner: PROC [stream: IO.STREAM] RETURNS [scanner: Scanner] ~ {
scanner ← NEW[ScannerRep ← [
stream: stream,
tokenKind: whitespace,
inputBuffer: NEW[TEXT[inputBufferSize]],
locationOfBufferStart: IO.GetIndex[stream],
tokenStart: 0,
tokenLength: 0
]];
};
Kind: TYPE ~ {identifier, special, charconst, string, whitespace, newline, comment, eof};
KindOf: PROC [c: CHAR] RETURNS [Kind] ~ {
SELECT c FROM
IN ['a..'z], IN ['A..'Z], IN ['0..'9], '$ => RETURN [identifier];
Ascii.SP, Ascii.TAB, Ascii.LF => RETURN [whitespace];
Ascii.CR => RETURN [newline];
ENDCASE => RETURN [special];
};
breakLoc: INTLAST[INT];
breakCount: INT ← 0;
GetCharConstant: PROC [scanner: Scanner] ~ {
IF scanner.tokenStart>=scanner.inputBuffer.length-10 THEN RefillBuffer[scanner];
scanner.tokenKind ← charconst;
scanner.tokenLength ← 2;
IF scanner.inputBuffer[scanner.tokenStart+1] = '\\ THEN {
IF scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] IN ['0..'7] THEN {
WHILE scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] IN ['0..'7] DO
scanner.tokenLength ← scanner.tokenLength + 1;
ENDLOOP;
}
ELSE scanner.tokenLength ← 3;
};
};
GetStringConstant: PROC [scanner: Scanner] ~ {
GetC: PROC ~ {
IF scanner.tokenStart+scanner.tokenLength>=scanner.inputBuffer.length-10 THEN RefillBuffer[scanner];
scanner.tokenLength ← scanner.tokenLength + 1;
IF scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] = '\\ THEN {
scanner.tokenLength ← scanner.tokenLength + 1;
IF scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] IN ['0..'7] THEN {
WHILE scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] IN ['0..'7] DO
scanner.tokenLength ← scanner.tokenLength + 1;
ENDLOOP;
}
ELSE scanner.tokenLength ← scanner.tokenLength + 1;
};
};
scanner.tokenLength ← 0;
GetC[];
UNTIL scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] = '" DO
GetC[];
ENDLOOP;
scanner.tokenLength ← scanner.tokenLength + 1;
};
GetComment: PROC [scanner: Scanner] ~ {
c: CHAR ← ' ; -- next char after the current end-of-token
cc: CHAR ← ' ; -- char after c
PeekCC: PROC ~ {
IF scanner.tokenStart+scanner.tokenLength+2>=scanner.inputBuffer.length THEN RefillBuffer[scanner];
c ← cc ← '\n;
IF scanner.tokenStart+scanner.tokenLength<scanner.inputBuffer.length THEN
c ← scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength];
IF scanner.tokenStart+scanner.tokenLength+1<scanner.inputBuffer.length THEN
cc ← scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength+1];
};
scanner.tokenLength ← 2;
PeekCC[];
UNTIL c = '\n OR (c = '- AND cc = '-) DO
scanner.tokenLength ← scanner.tokenLength + 1;
PeekCC[];
ENDLOOP;
IF (c = '- AND cc = '-) THEN scanner.tokenLength ← scanner.tokenLength + 2;
scanner.tokenKind ← comment;
};
GetToken: PROC [scanner: Scanner] ~ {
Gets the next token into inputBuffer[tokenStart] .. inputBuffer[tokenLength-1]
c: CHAR;
scanner.tokenStart ← scanner.tokenStart+scanner.tokenLength;
scanner.tokenLength ← 0;
IF scanner.tokenStart>=scanner.inputBuffer.length-1 THEN RefillBuffer[scanner];
IF scanner.inputBuffer.length = 0 THEN {scanner.tokenKind ← eof; RETURN};
c ← scanner.inputBuffer[scanner.tokenStart];
IF c = '' THEN {GetCharConstant[scanner]; RETURN};
IF c = '" THEN {GetStringConstant[scanner]; RETURN};
IF c = '- AND scanner.inputBuffer[scanner.tokenStart+1] = '- THEN {GetComment[scanner]; RETURN};
scanner.tokenKind ← KindOf[scanner.inputBuffer[scanner.tokenStart]];
IF scanner.tokenKind = special THEN {
scanner.tokenLength ← 1;
RETURN;
};
WHILE scanner.tokenStart+scanner.tokenLength<scanner.inputBuffer.length
AND KindOf[scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength]] = scanner.tokenKind DO
IF scanner.locationOfBufferStart+scanner.tokenStart+scanner.tokenLength = breakLoc THEN breakCount ← breakCount + 1;
scanner.tokenLength ← scanner.tokenLength + 1;
IF scanner.tokenStart + scanner.tokenLength = scanner.inputBuffer.length THEN {
IF IO.EndOf[scanner.stream] THEN RETURN;
RefillBuffer[scanner];
};
ENDLOOP;
};
RefillBuffer: PROC [scanner: Scanner] = {
residual: NAT ~ scanner.inputBuffer.length-scanner.tokenStart;
FOR i: NAT IN [0..residual) DO
scanner.inputBuffer[i] ← scanner.inputBuffer[i+scanner.tokenStart];
ENDLOOP;
scanner.inputBuffer.length ← residual;
scanner.locationOfBufferStart ← scanner.locationOfBufferStart+scanner.tokenStart;
scanner.tokenStart ← 0;
[] ← scanner.stream.GetBlock[block: scanner.inputBuffer, startIndex: residual, count: scanner.inputBuffer.maxLength-residual];
};
Match: PROC [scanner: Scanner, key: REF READONLY TEXT] RETURNS [equal: BOOLEAN] = {
i: NAT ← scanner.tokenStart;
IF key.length # scanner.tokenLength THEN RETURN [FALSE];
FOR j: NAT IN [0..key.length) DO
IF key[j]#scanner.inputBuffer[i] THEN RETURN [FALSE];
i←i+1;
ENDLOOP;
RETURN [TRUE]
};
MatchRope: PROC [scanner: Scanner, key: ROPE] RETURNS [equal: BOOLEAN] = {
i: NAT ← scanner.tokenStart;
IF key.Length # scanner.tokenLength THEN RETURN [FALSE];
FOR j: NAT IN [0..scanner.tokenLength) DO
IF key.Fetch[j]#scanner.inputBuffer[i] THEN RETURN [FALSE];
i←i+1;
ENDLOOP;
RETURN [TRUE]
};
IDRope: PROC [scanner: Scanner] RETURNS [rope: ROPE] = {
i: INTEGER ← scanner.tokenStart-1;
p: PROC RETURNS [CHAR] ~ {RETURN [scanner.inputBuffer[i←i+1]]};
IF scanner.tokenKind # identifier THEN RaiseError[scanner];
RETURN [Rope.FromProc[scanner.tokenLength, p]];
};
Pair: TYPE ~ RECORD [
member: ROPE,
owner: ROPE
];
ParseError: ERROR [sourceLoc: INT] = CODE;
RaiseError: PROC [scanner: Scanner] = {
ParseError[scanner.locationOfBufferStart+scanner.tokenStart];
};
GetUsingLists: PROC [stem: ROPE] RETURNS [list: LIST OF Pair] ~ {
stream: IO.STREAMFS.StreamOpen[Rope.Concat[stem, ".usingList"]];
scanner: Scanner ← CreateScanner[stream];
GetTok: PROC ~ {
GetToken[scanner];
WHILE scanner.tokenKind = comment OR scanner.tokenKind = whitespace OR scanner.tokenKind = newline DO
GetToken[scanner];
ENDLOOP;
};
GetTok[];
UNTIL scanner.tokenKind = eof OR Match[scanner, "DIRECTORY"] DO
GetToken[scanner];
ENDLOOP;
IF scanner.tokenKind = eof THEN RaiseError[scanner];
GetTok[];
UNTIL Match[scanner, ";"] DO
module: ROPE ~ IDRope[scanner];
GetTok[];
IF NOT Match[scanner, "USING"] THEN RaiseError[scanner];
GetTok[];
IF NOT Match[scanner, "["] THEN RaiseError[scanner];
GetTok[];
UNTIL Match[scanner, "]"] DO
element: ROPE ~ IDRope[scanner];
list ← CONS[[element, module], list];
GetTok[];
IF Match[scanner, ","] THEN GetTok[]
ELSE IF NOT Match[scanner, "]"] THEN RaiseError[scanner];
ENDLOOP;
GetTok[];
IF Match[scanner, ","] THEN GetTok[]
ELSE IF NOT Match[scanner, ";"] THEN RaiseError[scanner];
ENDLOOP;
stream.Close;
};
maxNest: INT ~ 200;
useCurlys: BOOLEANTRUE;
adjustLineBreaks: BOOLEANTRUE;
adjustIndentation: BOOLEANTRUE;
ExpungeOpensIn: PROC [fileName: ROPE, stem: ROPE, cmd: Commander.Handle] ~ {
idTable: LIST OF Pair ← GetUsingLists[stem];
stream: IO.STREAMFS.StreamOpen[fileName];
output: IO.STREAMFS.StreamOpen[fileName, $create];
BEGIN
ENABLE UNWIND => {
openFile: FS.OpenFile ~ FS.OpenFileFromStream[output];
fullFName: ROPE ~ FS.GetName[openFile].fullFName;
wantedCreatedTime: BasicTime.GMTFS.GetInfo[openFile].created;
output.Close[abort: TRUE];
FS.Delete[fullFName, wantedCreatedTime ! FS.Error => CONTINUE];
};
scanner: Scanner ~ CreateScanner[stream];
opens: ARRAY [0..maxNest) OF LIST OF ROPEALL[NIL];
startLine: ARRAY [0..maxNest) OF INTALL[0];
nest: INT ← 0;
afterDot: BOOLEANTRUE;
CopyToken: PROC ~ {
output.PutBlock[scanner.inputBuffer, scanner.tokenStart, scanner.tokenLength];
IF scanner.tokenKind # whitespace THEN somethingSinceNewLine ← TRUE;
};
Nest: PROC ~ {
nest ← nest + 1;
startLine[nest] ← lineNumber;
};
UnNest: PROC ~ {
opens[nest] ← NIL;
nest ← nest - 1;
};
GetTok: PROC ~ {
GetToken[scanner];
WHILE scanner.tokenKind = comment OR scanner.tokenKind = whitespace OR scanner.tokenKind = newline DO
CopyToken[];
GetToken[scanner];
ENDLOOP;
};
IsModuleName: PROC [id: ROPE] RETURNS [BOOLEAN] ~ {
IF id.Concat["Impl"].Equal[stem, FALSE] THEN RETURN [FALSE];
Leave opens for the normally-named exported interface alone.
FOR p: LIST OF Pair ← idTable, p.rest UNTIL p = NIL DO
IF p.first.owner.Equal[id] THEN RETURN [TRUE]
ENDLOOP;
RETURN [FALSE];
};
IsOpen: PROC [id: ROPE] RETURNS [BOOLEAN] ~ {
FOR i: INT DECREASING IN [0..nest] DO
FOR p: LIST OF ROPE ← opens[i], p.rest UNTIL p = NIL DO
IF id.Equal[p.first] THEN RETURN [TRUE];
ENDLOOP;
ENDLOOP;
RETURN [FALSE];
};
ParseOpen: PROC ~ {
didOpen: BOOLEANFALSE;
afterDot ← FALSE;
GetTok[];
UNTIL Match[scanner, ";"] DO
id: ROPE ← IDRope[scanner];
GetTok[];
IF Match[scanner, ":"] THEN {
a named open - leave it alone.
IF didOpen THEN output.PutRope[", "] ELSE output.PutRope["OPEN "];
didOpen ← TRUE;
somethingSinceNewLine ← TRUE;
output.PutRope[id];
CopyToken[]; -- the colon
GetTok[];
CopyToken[]; -- the name
GetTok[];
}
ELSE IF (Match[scanner, ","] OR Match[scanner, ";"]) AND IsModuleName[id] THEN {
opens[nest] ← CONS[id, opens[nest]];
}
ELSE {
Something unusual, like an expression
understood: BOOL ← (Match[scanner, ","] OR Match[scanner, ";"]);
IF didOpen THEN output.PutRope[", "] ELSE output.PutRope["OPEN "];
didOpen ← TRUE;
somethingSinceNewLine ← TRUE;
output.PutRope[id];
IF NOT understood THEN {CopyToken[]; RETURN};
};
IF Match[scanner, ","] THEN {
GetTok[];
}
ELSE IF NOT Match[scanner, ";"] THEN RaiseError[scanner];
ENDLOOP;
IF didOpen THEN {output.PutChar[';]; somethingSinceNewLine ← TRUE};
};
TryToInsertQualifier: PROC ~ {
FOR p: LIST OF Pair ← idTable, p.rest UNTIL p = NIL DO
IF MatchRope[scanner, p.first.member] AND IsOpen[p.first.owner] THEN {
somethingSinceNewLine ← TRUE;
output.PutRope[p.first.owner];
output.PutChar['.];
RETURN;
};
ENDLOOP;
};
EmitCurlyBegin: PROC ~ {
IF adjustLineBreaks AND NOT somethingSinceNewLine AND tokenKindBeforeNewLine = special THEN {
output.SetIndex[lastNewLineOutputIndex];
output.PutChar[' ];
};
output.PutChar['{];
somethingSinceNewLine ← TRUE;
afterDot ← FALSE;
Nest[];
};
EmitCurlyEnd: PROC ~ {
IF adjustLineBreaks AND adjustIndentation AND somethingSinceNewLine AND lineNumber # startLine[nest] THEN EmitNewLine[];
output.PutChar['}];
afterDot ← FALSE;
somethingSinceNewLine ← TRUE;
UnNest[];
};
EmitNewLine: PROC ~ {
tokenKindBeforeNewLine ← prevNonWhiteTokenKind;
lastNewLineOutputIndex ← output.GetIndex;
output.PutChar['\n];
IF adjustIndentation THEN {
FOR i: INT IN [0..nest) DO
output.PutChar['\t];
ENDLOOP;
IF specialNest THEN output.PutChar['\t];
};
somethingSinceNewLine ← FALSE;
};
lastNewLineOutputIndex: INT ← 0;
tokenKindBeforeNewLine: Kind ← whitespace;
prevNonWhiteTokenKind: Kind ← whitespace;
somethingSinceNewLine: BOOLEANFALSE;
lineNumber: INT ← 0;
specialNest: BOOLEANFALSE;
DO
IF scanner.tokenKind # whitespace THEN
prevNonWhiteTokenKind ← scanner.tokenKind;
GetToken[scanner];
SELECT scanner.tokenKind FROM
identifier => {
SELECT TRUE FROM
Match[scanner, "DIRECTORY"], Match[scanner, "PROGRAM"], Match[scanner, "MONITOR"] => specialNest ← TRUE;
Match[scanner, "BEGIN"] =>
{IF useCurlys THEN {EmitCurlyBegin[]; LOOP} ELSE Nest[]};
Match[scanner, "DO"], Match[scanner, "FROM"] => Nest[];
Match[scanner, "END"] =>
{IF useCurlys THEN {EmitCurlyEnd[]; LOOP} ELSE UnNest[]};
Match[scanner, "ENDLOOP"], Match[scanner, "ENDCASE"] => UnNest[];
Match[scanner, "OPEN"] => {ParseOpen[]; LOOP};
ENDCASE => {
IF afterDot THEN NULL
ELSE TryToInsertQualifier[];
};
afterDot ← FALSE;
CopyToken[];
};
special => {
char: CHAR ← scanner.inputBuffer[scanner.tokenStart];
IF char = '← OR char = '\251 THEN specialNest ← TRUE;
IF char = '; OR char = '~ OR char = '= OR char = '} THEN specialNest ← FALSE;
IF char = '{ OR char = '( OR char = '[ THEN Nest[]
ELSE IF char = '} THEN {EmitCurlyEnd[]; LOOP}
ELSE IF char = ') OR char = '] THEN UnNest[];
afterDot ← (char = '.);
CopyToken[];
};
charconst, string => {afterDot ← FALSE; CopyToken[]};
whitespace =>
{IF adjustIndentation AND NOT somethingSinceNewLine THEN NULL ELSE CopyToken[]};
comment => CopyToken[];
newline => {lineNumber ← lineNumber + 1; EmitNewLine[]};
eof => EXIT;
ENDCASE => ERROR;
ENDLOOP;
END;
stream.Close;
output.Close;
};
GetCmdToken: PROC [stream: IO.STREAM] RETURNS [rope: ROPENIL] ~ {
rope ← IO.GetTokenRope[stream ! IO.EndOfStream => CONTINUE].token;
};
ExpungeOpensCommand: Commander.CommandProc ~ {
c: IO.STREAMIO.RIS[cmd.commandLine];
FOR stem: ROPE ← GetCmdToken[c], GetCmdToken[c] UNTIL stem.Length = 0 DO
pattern: ROPE ← Rope.Concat[stem, ".mesa"];
ForEachFile: PROC [fullFName: ROPE] RETURNS [continue: BOOLEANTRUE] ~ {
base: ROPE ← fullFName.Substr[0, fullFName.Find[".mesa"]];
result ← CommandTool.DoCommand[Rope.Concat["SetKeep 6 ", fullFName], cmd];
IF result = $Failure THEN RETURN [FALSE];
result ← CommandTool.DoCommand[Rope.Concat["WriteMesaPlain ", fullFName], cmd];
IF result = $Failure THEN RETURN [FALSE];
result ← CommandTool.DoCommand[Rope.Concat["UsingList ", base], cmd];
IF result = $Failure THEN RETURN [FALSE];
ExpungeOpensIn[fullFName, base, cmd];
result ← CommandTool.DoCommand[Rope.Concat["TiogaMesa ", fullFName], cmd];
IF result = $Failure THEN RETURN [FALSE];
};
IF Rope.Find[pattern, "*"] >= 0 THEN FS.EnumerateForNames[pattern, ForEachFile ! FS.Error => {cmd.out.PutRope[error.explanation]; cmd.out.PutChar['\n]; result ← $Failure; CONTINUE}]
ELSE [] ← ForEachFile[pattern ! FS.Error => {cmd.out.PutRope[error.explanation]; cmd.out.PutChar['\n]; result ← $Failure; CONTINUE}];
IF result = $Failure THEN RETURN;
ENDLOOP;
};
Commander.Register["ExpungeOpens", ExpungeOpensCommand, "Attempt to remove OPENs from a Cedar program"];
END.