-- MDScanImpl.Mesa
-- last edit 17-Dec-81 12:59:49
-- last edit May 22, 1983 4:48 pm, Russ Atkinson
-- Pilot 6.0/ Mesa 7.0
-- scanning and parsing subroutines for the system modeller
DIRECTORY
Ascii: TYPE USING [CR, FF, TAB],
CWF: TYPE USING [SWF1, WF0, WF1, WF2, WF3],
File: TYPE USING [Capability],
LongString: TYPE USING [EqualString, EquivalentString],
MDSubr: TYPE USING [AddToDep, AddToDepends, AddToMod, ADepRecord, DepSeq, EntrySeq,
GetAnEntry, InsertOtherSym, IsAlpha, IsDigit, Look, lookNil, LookRecord,
LookSeq, ModInfo, ModType, StringSeq, Token, versionUnknown],
Space: TYPE USING [Create, Delete, Handle, Map, virtualMemory],
Stream: TYPE USING [Handle],
String: TYPE USING [CompareStrings, StringToDecimal],
Subr: TYPE USING [AllocateString, Any, CopyString, EndsIn, EnumerateDirectory, FreeString,
GetChar, GetCreateDateWithSpace, GetLine, GetString, LongZone, PackedTime,
strcpy, StripLeadingBlanks, SubStrCopy],
SystemInternal: TYPE USING [UniversalID],
Time: TYPE USING [Current];
MDScanImpl: PROGRAM
IMPORTS CWF, LongString, MDSubr, Space, String, Subr, Time
EXPORTS MDSubr = {
AliasSeqRecord: TYPE = RECORD[
size: CARDINAL ← 0,
body: SEQUENCE maxsize: CARDINAL OF AliasRecord
];
AliasRecord: TYPE = RECORD[
str: LONG STRING ← NIL,
val: LONG STRING ← NIL
];
-- MDS USAGE !!!
peektok: PUBLIC MDSubr.Token;
peekvalue: PUBLIC LONG UNSPECIFIED;
nextchar: CHAR;
tokstring: LONG POINTER TO ARRAY MDSubr.Token OF LONG STRING ← NIL;
aliasseq: LONG POINTER TO AliasSeqRecord ← NIL;
savestr: LONG STRING ← NIL;
toksave: LONG STRING ← NIL;
-- endof MDS USAGE
NALIASES: CARDINAL = 50;
MaxLineLength: CARDINAL = 600;
-- parse the stream handle, add that parent depends
-- on everything in the streamhandle
-- it turns out we ignore IMPORTS and EXPORTS since those entries must appear
-- in the DIRECTORY clause
-- if pmod = NIL then use depseq
ParseObject: PUBLIC PROC[sh: Stream.Handle, parent: CARDINAL,
entryseq: MDSubr.EntrySeq, pmod: MDSubr.ModInfo,
depseq: MDSubr.DepSeq, stringseq: MDSubr.StringSeq, sfn: LONG STRING] = {
tok: MDSubr.Token ← tokBAD;
tokvalue: LONG UNSPECIFIED;
str: LONG STRING;
child: CARDINAL;
stemp: STRING ← [100];
imp, isconfig, isdefns: BOOL ← FALSE;
savename: STRING ← [100];
ScanInit[sh];
IF pmod ~= NIL THEN {
pmod.isconfig ← pmod.isdefn ← FALSE;
pmod.impinx ← pmod.expinx ← pmod.dirinx ← 0;
};
WHILE tok ~= tokBEGIN AND tok ~= tokEOF DO
SELECT tok FROM
tokDIR => tok ← Direct[sh, parent, entryseq, pmod, depseq, sfn];
tokCONFIG => {
IF savename.length > 0 AND depseq ~= NIL THEN
depseq.modulename ← Subr.CopyString[savename, entryseq.entryzone];
isconfig ← TRUE;
[tok,tokvalue] ← NextTok[sh];
};
tokPROGRAM => {
IF savename.length > 0 AND depseq ~= NIL THEN
depseq.modulename ← Subr.CopyString[savename, entryseq.entryzone];
isconfig ← FALSE;
[tok,tokvalue] ← NextTok[sh];
};
tokMONITOR => {
IF savename.length > 0 AND depseq ~= NIL THEN
depseq.modulename ← Subr.CopyString[savename, entryseq.entryzone];
isconfig ← FALSE;
[tok,tokvalue] ← NextTok[sh];
};
tokDEFINITIONS => {
IF savename.length > 0 AND depseq ~= NIL THEN
depseq.modulename ← Subr.CopyString[savename, entryseq.entryzone];
isdefns ← TRUE;
[tok,tokvalue] ← NextTok[sh];
};
tokIMPORTS, tokEXPORTS => {
imp ← tok = tokIMPORTS;
[tok, tokvalue] ← NextTok[sh];
WHILE tok = tokID OR tok = tokCOLON OR tok = tokCOMMA DO
IF tok = tokID AND peektok ~= tokCOLON THEN {
str ← tokvalue;
str ← GetAlias[str];
CWF.SWF1[stemp,"%s.bcd"L, str];
IF pmod ~= NIL THEN {
[child,] ← MDSubr.GetAnEntry[stemp,
entryseq];
MDSubr.AddToMod[pmod, child, IF imp THEN
imports ELSE exports];
}
ELSE {
adeprecord: MDSubr.ADepRecord ← [];
adeprecord.bcdfilename
← Subr.CopyString[stemp, entryseq.entryzone];
adeprecord.relation ← IF imp THEN
imports ELSE exports;
adeprecord.modulename
← Subr.CopyString[tokvalue, entryseq.entryzone];
AddToDep[depseq, @adeprecord];
};
};
[tok, tokvalue] ← NextTok[sh];
ENDLOOP;
};
ENDCASE => {
-- this may be the module name
IF tok = tokID THEN Subr.strcpy[savename, tokvalue];
[tok,tokvalue] ← NextTok[sh];
};
ENDLOOP;
IF pmod ~= NIL THEN {
pmod.isconfig ← isconfig;
pmod.isdefn ← isdefns;
}
ELSE{
depseq.isconfig ← isconfig;
depseq.isdefns ← isdefns;
};
IF NOT isconfig THEN RETURN;
tok ← ConfigBody[sh, parent, entryseq, sfn, depseq];
IF tok = tokEND AND stringseq ~= NIL THEN
ParseStrings[sh, stringseq];
RETURN;
};
-- parses the configuration AFTER the {
ConfigBody: PROC[sh: Stream.Handle, parent: CARDINAL,
entryseq: MDSubr.EntrySeq, sfn: LONG STRING, depseq: MDSubr.DepSeq]
RETURNS[tok: MDSubr.Token] = {
tokvalue: LONG UNSPECIFIED;
str: LONG STRING;
child: CARDINAL;
stemp: STRING ← [40];
save: STRING ← [40];
-- [itemlist] ← id : id [idlist] LINKS : {CODE, FRAME} {PLUS, THEN} ...;
-- id : id ← ...;
DO
{
[tok,tokvalue] ← NextTok[sh];
save.length ← 0;
IF tok = tokID THEN Subr.strcpy[save, tokvalue]; -- used if ← or :
IF tok = tokEND THEN EXIT;
IF tok = tokLB THEN {
WHILE tok ~= tokRB AND tok ~= tokSEMI AND tok ~= tokEND
AND tok ~= tokEOF DO
[tok, tokvalue] ← NextTok[sh];
IF tok = tokID THEN AddAlias[tokvalue, NIL];
ENDLOOP;
CheckTok[tok,tokRB,sfn];
}
-- ELSE IF peektok = tokCOLON THEN {
-- [tok,] ← NextTok[sh];
-- IF peektok = tokID AND save.length > 0 THEN
-- AddAlias[save, NIL];
-- [tok,tokvalue] ← NextTok[sh];
-- }
;
IF peektok = tokARROW THEN {
[tok,] ← NextTok[sh];
IF peektok = tokID AND save.length > 0 THEN
AddAlias[save, NIL];
[tok,tokvalue] ← NextTok[sh];
};
DO
CheckTok[tok, tokID,sfn];
IF peektok = tokCOLON THEN {
-- id1: id2
-- id1: CONFIGURATION
str ← tokvalue;
Subr.strcpy[save, str];
[] ← NextTok[sh];
[tok, tokvalue] ← NextTok[sh];
IF tok = tokCONFIG THEN {
WHILE tok ~= tokEOF AND tok ~= tokBEGIN DO
[tok, tokvalue] ← NextTok[sh];
ENDLOOP;
IF tok = tokEOF THEN RETURN[tok];
AddAlias[save, NIL];
tok ← ConfigBody[sh, parent, entryseq, sfn,
depseq];
CheckTok[tok, tokEND,sfn];
[tok,] ← NextTok[sh];
CheckTok[tok, tokSEMI,sfn];
GOTO outer;
};
CheckTok[tok, tokID,sfn];
AddAlias[save, NIL]; -- ignore id1
};
str ← tokvalue;
str ← GetAlias[str];
IF str ~= NIL THEN {
CWF.SWF1[stemp, "%s.bcd"L, str];
IF entryseq ~= NIL THEN {
[child,] ← MDSubr.GetAnEntry[stemp, entryseq];
MDSubr.AddToDepends[parent, child, entryseq];
}
ELSE {
adeprecord: MDSubr.ADepRecord ← [];
adeprecord.bcdfilename
← Subr.CopyString[stemp, entryseq.entryzone];
MDSubr.AddToDep[depseq, @adeprecord];
};
};
[tok,] ← NextTok[sh];
WHILE tok ~= tokSEMI AND tok ~= tokPLUS AND tok ~= tokTHEN
AND tok ~= tokEND AND tok ~= tokEOF DO
[tok, tokvalue] ← NextTok[sh];
ENDLOOP;
IF tok = tokPLUS OR tok = tokTHEN THEN {
[tok, tokvalue] ← NextTok[sh];
LOOP;
};
EXIT;
ENDLOOP;
IF tok = tokSEMI THEN LOOP
ELSE {
CheckTok[tok, tokEND, sfn];
RETURN[tok];
};
EXITS
outer => NULL;
};
ENDLOOP;
};
-- Interface: FROM "FileName": TYPE USING [a,b,c],;
-- Interface: TYPE USING [a,b,c],;
-- Interface ,;
-- FileName: TYPE Interface
-- FileName: TYPE
Direct: PROC[sh: Stream.Handle, parent: CARDINAL, entryseq: MDSubr.EntrySeq,
pmod: MDSubr.ModInfo, depseq: MDSubr.DepSeq, sfn: LONG STRING]
RETURNS[MDSubr.Token] = {
tok: MDSubr.Token;
tokvalue: LONG UNSPECIFIED;
filename: LONG STRING;
stemp: STRING ← [40];
child: CARDINAL;
interface: STRING ← [100];
i: CARDINAL;
isfrom: BOOL;
[tok,tokvalue] ← NextTok[sh];
WHILE tok ~= tokPROGRAM AND tok ~= tokEOF AND tok ~= tokDEFINITIONS
AND tok ~= tokSEMI AND tok~= tokCONFIG DO
IF tok = tokID THEN {
filename ← tokvalue;
Subr.strcpy[interface, filename];
[tok, tokvalue] ← NextTok[sh];
IF tok = tokCOLON THEN {
[tok, tokvalue] ← NextTok[sh];
IF tok = tokFROM THEN isfrom ← TRUE
ELSE IF tok = tokTYPE THEN isfrom ← FALSE
ELSE CheckTok[tok, tokFROM, sfn];
IF peektok = tokSTRLIT OR peektok = tokID THEN {
[tok, tokvalue] ← NextTok[sh];
filename ← tokvalue;
IF NOT isfrom THEN {
Subr.strcpy[stemp, filename];
Subr.strcpy[filename, interface];
Subr.strcpy[interface, stemp];
};
IF Subr.EndsIn[filename, ".bcd"L] THEN
filename.length ← filename.length - 4;
IF NOT LongString.EquivalentString[interface,
filename] THEN {
IF Subr.Any[filename, '>] THEN {
i ← filename.length - 1;
DO
IF filename[i] = '> THEN {
Subr.SubStrCopy[filename,
filename, i+1];
EXIT;
};
IF i = 0 THEN EXIT;
i ← i - 1;
ENDLOOP;
};
AddAlias[interface, filename];
Subr.strcpy[interface, filename];
};
};
};
CWF.SWF1[stemp,"%s.bcd"L,interface];
IF pmod ~= NIL THEN {
[child,] ← MDSubr.GetAnEntry[stemp, entryseq];
MDSubr.AddToDepends[parent, child, entryseq];
MDSubr.AddToMod[pmod, child, directory];
}
ELSE {
adeprecord: MDSubr.ADepRecord ← [];
adeprecord.bcdfilename ← Subr.CopyString[stemp, entryseq.entryzone];
adeprecord.relation ← directory;
adeprecord.modulename ← Subr.CopyString[interface, entryseq.entryzone];
AddToDep[depseq, @adeprecord];
};
WHILE tok ~= tokCOMMA AND tok ~= tokSEMI AND tok ~= tokEOF
AND tok ~= tokPROGRAM AND tok ~= tokDEFINITIONS AND
tok ~= tokCONFIG DO
IF tok = tokLB THEN {
WHILE tok ~= tokEOF AND tok ~= tokRB DO
[tok,tokvalue] ← NextTok[sh];
ENDLOOP;
CheckTok[tok, tokRB, sfn];
};
[tok,tokvalue] ← NextTok[sh];
ENDLOOP;
}
ELSE [tok,tokvalue] ← NextTok[sh];
ENDLOOP;
RETURN[tok];
};
ParseStrings: PROC[sh: Stream.Handle, stringseq: MDSubr.StringSeq] = {
line: STRING ← [MaxLineLength];
linx: CARDINAL;
str: STRING ← [MaxLineLength];
val: STRING ← [MaxLineLength];
WHILE Subr.GetLine[sh,line] DO
linx ← 0;
linx ← Subr.GetString[line, str, linx];
IF str.length = 0 OR NOT MDSubr.IsAlpha[str[0]] THEN LOOP;
linx ← Subr.GetString[line, val, linx];
IF val.length ~= 1 OR val[0] ~= '= THEN LOOP;
Subr.SubStrCopy[val, line, linx];
Subr.StripLeadingBlanks[str];
Subr.StripLeadingBlanks[val];
MDSubr.InsertOtherSym[str, val, stringseq];
ENDLOOP;
};
-- initiallizes the various data structures
ScanInit: PUBLIC PROC [st: Stream.Handle] = {
IF aliasseq = NIL THEN Init[];
FreeAliases[]; -- clear out any aliases here previously
peektok ← tokBAD;
peekvalue ← 0;
nextchar ← Ascii.CR;
[] ← NextTok[st];
};
-- to free this memory, simply call StopScanner
Init: PROC = {
longzone: UNCOUNTED ZONE ← Subr.LongZone[];
tokstring ← longzone.NEW[ARRAY MDSubr.Token OF LONG STRING];
InitTokString[];
aliasseq ← longzone.NEW[AliasSeqRecord[NALIASES]];
savestr ← Subr.AllocateString[200];
toksave ← Subr.AllocateString[200];
};
-- frees memory as needed, call only once
StopScanner: PUBLIC PROC = {
longzone: UNCOUNTED ZONE ← Subr.LongZone[];
IF tokstring = NIL THEN RETURN; -- Init never called
FOR t: MDSubr.Token IN MDSubr.Token DO
Subr.FreeString[tokstring[t]];
ENDLOOP;
longzone.FREE[@tokstring];
FreeAliases[];
longzone.FREE[@aliasseq];
Subr.FreeString[toksave];
Subr.FreeString[savestr];
savestr ← toksave ← NIL;
};
-- frees memory for aliases, call many times
FreeAliases: PROC = {
FOR i: CARDINAL IN [0 .. aliasseq.size) DO
Subr.FreeString[aliasseq[i].str];
Subr.FreeString[aliasseq[i].val];
ENDLOOP;
aliasseq.size ← 0;
};
-- NOTE: this checks the type of MDSubr.Token in case the string literal must
-- be saved
NextTok: PUBLIC PROC [st: Stream.Handle] RETURNS[MDSubr.Token,LONG UNSPECIFIED] ={
tok: MDSubr.Token;
tokvalue: LONG UNSPECIFIED;
tok ← peektok;
IF tok = tokID OR tok = tokSTRLIT THEN {
Subr.strcpy[toksave,peekvalue];
tokvalue ← toksave;
}
ELSE tokvalue ← peekvalue;
[peektok,peekvalue] ← ReadTok[st];
-- IF Subr.debugflg THEN {
-- CWF.WF1["tok %s"L,GetTokString[tok]];
-- IF tok = tokID THEN CWF.WF1[" = '%s'\n"L,tokvalue];
-- CWF.WF0["\n"L];
-- };
RETURN[tok,tokvalue];
};
-- if tok = tokID, tokvalue is a STRING
ReadTok: PROC [st: Stream.Handle]
RETURNS[toktype: MDSubr.Token,tokval: LONG UNSPECIFIED] ={
i: CARDINAL;
lastchar: CHAR;
DO
IF nextchar = 0C THEN RETURN[tokEOF,0];
WHILE nextchar = ' OR nextchar = Ascii.CR OR nextchar = Ascii.TAB DO
nextchar ← Subr.GetChar[st]
ENDLOOP;
IF MDSubr.IsAlpha[nextchar] THEN {
i ← 0;
WHILE (MDSubr.IsAlpha[nextchar] OR MDSubr.IsDigit[nextchar])
AND i <= savestr.maxlength DO
savestr[i] ← nextchar;
i ← i + 1;
nextchar ← Subr.GetChar[st];
ENDLOOP;
savestr.length ← i;
toktype ← KeywordLookup[savestr];
IF toktype ~= tokBAD THEN RETURN[toktype,0];
-- not keyword, must be identifier
RETURN[tokID,savestr]
}
ELSE IF MDSubr.IsDigit[nextchar] THEN {
stemp: STRING ← [40];
i ← 0;
WHILE MDSubr.IsDigit[nextchar] AND i <= stemp.maxlength DO
stemp[i] ← nextchar;
i ← i+1;
nextchar ← Subr.GetChar[st];
ENDLOOP;
stemp.length ← i;
RETURN[tokNUM,String.StringToDecimal[stemp]]
}
ELSE {
lastchar ← nextchar;
nextchar ← Subr.GetChar[st];
SELECT lastchar FROM
'( => toktype ← tokLP;
') => toktype ← tokRP;
'[ => toktype ← tokLB;
'] => toktype ← tokRB;
'{ => toktype ← tokBEGIN;
'} => toktype ← tokEND;
'. => toktype ← tokDOT;
': => toktype ← tokCOLON;
', => toktype ← tokCOMMA;
'@ => toktype ← tokAT;
'! => toktype ← tokBANG;
'; => toktype ← tokSEMI;
'> => toktype ← tokGT;
'< => toktype ← tokLT;
'^ => toktype ← tokUP;
'← => toktype ← tokARROW;
Ascii.FF => LOOP;
0C => toktype ← tokEOF;
'= => {
toktype ← tokEQ;
IF nextchar = '> THEN {
nextchar ← Subr.GetChar[st];
toktype ← tokIMPLIES;
};
};
'- => {
{
IF nextchar ~= '- THEN CWF.WF0["bad comment\n"L];
nextchar ← Subr.GetChar[st];
WHILE nextchar ~= Ascii.CR AND nextchar ~= 0C DO
IF nextchar = '- THEN {
nextchar ← Subr.GetChar[st];
IF nextchar = '- THEN {
nextchar ← Subr.GetChar[st];
GOTO goon;
};
};
nextchar ← Subr.GetChar[st];
ENDLOOP;
nextchar ← Subr.GetChar[st];
EXITS
goon => NULL;
};
LOOP;
};
'" => {
i ← 0;
WHILE i < savestr.maxlength DO
savestr[i] ← nextchar;
nextchar ← Subr.GetChar[st];
i ← i + 1;
IF nextchar = '" THEN {
nextchar ← Subr.GetChar[st];
IF nextchar = '" THEN LOOP;
EXIT;
};
REPEAT
FINISHED =>
CWF.WF0["String literal too long\n"L];
ENDLOOP;
savestr.length ← i;
RETURN[tokSTRLIT, savestr];
};
ENDCASE => {
i: INTEGER ← LOOPHOLE[lastchar];
CWF.WF1["unknown char %c\n"L,@i];
toktype ← tokEOF;
};
RETURN[toktype,0];
};
ENDLOOP;
};
-- return the tok if found, return tokBAD if error
KeywordLookup: PROC[str: LONG STRING] RETURNS[tok: MDSubr.Token] = {
OPEN LongString;
IF EquivalentString[str,"BEGIN"L] THEN RETURN[tokBEGIN];
-- deviation due to use of Defs modules "Code"
IF EqualString[str,"CODE"L] THEN RETURN[tokCODE];
IF EquivalentString[str,"CONFIGURATION"L] THEN RETURN[tokCONFIG];
IF EquivalentString[str,"CONTROL"L] THEN RETURN[tokCONTROL];
IF EquivalentString[str,"DEFINITIONS"L] THEN RETURN[tokDEFINITIONS];
-- deviation due to use of Defs modules "Directory"
IF EqualString[str,"DIRECTORY"L] THEN RETURN[tokDIR];
IF EquivalentString[str,"END"L] THEN RETURN[tokEND];
IF EquivalentString[str,"EXPORTS"L] THEN RETURN[tokEXPORTS];
IF EquivalentString[str,"FRAME"L] THEN RETURN[tokFRAME];
IF EquivalentString[str,"FROM"L] THEN RETURN[tokFROM];
IF EquivalentString[str,"IMPORTS"L] THEN RETURN[tokIMPORTS];
IF EquivalentString[str,"LET"L] THEN RETURN[tokLINKS];
IF EquivalentString[str,"LINKS"L] THEN RETURN[tokLINKS];
IF EquivalentString[str,"MONITOR"L] THEN RETURN[tokMONITOR];
IF EquivalentString[str,"OPEN"L] THEN RETURN[tokLINKS];
IF EquivalentString[str,"OTHERS"L] THEN RETURN[tokOTHERS];
IF EquivalentString[str,"PACK"L] THEN RETURN[tokPACK];
IF EquivalentString[str,"PLUS"L] THEN RETURN[tokPLUS];
IF EquivalentString[str,"PROC"L] THEN RETURN[tokPROC];
IF EquivalentString[str,"PROGRAM"L] THEN RETURN[tokPROGRAM];
IF EquivalentString[str,"RETURNS"L] THEN RETURN[tokRETURNS];
IF EquivalentString[str,"SELECT"L] THEN RETURN[tokSELECT];
IF EquivalentString[str,"SHARES"L] THEN RETURN[tokSHARES];
-- deviation due to use of Defs modules "String"
IF EqualString[str,"STRING"L] THEN RETURN[tokSTRING];
IF EquivalentString[str,"THEN"L] THEN RETURN[tokTHEN];
IF EquivalentString[str,"TYPE"L] THEN RETURN[tokTYPE];
IF EquivalentString[str,"USING"L] THEN RETURN[tokUSING];
RETURN[tokBAD];
};
GetTokString: PUBLIC PROC[tok: MDSubr.Token] RETURNS[LONG STRING] = {
RETURN[tokstring[tok]];
};
CheckTok: PUBLIC PROC[tokis, tokshouldbe: MDSubr.Token, sfn: LONG STRING] = {
IF tokis ~= tokshouldbe THEN
CWF.WF3["MDSubr.Token is %s, should be %s, in file %s\n"L,
GetTokString[tokis],GetTokString[tokshouldbe],sfn];
};
AddAlias: PROC[str, val: LONG STRING] = {
IF aliasseq.size >= aliasseq.maxsize THEN CWF.WF0["Error - too many aliases\n"L]
ELSE {
aliasseq[aliasseq.size].str ← Subr.CopyString[str];
aliasseq[aliasseq.size].val ← IF val ~= NIL THEN Subr.CopyString[val] ELSE NIL;
aliasseq.size ← aliasseq.size + 1;
};
};
-- aliases must be correctly capitalized
GetAlias: PROC[str: LONG STRING] RETURNS[LONG STRING] = {
FOR i: CARDINAL IN [0 .. aliasseq.size) DO
IF LongString.EqualString[str, aliasseq[i].str] THEN
RETURN[aliasseq[i].val];
ENDLOOP;
RETURN[str];
};
AddToMod: PUBLIC PROC[pmod: MDSubr.ModInfo, child: CARDINAL, type: MDSubr.ModType] =
{
SELECT type FROM
imports => {
IF pmod.impinx >= LENGTH[pmod.imports] THEN
CWF.WF0["pmod - too many imports\n"L]
ELSE {
pmod.imports[pmod.impinx] ← child;
pmod.impinx ← pmod.impinx + 1;
};
};
exports => {
IF pmod.expinx >= LENGTH[pmod.exports] THEN
CWF.WF0["pmod - too many exports\n"L]
ELSE {
pmod.exports[pmod.expinx] ← child;
pmod.expinx ← pmod.expinx + 1;
};
};
directory => {
IF pmod.dirinx >= LENGTH[pmod.dirname] THEN
CWF.WF0["pmod - too many directory entries\n"L]
ELSE {
pmod.dirname[pmod.dirinx] ← child;
pmod.dirinx ← pmod.dirinx + 1;
};
};
ENDCASE;
};
AddToDep: PUBLIC PROC[depseq: MDSubr.DepSeq,
padeprecord: POINTER TO MDSubr.ADepRecord] = {
IF padeprecord^.relation = errortype THEN ERROR;
IF depseq.size + 1 >= depseq.maxsize THEN
CWF.WF0["Error - too many dependencies.\n"L]
ELSE {
depseq[depseq.size] ← padeprecord^;
depseq.size ← depseq.size + 1;
};
};
-- fills in FP if file in in lookseq
ReadLocalDirectory: PUBLIC PROC[lookseq: MDSubr.LookSeq] ={
fcount: CARDINAL ← 0;
MyDirProc: PROC[cap: File.Capability, sfn: LONG STRING] RETURNS [BOOL] = {
look: MDSubr.Look;
fcount ← fcount + 1;
[look,] ← LookLook[sfn, lookseq];
IF look ~= NIL THEN {
look.cap ← cap;
look.presentonlocaldisk ← TRUE;
RETURN[FALSE];
};
RETURN[FALSE];
};
-- CWF.WF0["Reading local directory.\n"L];
Subr.EnumerateDirectory[MyDirProc];
CWF.WF1["%u files in local directory\n"L, @fcount];
};
-- fills in lookseq with all files in directory
-- except if ThrowAwayThisFile[]
ReadInLocalDirectoryAll: PUBLIC PROC[lookseq: MDSubr.LookSeq,
ThrowAwayThisFile: PROC[name: LONG STRING] RETURNS[BOOL]] = {
fcount: CARDINAL ← 0;
AddDirName: PROC[cap: File.Capability, sfn: LONG STRING] RETURNS[BOOL] = {
look: MDSubr.Look;
fcount ← fcount + 1;
IF ThrowAwayThisFile[sfn] THEN RETURN[FALSE];
look ← AddToLook[sfn,lookseq];
look.cap ← cap;
look.presentonlocaldisk ← TRUE;
IF lookseq.size >= lookseq.maxsize THEN {
m: CARDINAL ← lookseq.maxsize;
CWF.WF1[
"Error - More than %d files, will do only that many files.\n"L,
@m];
RETURN[TRUE];
};
RETURN[FALSE];
};
-- CWF.WF0["Begin reading local directory.\n"L];
Subr.EnumerateDirectory[AddDirName];
CWF.WF1["%u files in local directory\n"L, @fcount];
};
AnalyzeLocalFiles: PUBLIC PROC[oktosort: BOOL, lookseq: MDSubr.LookSeq,
getcreatedate: BOOL] = {
-- fill in mod time for all local files we need
time: Subr.PackedTime;
-- mtc, mtr: LONG CARDINAL;
count: CARDINAL ← 0;
look: MDSubr.Look;
filename: STRING ← [100];
space: Space.Handle;
IF oktosort THEN SortByFileCap[lookseq];
space ← Space.Create[size: 1, parent: Space.virtualMemory];
Space.Map[space]; -- map in for CopyIn
-- CWF.WF0["Begin reading local file times.\n"];
time ← Time.Current[];
FOR i: CARDINAL IN [0 .. lookseq.size) DO
look ← @lookseq[i];
IF look.name = NIL OR NOT look.need
OR NOT look.presentonlocaldisk
OR look.localversion ~= MDSubr.versionUnknown THEN LOOP;
-- [create: mtc, read: mtr, length: look.locallength] ←
-- Subr.GetCreateDate[look.cap];
-- [createDate: mtc, readDate: mtr, byteLength: look.locallength] ←
-- Directory.GetProps[look.cap, filename
-- ! Directory.Error => {
-- CWF.WF0["Directory.Error.\n"L];
-- mtc ← mtr ← 0;
-- CONTINUE
-- }];
-- look.localversion ← IF getcreatedate THEN mtc ELSE mtr;
look.localversion ← Subr.GetCreateDateWithSpace[look.cap, space];
count ← count + 1;
ENDLOOP;
time ← Time.Current[] - time;
Space.Delete[space: space];
IF count > 0 THEN
CWF.WF2["Took %lu secs to read properties of %u files.\n"L,
@time, @count];
};
PrintLook: PUBLIC PROC[lookseq: MDSubr.LookSeq] = {
look: MDSubr.Look;
len: LONG CARDINAL;
CWF.WF0["Look Summary:\n"L];
FOR i: CARDINAL IN [0 .. lookseq.size) DO
look ← @lookseq[i];
IF look.name ~= NIL AND look.need THEN {
CWF.WF1["File %s:\n"L,look.name];
IF look.presentonlocaldisk THEN {
CWF.WF0[" Local Disk"L];
IF look.localversion ~= MDSubr.versionUnknown THEN {
len ← look.localversion;
CWF.WF1[", Time %lt"L, @len];
};
CWF.WF0["\n"L];
};
};
ENDLOOP;
};
SortByFileCap: PROC[lookseq: MDSubr.LookSeq]={
FileTimeLessThan: PROC[left, right:MDSubr.Look]RETURNS[BOOL] = {
l,r: LONG POINTER TO SystemInternal.UniversalID;
l ← LOOPHOLE[@left.cap];
r ← LOOPHOLE[@right.cap];
RETURN[l.sequence < r.sequence];
};
TreeSort[lookseq, FileTimeLessThan];
};
SortByFileTime: PUBLIC PROC[lookseq: MDSubr.LookSeq,
descending: BOOL ← FALSE]={
FileTimeLessThan: PROC[left, right:MDSubr.Look]RETURNS[BOOL] = {
RETURN[IF descending
THEN(left.localversion > right.localversion)
ELSE(left.localversion < right.localversion)];
};
TreeSort[lookseq, FileTimeLessThan];
};
SortByFileName: PUBLIC PROC[lookseq: MDSubr.LookSeq,
descending: BOOL ← FALSE]={
FileNameLessThan: PROC[left, right: MDSubr.Look] RETURNS [BOOL] = {
s1: STRING ← [100];
s2: STRING ← [100];
IF left.name = NIL OR right.name = NIL THEN RETURN[FALSE];
Subr.strcpy[s1, left.name];
Subr.strcpy[s2, right.name];
-- IF String.CompareStrings[s1, s2, TRUE] > 0 THEN
-- CWF.WF2["%s is greater than than %s\n"L, s1,s2];
RETURN[IF descending
THEN(String.CompareStrings[s1, s2, TRUE] > 0)
ELSE(String.CompareStrings[s1, s2, TRUE] < 0)];
};
TreeSort[lookseq, FileNameLessThan];
};
TreeSort: PROC[lookseq: MDSubr.LookSeq,LessThan: PROC[MDSubr.Look,
MDSubr.Look] RETURNS[BOOL]] = {
left, right: MDSubr.Look;
siftUp: PROC[low, high: INTEGER] ={
k, son: INTEGER;
left, right: MDSubr.Look;
k ← low;
DO
IF 2*k>high THEN EXIT;
left ← @lookseq[2*k+1-1];
right ← @lookseq[2*k-1];
IF 2*k+1>high OR LessThan[left, right] THEN
son ← 2*k
ELSE
son ← 2*k+1;
left ← @lookseq[son-1];
right ← @lookseq[k-1];
IF LessThan[left, right] THEN {
EXIT;
};
LookExch[left, right];
k ← son;
ENDLOOP;
};
FOR i:CARDINAL DECREASING IN [1..lookseq.size/2] DO
siftUp[i,lookseq.size]
ENDLOOP;
FOR i:CARDINAL DECREASING IN [1..lookseq.size) DO
left ← @lookseq[0];
right ← @lookseq[i];
LookExch[left, right];
siftUp[1,i]
ENDLOOP;
};
LookExch: PROC[left, right: MDSubr.Look] = {
t: MDSubr.LookRecord;
t ← left^;
left^ ← right^;
right^ ← t;
};
-- returns NIL if not found
LookLook: PUBLIC PROC[name: LONG STRING, lookseq: MDSubr.LookSeq]
RETURNS[look: MDSubr.Look, lookinx: CARDINAL] = {
FOR i: CARDINAL IN [0..lookseq.size) DO
look ← @lookseq[i];
IF look.need
AND look.name ~= NIL
AND look.name.length = name.length
AND LongString.EquivalentString[name, look.name]
THEN RETURN[look, i];
ENDLOOP;
RETURN[NIL, MDSubr.lookNil];
};
FreeLookSeq: PUBLIC PROC[plookseq: LONG POINTER TO MDSubr.LookSeq] = {
IF plookseq^ = NIL THEN RETURN;
IF FALSE THEN
-- no frees allowed
FOR i: CARDINAL IN [0.. plookseq^.size) DO
Subr.FreeString[plookseq^[i].name];
ENDLOOP;
-- longzone.FREE[plookseq];
plookseq^ ← NIL;
};
AddToLook: PUBLIC PROC[name: LONG STRING, lookseq: MDSubr.LookSeq]
RETURNS[look: MDSubr.Look] = {
IF lookseq.size >= lookseq.maxsize THEN {
m: CARDINAL ← lookseq.maxsize;
CWF.WF1["Error - more than %d files to look for."L, @m];
RETURN[NIL];
};
look ← @lookseq[lookseq.size];
-- other fields are defaulted
look^ ← [need: TRUE, name: Subr.CopyString[name, lookseq.lookzone]];
lookseq.size ← lookseq.size + 1;
RETURN[look];
};
InitTokString: PROC = {
TokString: ARRAY MDSubr.Token OF STRING =
["ErrorMDSubr.Token"L, "EndOfFile"L, "("L, ")"L, "["L, "]"L, "."L,
":"L, "L,"L, "="L, "@"L, "!"L, ";"L, ">"L,"<"L,
"^"L, "=>"L, "←"L, "Identifier"L, "Number"L, "TYPE"L,
"PLUS"L, "THEN"L, "PROC"L, "RETURNS"L, "StringLiteral"L,
"STRING"L, "SELECT"L, "FROM"L, "DIRECTORY"L,"IMPORTS"L,
"EXPORTS"L,"PROGRAM"L,"{"L, "DEFINITIONS"L,
"OTHERS"L, "CONTROL"L, "CONFIGURATION"L, "LINKS"L, "CODE"L,
"FRAME"L, "PACK"L, "END"L, "USING"L, "SHARES"L, "LET"L, "OPEN"L, "MONITOR"L];
FOR t: MDSubr.Token IN MDSubr.Token DO
tokstring[t] ← Subr.CopyString[TokString[t]];
ENDLOOP;
};
}.