UsingDependenciesImpl:
CEDAR
PROGRAM
IMPORTS BasicTime, IO, FS, FileNames, Commander, CommandTool, Interpreter, RefText, Rope, List, TiogaAccess, SymTab
~ 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 ← 2*1024;
CreateScanner:
PROC
RETURNS [scanner: Scanner] ~ {
scanner ← NEW[ScannerRep];
scanner.inputBuffer ← NEW[TEXT[inputBufferSize]];
};
SetScanner:
PROC [scanner: Scanner, stream:
IO.
STREAM] ~ {
scanner.stream ← stream;
scanner.tokenKind ← whitespace;
scanner.locationOfBufferStart ← IO.GetIndex[stream];
scanner.tokenStart ← 0;
scanner.tokenLength ← 0;
scanner.inputBuffer.length ← 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: INT ← LAST[INT];
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;
scanner.tokenKind ← string;
};
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]];
};
AppendID:
PROC [text:
REF
TEXT, scanner: Scanner]
RETURNS [
REF
TEXT] = {
RETURN [RefText.Append[to: text, from: scanner.inputBuffer, start: scanner.tokenStart, len: scanner.tokenLength]]
};
Pair:
TYPE ~
RECORD [
member: ROPE,
owner: ROPE
];
ParseError:
ERROR [sourceLoc:
INT] =
CODE;
RaiseError:
PROC [scanner: Scanner] = {
ParseError[scanner.locationOfBufferStart+scanner.tokenStart];
};
RecordDependencies:
PROC [scanner: Scanner, stem:
ROPE, cmd: Commander.Handle, symTab: SymTab.Ref] ~ {
GetTok:
PROC ~ {
GetToken[scanner];
WHILE scanner.tokenKind = comment
OR scanner.tokenKind = whitespace
OR scanner.tokenKind = newline
DO
GetToken[scanner];
ENDLOOP;
};
text: REF TEXT ← RefText.ObtainScratch[100];
GetTok[];
UNTIL scanner.tokenKind = eof
OR Match[scanner, "DIRECTORY"]
DO
GetToken[scanner];
ENDLOOP;
IF scanner.tokenKind = eof THEN RaiseError[scanner];
GetTok[];
IF scanner.tokenKind = special AND scanner.inputBuffer[scanner.tokenStart] = '. THEN NULL
ELSE {
UNTIL Match[scanner, ";"]
DO
text.length ← 0;
text ← AppendID[text, scanner];
GetTok[];
IF Match[scanner, ":"]
THEN {
GetTok[];
IF NOT Match[scanner, "FROM"] THEN RaiseError[scanner];
GetTok[];
IF scanner.tokenKind # string THEN RaiseError[scanner];
text.length ← 0;
text ← RefText.Append[to: text, from: scanner.inputBuffer, start: scanner.tokenStart+1, len: scanner.tokenLength-2];
GetTok[];
};
IF NOT Match[scanner, "USING"] THEN RaiseError[scanner];
GetTok[];
IF NOT Match[scanner, "["] THEN RaiseError[scanner];
GetTok[];
UNTIL Match[scanner, "]"]
DO
savedLength: NAT ← text.length;
val: REF LIST OF ROPE ← NIL;
text ← RefText.AppendChar[text, '.];
text ← AppendID[text, scanner];
TRUSTED {
val ← NARROW[symTab.Fetch[LOOPHOLE[text]].val];
};
IF val =
NIL
THEN {
val ← NEW[LIST OF ROPE ← NIL];
[] ← symTab.Store[Rope.FromRefText[text], val];
};
val^ ← CONS[stem, val^];
GetTok[];
IF Match[scanner, ","] THEN GetTok[]
ELSE IF NOT Match[scanner, "]"] THEN RaiseError[scanner];
text.length ← savedLength;
ENDLOOP;
GetTok[];
IF Match[scanner, ","] THEN GetTok[]
ELSE IF NOT Match[scanner, ";"] THEN RaiseError[scanner];
ENDLOOP;
};
RefText.ReleaseScratch[text];
};
Compare:
PROC [ref1:
REF
ANY, ref2:
REF
ANY]
RETURNS [Basics.Comparison] ~ {
r1: LIST OF ROPE ← NARROW[ref1];
r2: LIST OF ROPE ← NARROW[ref2];
RETURN [Rope.Compare[r1.first, r2.first, FALSE]]
};
WriteResults:
PROC [stream:
IO.
STREAM, symTab: SymTab.Ref, cmd: Commander.Handle] ~ {
list: LIST OF REF ← NIL;
EachPair:
PROC [key:
ROPE, val:
REF]
RETURNS [quit:
BOOL ←
FALSE] ~ {
refList: REF LIST OF ROPE ← NARROW[val];
ropes: LIST OF ROPE ← refList^;
ropes ← CONS[key, ropes];
list ← CONS[ropes, list];
};
[] ← symTab.Pairs[EachPair];
list ← List.Sort[list, Compare];
FOR p:
LIST
OF
REF ← list, p.rest
UNTIL p =
NIL
DO
r: LIST OF ROPE ← NARROW[p.first];
s: LIST OF ROPE ← DReverse[r.rest];
r.rest ← NIL;
stream.PutRope[r.first];
stream.PutRope[": "];
WHILE s #
NIL
DO
stream.PutRope[s.first];
stream.PutChar[' ];
{t: LIST OF ROPE ← s; s ← s.rest; t.rest ← NIL};
ENDLOOP;
IF doImpls THEN stream.PutRope[FindImplementor[r.first]];
stream.PutChar['\n];
ENDLOOP;
};
FindImplementor:
PROC [def:
ROPE]
RETURNS [imp:
ROPE] ~ {
this Proc calls the Interpreter to evaluate a rope of the form Interface.ProcName. If a rope of the form XXX.ProcName comes back, it is assumed that XXX is the implementor of Interface. Since it is possible to get other things back from the Interpreter, like types or errors, some defaults are returned ( -- or ??) instead of the interpreted result.
deftail, result: ROPE ← NIL;
noResult: BOOL ← TRUE;
[result, , noResult] ← Interpreter.EvaluateToRope[def];
IF noResult OR result=NIL THEN RETURN["??"];
deftail ← FileNames.Tail[def, '.];
result ← IF Rope.Equal[deftail, FileNames.Tail[result, '.], TRUE] THEN Rope.Substr[base: result, start: 0, len: Rope.Length[result]-Rope.Length[deftail]-1] ELSE "!!";
RETURN[IF Rope.Length[result]=0 THEN "!!" ELSE result];
};
DReverse:
PROC [old:
LIST
OF
ROPE]
RETURNS [new:
LIST
OF
ROPE ←
NIL] ~ {
Destructive.
WHILE old #
NIL
DO
t: LIST OF ROPE ~ old;
old ← t.rest;
t.rest ← new;
new ← t;
ENDLOOP;
};
Break:
PROC [char:
CHAR]
RETURNS [
IO.CharClass] = {
IF char = '← OR char = '; THEN RETURN [break];
IF char = ' OR char = ' OR char = ', OR char = '\n THEN RETURN [sepr];
RETURN [other];
};
GetCmdToken:
PROC [stream:
IO.
STREAM]
RETURNS [rope:
ROPE ←
NIL] ~ {
rope ← IO.GetTokenRope[stream, Break ! IO.EndOfStream => CONTINUE].token;
};
FindNewer:
PROC [fileName:
ROPE, newerThan: BasicTime.
GMT]
RETURNS [fullName:
ROPE ←
NIL] ~ {
full: ROPE ← NIL;
infoProc: FS.InfoProc ~ {IF BasicTime.Period[from: newerThan, to: created] > 0 THEN {fullName ← fullFName; continue ← FALSE}};
FS.EnumerateForInfo[fileName, infoProc];
};
UsingDependenciesCommand: Commander.CommandProc ~ {
Reformat:
PROC [filename:
ROPE] ~ {
EndNode:
PROC [delta:
INTEGER ← 0, format:
ATOM ←
NIL] = {
tc.endOfNode ← TRUE;
tc.char ← '\n;
tc.format ← format;
tc.deltaLevel ← delta;
TiogaAccess.Put[writer, tc];
tc.endOfNode ← FALSE;
};
PutCharB: Rope.ActionType = {
[c: CHAR] RETURNS [quit: BOOL ← FALSE]
tc.char ← c;
TiogaAccess.Put[writer, tc];
};
PutRope:
PROC [rope:
ROPE] = {
[] ← Rope.Map[base: rope, action: PutCharB];
};
PutRopeItalic:
PROC [rope:
ROPE] = {
tc.looks['i] ← TRUE;
[] ← Rope.Map[base: rope, action: PutCharB];
tc.looks['i] ← FALSE;
};
PutRopeBold:
PROC [rope:
ROPE] = {
tc.looks['b] ← TRUE;
[] ← Rope.Map[base: rope, action: PutCharB];
tc.looks['b] ← FALSE;
};
tc: TiogaAccess.TiogaChar ← [
charSet: 0,
char: '\n,
looks: ALL[FALSE],
format: $code,
comment: FALSE,
endOfNode: FALSE,
deltaLevel: 0,
propList: List.PutAssoc[key: $FromTiogaFile, val: $Yes, aList: NIL]
propList: NIL
];
nextS: IO.STREAM;
in: IO.STREAM;
nextRope: ROPE;
ropeList, newList: LIST OF ROPE;
writer: TiogaAccess.Writer ← TiogaAccess.Create[];
in ← FS.StreamOpen[fileName: filename, accessOptions: $read ! FS.Error => {result ← $Failure; msg ← error.explanation; CONTINUE}];
IF in = NIL THEN RETURN;
The following code assumes there are lines with one or more ropes per line. The first rope is of the form Interface.ProcName. Succeeding ropes up to the last rope in the line are assumed to be names of Implmentations which use that Interface.ProcName. The last rope is assumed to be the name of the Implementor of the Interface.ProcName.
UNTIL
IO.EndOf[in]
DO
newList ← ropeList ← NIL;
nextS ← IO.RIS[IO.GetLineRope[stream: in]]; -- get next rope from input
nextRope ← IO.GetTokenRope[nextS, IO.TokenProc].token; -- first token, boldface
PutRopeBold[Rope.Concat[nextRope, ":"]];
UNTIL
IO.EndOf[nextS]
DO ropeList ←
CONS[
IO.GetTokenRope[nextS,
IO.TokenProc !
IO.EndOfStream =>
CONTINUE;].token, ropeList];
ENDLOOP;
IF ropeList#
NIL
THEN {
newList ← DReverse[ropeList]; -- destructive reverse
nextRope ← IF newList.rest=NIL THEN newList.first ELSE newList.rest.first; -- initialize in case never do following loop
FOR r:
LIST
OF
ROPE ← newList, r.rest
UNTIL r.rest=
NIL
DO
-- do all but last rope
PutRope[Rope.Concat[" ", r.first]];
nextRope ← r.rest.first;
ENDLOOP;
IF doImpls THEN PutRopeItalic[Rope.Concat[" ", nextRope]] -- last rope is Implementor, italics
ELSE PutRope[Rope.Concat[" ", nextRope]]; -- last rope is not Implementor
EndNode[delta: 0, format: $code];
};
ENDLOOP;
TiogaAccess.WriteFile[writer, filename];
};
switchChar: CHAR = '-;
gets: ROPE ← NIL;
outputStream: IO.STREAM ← NIL;
symTab: SymTab.Ref ← SymTab.Create[997, TRUE];
scanner: Scanner ← CreateScanner[];
c: IO.STREAM ← IO.RIS[cmd.commandLine];
outputName: ROPE ← GetCmdToken[c];
IF outputName=NIL OR Rope.Equal[outputName, ""] THEN RETURN[$Failure, docRope];
doImpls ← reformat ← FALSE;
WHILE Rope.Fetch[base: outputName, index: 0]=switchChar
DO
FOR index:
INT
IN [1..Rope.Length[outputName])
DO
SELECT Rope.Fetch[base: outputName, index: index]
FROM
'i => doImpls ← TRUE;
'f => reformat ← TRUE;
ENDCASE;
ENDLOOP;
outputName ← GetCmdToken[c];
ENDLOOP;
gets ← GetCmdToken[c];
IF NOT gets.Equal["←"] THEN RETURN[$Failure, docRope];
outputStream ← FS.StreamOpen[fileName: outputName, accessOptions: $create, keep: 2 ! FS.Error => {result ← $Failure; msg ← error.explanation; CONTINUE}];
IF outputStream = NIL THEN RETURN;
FOR stem:
ROPE ← GetCmdToken[c], GetCmdToken[c]
UNTIL stem.Length = 0
DO
pattern: ROPE ← IF Rope.Find[stem, ".bcd", 0, FALSE] < 0 THEN Rope.Concat[stem, ".bcd"] ELSE stem;
ForEachFile:
PROC [fullFName:
ROPE]
RETURNS [continue:
BOOLEAN ←
TRUE] ~ {
bcdDate: BasicTime.GMT ~ FS.FileInfo[name: fullFName, remoteCheck: FALSE].created;
base: ROPE;
cp: FS.ComponentPositions;
useName: ROPE ← Rope.Concat[base, ".usingList"];
[fullFName, cp] ← FS.ExpandName[fullFName];
base ← fullFName.Substr[cp.base.start, cp.base.length];
useName ← FindNewer[Rope.Concat[base, ".usingList"], bcdDate];
IF useName=
NIL
THEN {
result ← CommandTool.DoCommand[Rope.Concat["UsingList ", fullFName], cmd];
useName ← Rope.Concat[base, ".usingList"];
}
ELSE {
cmd.out.PutF["Using old %g\n", IO.rope[useName]];
result ← NIL;
};
IF result = $Failure THEN NULL
ELSE {
stream: IO.STREAM ← NIL;
stream ← FS.StreamOpen[useName ! FS.Error => {cmd.out.PutRope[error.explanation]; cmd.out.PutChar['\n]; CONTINUE}];
IF stream #
NIL
THEN {
SetScanner[scanner, stream];
RecordDependencies[scanner, base, cmd, symTab];
stream.Close;
};
};
};
IF Rope.Find[pattern, "*"] >= 0 THEN FS.EnumerateForNames[pattern, ForEachFile ! FS.Error => {msg ← error.explanation; result ← $Failure; CONTINUE}]
ELSE [] ← ForEachFile[pattern ! FS.Error => {msg ← error.explanation; result ← $Failure; CONTINUE}];
IF result = $Failure THEN RETURN;
ENDLOOP;
WriteResults[outputStream, symTab, cmd];
outputStream.Close;
result ← CommandTool.DoCommand[Rope.Concat["ReadIndent ", outputName], cmd];
IF reformat THEN Reformat[outputName];
cmd.out.PutF["%g written.\n", IO.rope[outputName]];
};
reformat: BOOL ← FALSE;
doImpls: BOOL ← FALSE;
docRope: ROPE ~ "Create listing of procedure-level dependencies from bcds ([switches] <outputFile> ← <list of bcd patterns>)\n";
Commander.Register["UsingDependencies", UsingDependenciesCommand, docRope];
END.