ConfigAndMesaDeps.Mesa
Last Edited by: Spreitzer, May 9, 1986 9:52:47 pm PDT
Carl Hauser, April 11, 1985 3:52:50 pm PST
DIRECTORY Basics, BasicTime, BcdDefs, BCDery, FS, IO, IOClasses, List, ListerUtils, MakeDo, RedBlackTree, Rope, TimeStamp;
ConfigAndMesaDeps: CEDAR MONITOR
IMPORTS BasicTime, BCDery, FS, IO, IOClasses, ListerUtils, MakeDo, RedBlackTree, Rope =
INVARIANT
StampRef in node props
BEGIN OPEN MakeDo;
GotoSyntaxError: ERROR = CODE;
ROPE: TYPE = Rope.ROPE;
StampRef: TYPE = REF StampRep; StampRep: TYPE = RECORD [
created: BasicTime.GMT,
stamp: TimeStamp.Stamp];
SourceData: TYPE = REF SourceDataRep; SourceDataRep: TYPE = RECORD [
bcdName: ROPE,
sourceType: SourceType,
supports: RedBlackTree.Table,
sourceNode, bcdNode: Node,
sourceStamp: Stamp ← TimeStamp.Null,
bcdCreateTime: BasicTime.GMT,
bcdReadable, supportsInvalid: BOOLFALSE,
cmd: ROPENIL
];
SourceType: TYPE = {Mesa, Config};
Support: TYPE = REF SupportRep; SupportRep: TYPE = RECORD [
node: Node,
version: Stamp];
ParseData: TYPE = REF ParseDataRep; ParseDataRep: TYPE = RECORD [
source: ROPE,
sourceType: SourceType,
stamp: Time ← notExistTime,
refdModules: RopeList ← NIL];
parses: RedBlackTree.Table ← RedBlackTree.Create[GetParseKey, CompareParses];
configAndMesaClass: ActionClass ← NEW [ActionClassRep ← [
CheckConsistency: CheckConsistency,
Rederive: RederiveSource
]];
GetParseKey: PROC [data: REF ANY] RETURNS [ROPE] --RedBlackTree.GetKey-- = {
pd: ParseData = NARROW[data];
RETURN[pd.source]};
CompareParses: PROC [k, data: REF ANY] RETURNS [Basics.Comparison] --RedBlackTree.Compare-- = {
k1: ROPE = NARROW[k];
k2: ROPE = GetParseKey[data];
RETURN [k1.Compare[s2: k2, case: FALSE]]};
GetSupportKey: PROC [data: REF ANY] RETURNS [Node] --RedBlackTree.GetKey-- = {
s: Support = NARROW[data];
RETURN[s.node]};
CompareSupport: PROC [k, data: REF ANY] RETURNS [Basics.Comparison] --RedBlackTree.Compare-- = {
k1: INT = LOOPHOLE[k];
k2: INT = LOOPHOLE[GetSupportKey[data]];
RETURN [SELECT k1 FROM
<k2 => less,
=k2 => equal,
>k2 => greater,
ENDCASE => ERROR];
};
SourceFind: PROC [resultName: ROPE, finderData: REF ANY] RETURNS [found: BOOLEAN, sought: Node, makes, cmdFrom: NodeList, from: From, cmd: ROPE, class: ActionClass, foundData: REF ANY] -- FinderProc -- =
BEGIN
bcdExpanded, shortName, baseName, sourceName, bcdName, configName, switches: ROPE;
bcdCP: FS.ComponentPositions;
sourceNode, bcdNode, configNode, switchesNode: Node;
bcdCreateTime: BasicTime.GMT;
supports: RedBlackTree.Table;
md: SourceData;
sourceType: SourceType;
NoteDep: PROC [fileName: ROPE] = {
n: Node = GetNode[fileName.Cat[".BCD"], fileClass];
s: Support ← NARROW[supports.Lookup[n]];
IF s = NIL THEN {
s ← NEW [SupportRep ← [n, TimeStamp.Null]];
supports.Insert[s, n];
};
from.mustHave ← CONS[n, from.mustHave];
};
found ← TRUE;
[bcdExpanded, bcdCP, ] ← FS.ExpandName[resultName !FS.Error => {found ← FALSE; CONTINUE}];
IF NOT found THEN RETURN;
IF
NOT (found ←
bcdExpanded.Substr[start: bcdCP.ext.start, len: bcdCP.ext.length].
Equal[s2: "BCD", case: FALSE]
OR (bcdCP.ext.length = 0))
THEN RETURN;
baseName ← bcdExpanded.Substr[start: 0, len: bcdCP.base.start + bcdCP.base.length];
shortName ← bcdExpanded.Substr[start: bcdCP.base.start, len: bcdCP.base.length];
bcdName ← baseName.Cat[".BCD"];
switchesNode ← GetNode[bcdName.Concat[".Switches"], fileClass];
switches ← GetSwitches[bcdName];
makes ← LIST[sought ← bcdNode ← GetNode[bcdName, fileClass]];
bcdCreateTime ← InnerGetCreated[bcdNode];
from ← [NIL, NIL];
supports ← RedBlackTree.Create[GetSupportKey, CompareSupport];
sourceType ← Mesa;
cmd ← Rope.Cat["RCompile ", switches, " ", sourceName ← baseName.Concat[".Mesa"]];
IF
(NOT EnumerateDependancies[
(sourceNode ← GetNode[sourceName, fileClass]),
sourceType,
NoteDep])
AND EnumerateDependancies[
(configNode ← GetNode[configName ← baseName.Concat[".Config"], fileClass]),
Config,
NoteDep]
THEN {
sourceName ← configName;
sourceNode ← configNode;
sourceType ← Config;
cmd ← Rope.Cat["Bind ", switches, " ", shortName]};
cmdFrom ← LIST[sourceNode, switchesNode];
foundData ← md ← NEW [SourceDataRep ← [bcdName: bcdName, sourceType: sourceType, supports: supports, sourceNode: sourceNode, bcdNode: bcdNode, bcdCreateTime: bcdCreateTime, cmd: cmd]];
IF bcdCreateTime # MakeDo.notExistTime THEN MakeSupports[md];
from.mustHave ← CONS[sourceNode, from.mustHave];
class ← configAndMesaClass;
END;
GetSwitches: PROC [bcdName: ROPE] RETURNS [switches: ROPE] =
BEGIN
ss: IO.STREAMNIL;
ss ← FS.StreamOpen[bcdName.Cat[".Switches"] !FS.Error => CONTINUE];
IF ss = NIL THEN RETURN [NIL];
[] ← ss.SkipWhitespace[];
IF ss.EndOf[] THEN RETURN [NIL];
switches ← ss.GetTokenRope[IO.IDProc].token;
ss.Close[];
END;
CheckConsistency: PROC [a: Action, result: Node] RETURNS [consistent: BOOL, reason: ROPE] --ConsistencyChecker-- =
BEGIN
md: SourceData ← NARROW[a.PublicPartsOfAction[].foundData];
curSourceStamp: Stamp;
CheckSupport: PROC [data: REF ANY] RETURNS [stop: BOOLFALSE] --RedBlackTree.EachNode-- = {
s: Support = NARROW[data];
curBCDStamp: Stamp = CurBCDStamp[s.node];
IF curBCDStamp = TimeStamp.Null THEN {
consistent ← TRUE;
reason ← IO.PutFR["input %g doesn't exist", [rope[s.node.PublicPartsOfNode[].name]] ];
RETURN [TRUE];
};
IF curBCDStamp # s.version THEN {
out: IO.STREAMIO.ROS[];
out.PutRope["last used version "];
TRUSTED {ListerUtils.PrintVersion[s.version, out, FALSE]};
out.PutF[
" of %g, but current version is ",
[rope[s.node.PublicPartsOfNode[].name]]
];
TRUSTED {ListerUtils.PrintVersion[curBCDStamp, out, FALSE]};
consistent ← FALSE;
reason ← out.RopeFromROS[];
RETURN [TRUE];
};
};
UpdateSupport[md];
IF md.bcdCreateTime = MakeDo.notExistTime THEN RETURN [FALSE, "BCD doesn't exist"];
IF NOT md.bcdReadable THEN RETURN [FALSE, "BCD not readable"];
curSourceStamp ← CurSourceStamp[md.sourceNode];
IF curSourceStamp = TimeStamp.Null THEN RETURN [TRUE, "source doesn't exist"];
IF curSourceStamp # md.sourceStamp THEN {
out: IO.STREAMIO.ROS[];
out.PutRope["last used source "];
TRUSTED {ListerUtils.PrintVersion[md.sourceStamp, out, TRUE]};
out.PutRope[", but current source is "];
TRUSTED {ListerUtils.PrintVersion[curSourceStamp, out, TRUE]};
RETURN [FALSE, out.RopeFromROS[]];
};
consistent ← TRUE;
reason ← "all version stamps match";
md.supports.EnumerateIncreasing[CheckSupport];
END;
UpdateSupport: PROC [md: SourceData] = {
oldBcd: Time = md.bcdCreateTime;
md.bcdCreateTime ← InnerGetCreated[md.bcdNode];
IF md.bcdCreateTime = MakeDo.notExistTime THEN RETURN;
IF md.bcdCreateTime = oldBcd AND NOT md.supportsInvalid THEN RETURN;
MakeSupports[md];
};
CurSourceStamp: PROC [node: Node] RETURNS [stamp: Stamp] =
BEGIN
time: Time ← InnerGetCreated[node];
IF time = MakeDo.notExistTime THEN RETURN [TimeStamp.Null];
stamp ← [net: 0, host: 0, time: BasicTime.ToPupTime[time]];
END;
CurBCDStamp: ENTRY PROC [node: Node] RETURNS [stamp: Stamp] =
BEGIN
ENABLE UNWIND => {};
sr: StampRef ← NARROW[node.GetProp[$Stamp]];
created: BasicTime.GMT ← InnerGetCreated[node];
bcd: ListerUtils.RefBCD;
IF sr = NIL THEN node.SetProp[
prop: $Stamp,
val: sr ← NEW [StampRep ← [
created: notExistTime,
stamp: TimeStamp.Null]]
];
IF created = sr.created THEN RETURN [sr.stamp];
sr.created ← created;
IF created = MakeDo.notExistTime
THEN sr.stamp ← TimeStamp.Null
ELSE {
TRUSTED {bcd ← ListerUtils.ReadBcd[node.PublicPartsOfNode[].name !FS.Error => {bcd ← NIL; CONTINUE}]};
sr.stamp ← IF bcd = NIL OR bcd.versionIdent # BcdDefs.VersionID
THEN TimeStamp.Null
ELSE bcd.version;
};
stamp ← sr.stamp;
END;
MakeSupports: PROC [md: SourceData] =
BEGIN
NoteSupport: PROC [name: ROPE, version: Stamp] =
BEGIN
extName: ROPE = FS.ExpandName[name.Cat[".BCD"]].fullFName;
node: Node = GetNode[extName, fileClass];
s: Support = NARROW[md.supports.Lookup[node]];
IF s # NIL THEN s.version ← version;
END;
bcd: ListerUtils.RefBCD;
md.sourceStamp ← TimeStamp.Null;
md.bcdReadable ← FALSE;
TRUSTED {bcd ← ListerUtils.ReadBcd[md.bcdName !FS.Error => {bcd ← NIL; CONTINUE}]};
IF bcd = NIL THEN RETURN;
IF bcd.versionIdent # BcdDefs.VersionID THEN RETURN;
md.sourceStamp ← bcd.sourceVersion;
[] ← BCDery.EnumerateFiles[bcd: bcd, bcdFileName: md.bcdName, to: NoteSupport];
md.bcdReadable ← TRUE;
md.supportsInvalid ← FALSE;
END;
RederiveSource: PROC [a: Action] RETURNS [from: From, cmd: ROPE] --RederiveProc-- =
BEGIN
md: SourceData ← NARROW[a.PublicPartsOfAction[].foundData];
NoteDep: PROC [fileName: ROPE] = {
n: Node = GetNode[fileName.Cat[".BCD"], fileClass];
s: Support ← NARROW[md.supports.Lookup[n]];
IF s = NIL THEN {
s ← NEW [SupportRep ← [n, TimeStamp.Null]];
md.supports.Insert[s, n];
};
from.mustHave ← CONS[n, from.mustHave];
};
from ← [NIL, NIL];
md.supports.DestroyTable[];
[] ← EnumerateDependancies[md.sourceNode, md.sourceType, NoteDep];
md.supportsInvalid ← TRUE;
UpdateSupport[md];
from.mustHave ← CONS[md.sourceNode, from.mustHave];
cmd ← md.cmd;
END;
Break: IO.BreakProc = {RETURN [SELECT char FROM
IN ['a .. 'z], IN ['A .. 'Z], IN ['0 .. '9] => other,
IO.SP, IO.CR, IO.TAB, IO.LF, IO.FF => sepr,
ENDCASE => break]};
Letter: PROC [c: CHAR] RETURNS [letter: BOOLEAN] = INLINE
{letter ← (c IN ['a .. 'z]) OR (c IN ['A .. 'Z])};
EnumerateDependancies: PROC [sourceNode: Node, sourceType: SourceType, consume: PROC [fileName: ROPE]] RETURNS [exists: BOOLEAN] =
BEGIN
sourceName: ROPE = sourceNode.PublicPartsOfNode[].name;
pd: ParseData ← NARROW[parses.Lookup[sourceName]];
cur: BasicTime.GMT;
IF pd = NIL THEN {
pd ← NEW [ParseDataRep ← [source: sourceName, sourceType: sourceType]];
parses.Insert[pd, sourceName];
};
cur ← InnerGetCreated[sourceNode];
IF NOT (exists ← cur # notExistTime) THEN RETURN;
IF cur # pd.stamp THEN {
NoteDep: PROC [fileName: ROPE] = {
pd.refdModules ← CONS[fileName, pd.refdModules];
consume[fileName]};
pd.stamp ← cur; pd.refdModules ← NIL;
SELECT pd.sourceType FROM
Mesa => [] ← EnumerateMesaDependancies[sourceName, NoteDep];
Config => [] ← EnumerateConfigDependancies[sourceName, NoteDep];
ENDCASE => ERROR;
}
ELSE {
FOR rml: RopeList ← pd.refdModules, rml.rest WHILE rml # NIL DO
consume[rml.first] ENDLOOP};
END;
EnumerateMesaDependancies: PROC [sourceName: ROPE, consume: PROC [fileName: ROPE]] RETURNS [exists: BOOLEAN] =
BEGIN
NextToken: PROC RETURNS [ROPE] =
{RETURN [source.GetTokenRope[Break].token]};
ParseDirectory: PROC RETURNS [next: ROPE] =
BEGIN
firstClause: BOOLTRUE;
ParseClause: PROC RETURNS [next: ROPE] =
BEGIN
fileName: ROPE ← "?";
next ← NextToken[];
IF firstClause THEN
{firstClause ← FALSE; IF next.Equal[";"] THEN RETURN};
IF NOT Letter[next.Fetch[0]] THEN ERROR GotoSyntaxError;
fileName ← next;
next ← NextToken[];
IF next.Equal[":"] THEN
BEGIN
next ← NextToken[];
IF next.Equal["TYPE"] THEN {
next ← NextToken[];
IF Letter[next.Fetch[0]] AND NOT next.Equal["USING"] THEN next ← NextToken[];
}
ELSE IF next.Equal["FROM"] THEN {
fileName ← source.GetRopeLiteral[];
next ← NextToken[];
}
ELSE ERROR GotoSyntaxError;
END;
IF next.Equal["USING"] THEN
BEGIN
IF NOT (next ← NextToken[]).Equal["["] THEN ERROR GotoSyntaxError;
WHILE NOT (next ← NextToken[]).Equal["]"] DO NULL ENDLOOP;
next ← NextToken[];
END;
consume[fileName];
END;
IF (next ← NextToken[]).Equal["DIRECTORY"] THEN
BEGIN
DO
IF (next ← ParseClause[]).Equal[";"] THEN EXIT;
IF NOT next.Equal[","] THEN ERROR GotoSyntaxError;
ENDLOOP;
next ← NextToken[];
END;
IF NOT Letter[next.Fetch[0]] THEN ERROR GotoSyntaxError;
END;
source: IO.STREAMNIL;
source ← FS.StreamOpen[fileName: sourceName, accessOptions: $read !FS.Error => {source ← NIL; CONTINUE}];
exists ← source # NIL;
IF NOT exists THEN RETURN;
source ← IOClasses.CreateCommentFilterStream[source];
[] ← source.GetIndex[];
[] ← ParseDirectory[!GotoSyntaxError =>
BEGIN
SIGNAL Warning[IO.PutFR["Syntax error in %g; parse aborted at %g", IO.rope[sourceName], IO.int[source.GetIndex[]]]];
CONTINUE
END];
source.Close[];
END;
IsConfig: PROC [word: ROPE] RETURNS [is: BOOLEAN] =
{is ← word.Equal["CONFIGURATION"] OR word.Equal["CONFIG"]};
EnumerateConfigDependancies: PROC [sourceName: ROPE, Consume: PROC [fileName: ROPE]] RETURNS [exists: BOOLEAN] = {
locals: LIST OF LIST OF ROPENIL;
AddDef: PROC [name: ROPE] = {locals.first ← CONS[name, locals.first]};
MaybeConsume: PROC [moduleName: ROPE] = {
FOR l: LIST OF LIST OF ROPE ← locals, l.rest WHILE l # NIL DO
FOR m: LIST OF ROPE ← l.first, m.rest WHILE m # NIL DO
IF m.first.Equal[moduleName] THEN RETURN;
ENDLOOP;
ENDLOOP;
Consume[moduleName]};
NextToken: PROC RETURNS [ROPE] = {RETURN [source.GetTokenRope[Break].token]};
ParseConfigDescription: PROC = {
next: ROPE ← ParseCDirectory[];
next ← ParseCPacking[next];
next ← ParseConfiguration[next];
IF NOT next.Equal["."] THEN ERROR GotoSyntaxError;
};
ParseConfiguration: PROC [first: ROPE] RETURNS [next: ROPE] = {
IF NOT Letter[first.Fetch[0]] THEN ERROR GotoSyntaxError;
IF NOT (next ← NextToken[]).Equal[":"] THEN ERROR GotoSyntaxError;
IF NOT IsConfig[next ← NextToken[]] THEN ERROR GotoSyntaxError;
next ← ParseConfigurationRemains[];
};
ParseConfigurationRemains: PROC RETURNS [next: ROPE] = {
next ← ParseCHeadRemains[];
IF NOT (next.Equal["="] OR next.Equal["~"]) THEN ERROR GotoSyntaxError;
ParseCBody[];
next ← NextToken[];
};
ParseCBody: PROC = {
next: ROPE ← NextToken[];
curly: BOOLEAN;
IF next.Equal["BEGIN"] THEN curly ← FALSE
ELSE IF next.Equal["{"] THEN curly ← TRUE
ELSE ERROR GotoSyntaxError;
locals ← CONS[NIL, locals];
next ← NextToken[];
DO
semiSeen: BOOLFALSE;
next ← ParseCStatement[next];
WHILE next.Equal[";"] DO
next ← NextToken[];
semiSeen ← TRUE;
ENDLOOP;
IF next.Equal[IF curly THEN "}" ELSE "END"] THEN EXIT;
IF NOT semiSeen THEN ERROR GotoSyntaxError;
ENDLOOP;
locals ← locals.rest;
};
ParseCStatement: PROC [first: ROPE] RETURNS [next: ROPE] = {
next ← first;
IF next.Equal["["] THEN
BEGIN
next ← ParseItemList[FALSE];
IF NOT next.Equal["]"] THEN ERROR GotoSyntaxError;
next ← NextToken[];
IF NOT next.Equal["←"] THEN ERROR GotoSyntaxError;
next ← ParseCExpression[];
END
ELSE BEGIN
lhs, rhs: ROPE;
named: BOOLFALSE;
lhs ← rhs ← next;
IF named ← (next ← NextToken[]).Equal[":"] THEN
BEGIN
IF IsConfig[rhs ← NextToken[]] THEN {
AddDef[lhs];
next ← ParseConfigurationRemains[];
RETURN};
next ← NextToken[];
END;
IF next.Equal["←"] THEN
BEGIN
AddDef[lhs];
next ← ParseCExpression[];
END
ELSE BEGIN
IF named THEN AddDef[lhs];
MaybeConsume[rhs];
IF next.Equal["["] THEN
BEGIN
IF NOT (next ← NextToken[]).Equal["]"] THEN next ← EatIDList[next, FALSE];
IF NOT next.Equal["]"] THEN ERROR GotoSyntaxError;
next ← ParseCLinks[];
END
ELSE IF next.Equal["LINKS"] THEN {
SIGNAL Warning[IO.PutFR["[] missing before %g in %g", IO.int[source.GetIndex[]], IO.rope[sourceName]]];
next ← ParseCLinks[next];
};
END;
END;
};
ParseCExpression: PROC RETURNS [next: ROPE] = {
next ← ParseCRightSide[];
WHILE next.Equal["PLUS"] DO
next ← ParseCRightSide[];
ENDLOOP;
WHILE next.Equal["THEN"] DO
next ← ParseCRightSide[];
ENDLOOP;
};
ParseCRightSide: PROC RETURNS [next: ROPE] = {
next ← ParseItem[NIL, TRUE];
IF next.Equal["["] THEN
BEGIN
IF NOT (next ← NextToken[]).Equal["]"] THEN next ← EatIDList[next, FALSE];
IF NOT next.Equal["]"] THEN ERROR GotoSyntaxError;
next ← ParseCLinks[];
END;
};
ParseCHeadRemains: PROC RETURNS [next: ROPE] = {
next ← ParseCLinks[];
next ← ParseImports[next];
next ← ParseCExports[next];
next ← ParseControlClause[next];
};
ParseCLinks: PROC [first: ROPENIL] RETURNS [next: ROPE] = {
next ← IF first # NIL THEN first ELSE NextToken[];
IF NOT next.Equal["LINKS"] THEN RETURN;
IF NOT (next ← NextToken[]).Equal[":"] THEN ERROR GotoSyntaxError;
next ← NextToken[];
IF NOT (next.Equal["CODE"] OR next.Equal["FRAME"]) THEN ERROR GotoSyntaxError;
next ← NextToken[];
};
ParseImports: PROC [first: ROPE] RETURNS [next: ROPE] = {
IF NOT first.Equal["IMPORTS"] THEN RETURN [first];
next ← ParseItemList[FALSE];
};
ParseCExports: PROC [first: ROPE] RETURNS [next: ROPE] = {
IF NOT first.Equal["EXPORTS"] THEN RETURN [first];
next ← ParseItemList[FALSE];
};
ParseControlClause: PROC [first: ROPE] RETURNS [next: ROPE] = {
IF NOT first.Equal["CONTROL"] THEN RETURN [first];
next ← EatIDList[NIL, FALSE];
};
ParseItemList: PROC [notice: BOOLEAN] RETURNS [next: ROPE] = {
DO
next ← ParseItem[NIL, notice];
IF NOT next.Equal[","] THEN EXIT;
ENDLOOP;
};
ParseItem: PROC [first: ROPE, notice: BOOLEAN] RETURNS [next: ROPE] = {
lhs, rhs: ROPE;
named: BOOLFALSE;
lhs ← rhs ← IF first = NIL THEN NextToken[] ELSE first;
IF named ← (next ← NextToken[]).Equal[":"] THEN
BEGIN
rhs ← NextToken[];
next ← NextToken[];
END;
IF notice THEN {
IF named THEN AddDef[lhs];
MaybeConsume[rhs]};
};
ParseCDirectory: PROC RETURNS [next: ROPE] = {
firstClause: BOOLTRUE;
ParseClause: PROC RETURNS [next: ROPE] = {
moduleName: ROPE ← "?";
fileName: ROPE ← "?";
next ← NextToken[];
IF firstClause THEN
{firstClause ← FALSE; IF next.Equal[";"] THEN RETURN};
IF NOT Letter[next.Fetch[0]] THEN ERROR GotoSyntaxError;
moduleName ← fileName ← next;
next ← NextToken[];
IF next.Equal[":"] THEN
BEGIN
next ← NextToken[];
IF next.Equal["TYPE"] THEN {
next ← NextToken[];
IF Letter[next.Fetch[0]] THEN next ← NextToken[];
}
ELSE IF next.Equal["FROM"] THEN {
fileName ← source.GetRopeLiteral[];
next ← NextToken[];
}
ELSE ERROR GotoSyntaxError;
END;
Consume[fileName];
AddDef[moduleName];
};
locals ← CONS[NIL, locals];
IF NOT (next ← NextToken[]).Equal["DIRECTORY"] THEN RETURN;
DO
IF (next ← ParseClause[]).Equal[";"] THEN EXIT;
IF NOT next.Equal[","] THEN ERROR GotoSyntaxError;
ENDLOOP;
next ← NextToken[];
};
ParseCPacking: PROC [first: ROPE] RETURNS [next: ROPE] = {
next ← first;
WHILE next.Equal["PACK"] DO
next ← EatIDList[NIL, FALSE];
IF NOT next.Equal[";"] THEN ERROR GotoSyntaxError;
next ← NextToken[];
ENDLOOP;
};
EatIDList: PROC [first: ROPE, notice: BOOLEAN] RETURNS [next: ROPE] = {
next ← IF first = NIL THEN NextToken[] ELSE first;
DO
IF NOT Letter[next.Fetch[0]] THEN ERROR GotoSyntaxError;
IF notice THEN Consume[next];
next ← NextToken[];
IF NOT next.Equal[","] THEN EXIT;
next ← NextToken[];
ENDLOOP;
};
source: IO.STREAMNIL;
source ← FS.StreamOpen[fileName: sourceName, accessOptions: $read !FS.Error => {source ← NIL; CONTINUE}];
exists ← source # NIL;
IF NOT exists THEN RETURN;
source ← IOClasses.CreateCommentFilterStream[source];
[] ← source.GetIndex[];
ParseConfigDescription[!GotoSyntaxError =>
BEGIN
SIGNAL Warning[IO.PutFR["Syntax error in %g; parse aborted at %g", IO.rope[sourceName], IO.int[source.GetIndex[]]]];
CONTINUE
END];
source.Close[];
};
AddFinder[["Compiler and Binder", SourceFind], front];
END.