ConfigAndMesaDeps.Mesa
Last Edited by: Spreitzer, September 4, 1985 0:40:14 am PDT
Carl Hauser, April 11, 1985 3:52:50 pm PST
DIRECTORY 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: SupportList,
sourceNode, bcdNode: Node,
sourceStamp: Stamp ← TimeStamp.Null,
bcdCreateTime: BasicTime.GMT,
cmd: ROPENIL
];
SourceType: TYPE = {Mesa, Config};
SupportList: TYPE = LIST OF Support; Support: 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: CommandClass ← NEW [CommandClassRep ← [
NotCurrent: SourceNotCurrent,
Rederive: RederiveSource,
Explain: ExplainSource]];
GetParseKey: RedBlackTree.GetKey -- PROC [data: UserData] RETURNS [Key] -- = {
RETURN[ data ] };
CompareParses: RedBlackTree.Compare --PROC [k: Key, data: UserData] RETURNS [Basics.Comparison]-- = {
GetKey: PROC [ra: REF ANY] RETURNS [r: ROPE] = {
r ← WITH ra SELECT FROM
x: ParseData => x.source,
x: ROPE => x,
ENDCASE => ERROR};
RETURN [GetKey[k].Compare[s2: GetKey[data], case: FALSE]]};
SourceFind: PROC [resultName: ROPE, finderData: REF ANY] RETURNS [found: BOOLEAN, sought: Node, makes: NodeList, from, cmdFrom: From, cmd: ROPE, class: CommandClass, foundData: REF ANY] -- FinderProc -- =
BEGIN
bcdExpanded, shortName, baseName, sourceName, bcdName, configName, switches: ROPE;
bcdCP: FS.ComponentPositions;
sourceNode, bcdNode, configNode, switchesNode: Node;
bcdCreateTime: BasicTime.GMT;
md: SourceData;
sourceType: SourceType;
NoteDep: PROC [fileName: ROPE] =
{from.mustHave ← CONS[
GetNode[fileName.Cat[".BCD"], fileClass],
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 ← GetCreated[bcdNode];
from ← [NIL, NIL];
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 ← [NIL, LIST[sourceNode, switchesNode]];
foundData ← md ← NEW [SourceDataRep ← [bcdName: bcdName, sourceType: sourceType, supports: NIL, 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;
ExplainSource: ExplainProc --PROC [c: Command, to: IO.STREAM]-- =
BEGIN
NoteStamps: PROC [n: Node, cur, used: Stamp, dateworthy: BOOLEANFALSE] =
BEGIN
to.PutF["%g\t%g\t", IO.rope[IF cur # used THEN "*" ELSE ""], IO.rope[n.PublicPartsOfNode[].name]];
TRUSTED {ListerUtils.PrintVersion[used, to, dateworthy]};
to.PutRope["\t"];
TRUSTED {ListerUtils.PrintVersion[cur, to, dateworthy]};
to.PutRope["\n"];
END;
md: SourceData ← NARROW[c.PublicPartsOfCommand[].foundData];
bcdCreateTime: BasicTime.GMT ← GetCreated[md.bcdNode];
IF bcdCreateTime = notExistTime THEN {to.PutF["\t\tBCD doesn't exist\n"]; RETURN};
IF bcdCreateTime # md.bcdCreateTime THEN {
md.bcdCreateTime ← bcdCreateTime;
MakeSupports[md]};
to.PutF["\t%g of %g\n\treferences:\tused version:\tcurrent version\n", IO.rope[md.bcdName], IO.time[md.bcdCreateTime]];
NoteStamps[md.sourceNode, CurSourceStamp[md.sourceNode], md.sourceStamp, TRUE];
FOR sl: SupportList ← md.supports, sl.rest WHILE sl # NIL DO
curBCDStamp: Stamp = CurBCDStamp[sl.first.node];
NoteStamps[sl.first.node, curBCDStamp, sl.first.version];
ENDLOOP;
END;
SourceNotCurrent: PROC [c: Command, Needed: PROC [Node] RETURNS [BOOL]] RETURNS [whyNotCurrent: ROPE--NIL means all outputs up-to-date--] --NotCurrentProc-- =
BEGIN
md: SourceData ← NARROW[c.PublicPartsOfCommand[].foundData];
bcdCreateTime: BasicTime.GMT;
curSourceStamp: Stamp;
whyNotCurrent ← NIL;
IF NOT Needed[md.bcdNode] THEN RETURN;
bcdCreateTime ← GetCreated[md.bcdNode];
IF bcdCreateTime = MakeDo.notExistTime THEN RETURN;
IF bcdCreateTime # md.bcdCreateTime THEN {
md.bcdCreateTime ← bcdCreateTime;
MakeSupports[md]};
curSourceStamp ← CurSourceStamp[md.sourceNode];
IF NonCurrent[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]};
whyNotCurrent ← out.RopeFromROS[];
RETURN;
};
FOR sl: SupportList ← md.supports, sl.rest WHILE sl # NIL DO
curBCDStamp: Stamp = CurBCDStamp[sl.first.node];
IF NonCurrent[curBCDStamp, sl.first.version] THEN {
out: IO.STREAMIO.ROS[];
out.PutRope["last used version "];
TRUSTED {ListerUtils.PrintVersion[sl.first.version, out, FALSE]};
out.PutF[
" of %g, but current version is ",
[rope[sl.first.node.PublicPartsOfNode[].name]]
];
TRUSTED {ListerUtils.PrintVersion[curBCDStamp, out, FALSE]};
whyNotCurrent ← out.RopeFromROS[];
RETURN;
};
ENDLOOP;
whyNotCurrent ← NIL;
END;
NonCurrent: PROC [cur, old: Stamp] RETURNS [nc: BOOLEAN] =
{nc ← (cur # TimeStamp.Null) AND (cur # old)};
CurSourceStamp: PROC [node: Node] RETURNS [stamp: Stamp] =
BEGIN
time: Time ← GetCreated[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 ← GetCreated[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: ROPEFS.ExpandName[name.Cat[".BCD"]].fullFName;
node: Node ← GetNode[extName, fileClass];
md.supports ← CONS[[node, version], md.supports];
END;
bcd: ListerUtils.RefBCD;
md.supports ← NIL;
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];
END;
RederiveSource: PROC [c: Command] RETURNS [from: From, cmd: ROPE] --RederiveProc-- =
BEGIN
md: SourceData ← NARROW[c.PublicPartsOfCommand[].foundData];
NoteDep: PROC [fileName: ROPE] =
{from.mustHave ← CONS[
GetNode[fileName.Cat[".BCD"], fileClass],
from.mustHave]};
from ← [NIL, NIL];
[] ← EnumerateDependancies[md.sourceNode, md.sourceType, NoteDep];
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
pd: ParseData ← NARROW[parses.Lookup[sourceNode.PublicPartsOfNode[].name]];
cur: BasicTime.GMT;
IF pd = NIL THEN parses.Insert[pd ← NEW [ParseDataRep ← [source: sourceNode.PublicPartsOfNode[].name, sourceType: sourceType]], pd];
cur ← GetCreated[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[sourceNode.PublicPartsOfNode[].name, NoteDep];
Config => [] ← EnumerateConfigDependancies[sourceNode.PublicPartsOfNode[].name, 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] =
BEGIN
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 =
BEGIN
next: ROPE ← ParseCDirectory[];
next ← ParseCPacking[next];
next ← ParseConfiguration[next];
IF NOT next.Equal["."] THEN ERROR GotoSyntaxError;
END;
ParseConfiguration: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
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[];
END;
ParseConfigurationRemains: PROC RETURNS [next: ROPE] =
BEGIN
next ← ParseCHeadRemains[];
IF NOT next.Equal["="] THEN ERROR GotoSyntaxError;
ParseCBody[];
next ← NextToken[];
END;
ParseCBody: PROC =
BEGIN
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;
END;
ParseCStatement: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
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;
END;
ParseCExpression: PROC RETURNS [next: ROPE] =
BEGIN
next ← ParseCRightSide[];
WHILE next.Equal["PLUS"] DO
next ← ParseCRightSide[];
ENDLOOP;
WHILE next.Equal["THEN"] DO
next ← ParseCRightSide[];
ENDLOOP;
END;
ParseCRightSide: PROC RETURNS [next: ROPE] =
BEGIN
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;
END;
ParseCHeadRemains: PROC RETURNS [next: ROPE] =
BEGIN
next ← ParseCLinks[];
next ← ParseImports[next];
next ← ParseCExports[next];
next ← ParseControlClause[next];
END;
ParseCLinks: PROC [first: ROPENIL] RETURNS [next: ROPE] =
BEGIN
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[];
END;
ParseImports: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
IF NOT first.Equal["IMPORTS"] THEN RETURN [first];
next ← ParseItemList[FALSE];
END;
ParseCExports: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
IF NOT first.Equal["EXPORTS"] THEN RETURN [first];
next ← ParseItemList[FALSE];
END;
ParseControlClause: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
IF NOT first.Equal["CONTROL"] THEN RETURN [first];
next ← EatIDList[NIL, FALSE];
END;
ParseItemList: PROC [notice: BOOLEAN] RETURNS [next: ROPE] =
BEGIN
DO
next ← ParseItem[NIL, notice];
IF NOT next.Equal[","] THEN EXIT;
ENDLOOP;
END;
ParseItem: PROC [first: ROPE, notice: BOOLEAN] RETURNS [next: ROPE] =
BEGIN
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]};
END;
ParseCDirectory: PROC RETURNS [next: ROPE] =
BEGIN
firstClause: BOOLTRUE;
ParseClause: PROC RETURNS [next: ROPE] =
BEGIN
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];
END;
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[];
END;
ParseCPacking: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
next ← first;
WHILE next.Equal["PACK"] DO
next ← EatIDList[NIL, FALSE];
IF NOT next.Equal[";"] THEN ERROR GotoSyntaxError;
next ← NextToken[];
ENDLOOP;
END;
EatIDList: PROC [first: ROPE, notice: BOOLEAN] RETURNS [next: ROPE] =
BEGIN
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;
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[];
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[];
END;
AddFinder[["Compiler and Binder", SourceFind], front];
END.