<> <> <> DIRECTORY BasicTime, BcdDefs, BCDery, FS, IO, IOClasses, List, ListerUtils, MakeDo, RedBlackTree, Rope, TimeStamp; ConfigAndMesaDeps: CEDAR PROGRAM IMPORTS BasicTime, BCDery, FS, IO, IOClasses, List, ListerUtils, MakeDo, RedBlackTree, Rope = 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: ROPE _ NIL ]; 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: BasicTime.GMT _ BasicTime.nullGMT, refdModules: RopeList _ NIL]; parses: RedBlackTree.Table _ RedBlackTree.Create[GetParseKey, CompareParses]; configAndMesaClass: CommandClass _ NEW [CommandClassRep _ [ NotCurrent: SourceNotCurrent, Rederive: RederiveSource, UpdateTime: FileTime, Explain: ExplainSource]]; debugging: BOOLEAN _ FALSE; 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, from, why: NodeList, cmd: ROPE, class: CommandClass, foundData: REF ANY] -- FinderProc -- = BEGIN bcdExpanded, shortName, baseName, sourceName, bcdName, configName, switches: ROPE; bcdCP: FS.ComponentPositions; sourceNode, bcdNode, configNode: Node; bcdCreateTime: BasicTime.GMT; md: SourceData; sourceType: SourceType; NoteDep: PROC [fileName: ROPE] = {from _ CONS[ GetNode[FS.ExpandName[fileName.Cat[".BCD"]].fullFName], from]}; [bcdExpanded, bcdCP, ] _ FS.ExpandName[resultName]; 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"]; switches _ GetSwitches[bcdName]; makes _ LIST[sought _ bcdNode _ GetNode[bcdName]]; bcdCreateTime _ Latest[bcdNode, FileTime]; from _ NIL; sourceType _ Mesa; cmd _ Rope.Cat["RCompile ", switches, " ", sourceName _ baseName.Concat[".Mesa"]]; IF (NOT EnumerateDependancies[ (sourceNode _ GetNode[sourceName]), sourceType, NoteDep]) AND EnumerateDependancies[ (configNode _ GetNode[configName _ baseName.Concat[".Config"]]), Config, NoteDep] THEN { sourceName _ configName; sourceNode _ configNode; sourceType _ Config; cmd _ Rope.Cat["Bind ", switches, " ", shortName]}; why _ LIST[sourceNode]; foundData _ md _ NEW [SourceDataRep _ [bcdName: bcdName, sourceType: sourceType, supports: NIL, sourceNode: sourceNode, bcdNode: bcdNode, bcdCreateTime: bcdCreateTime, cmd: cmd]]; IF bcdCreateTime # BasicTime.nullGMT THEN MakeSupports[md]; from _ CONS[sourceNode, from]; class _ configAndMesaClass; END; GetSwitches: PROC [bcdName: ROPE] RETURNS [switches: ROPE] = BEGIN ss: IO.STREAM _ NIL; 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: BOOLEAN _ FALSE] = BEGIN to.PutF["\t%g%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]; IF md.bcdCreateTime = BasicTime.nullGMT THEN {to.PutF["\t\tBCD didn't exist\n"]; RETURN}; 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 NoteStamps[sl.first.node, CurBCDStamp[sl.first.node], sl.first.version]; ENDLOOP; END; SourceNotCurrent: PROC [c: Command] RETURNS [notCurrent: BOOLEAN] --NotCurrentProc-- = BEGIN md: SourceData _ NARROW[c.PublicPartsOfCommand[].foundData]; bcdCreateTime: BasicTime.GMT; IF NOT Needed[md.bcdNode] THEN RETURN; bcdCreateTime _ Latest[md.bcdNode]; IF bcdCreateTime = BasicTime.nullGMT THEN RETURN [FALSE]; IF bcdCreateTime # md.bcdCreateTime THEN { md.bcdCreateTime _ bcdCreateTime; MakeSupports[md]}; IF NonCurrent[CurSourceStamp[md.sourceNode], md.sourceStamp] THEN RETURN [TRUE]; FOR sl: SupportList _ md.supports, sl.rest WHILE sl # NIL DO IF NonCurrent[CurBCDStamp[sl.first.node], sl.first.version] THEN RETURN [TRUE]; ENDLOOP; notCurrent _ FALSE; 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: BasicTime.GMT _ Latest[node]; IF time = BasicTime.nullGMT THEN RETURN [TimeStamp.Null]; stamp _ [net: 0, host: 0, time: BasicTime.ToPupTime[time]]; END; CurBCDStamp: PROC [node: Node] RETURNS [stamp: Stamp] = BEGIN sr: StampRef _ NARROW[List.Assoc[key: $Stamp, aList: node.PublicPartsOfNode[].props]]; created: BasicTime.GMT; bcd: ListerUtils.RefBCD; IF sr = NIL THEN node.SetProps[List.PutAssoc[ key: $Stamp, val: sr _ NEW [StampRep _ [ created: BasicTime.nullGMT, stamp: TimeStamp.Null]], aList: node.PublicPartsOfNode[].props]]; created _ Latest[node, FileTime]; IF created = BasicTime.nullGMT THEN RETURN [TimeStamp.Null]; IF created = sr.created THEN RETURN [sr.stamp]; sr.created _ created; TRUSTED {bcd _ ListerUtils.ReadBcd[node.PublicPartsOfNode[].name !FS.Error => {bcd _ NIL; CONTINUE}]}; IF bcd = NIL THEN RETURN [TimeStamp.Null]; IF bcd.versionIdent # BcdDefs.VersionID THEN RETURN [TimeStamp.Null]; stamp _ sr.stamp _ bcd.version; 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]; 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: NodeList, cmd: ROPE] --RederiveProc-- = BEGIN md: SourceData _ NARROW[c.PublicPartsOfCommand[].foundData]; NoteDep: PROC [fileName: ROPE] = {from _ CONS[ GetNode[FS.ExpandName[fileName.Cat[".BCD"]].fullFName], from]}; from _ NIL; [] _ EnumerateDependancies[md.sourceNode, md.sourceType, NoteDep]; from _ CONS[md.sourceNode, from]; 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 _ Latest[sourceNode, FileTime]; IF NOT (exists _ cur # BasicTime.nullGMT) 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: BOOL _ TRUE; 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.STREAM _ NIL; source _ FS.StreamOpen[fileName: sourceName, accessOptions: $read !FS.Error => {source _ NIL; CONTINUE}]; exists _ source # NIL; IF debugging THEN Log[(IF exists THEN "Parsing %g" ELSE "Missing %g"), IO.rope[sourceName]]; 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 ROPE _ NIL; 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: BOOL _ FALSE; 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: BOOL _ FALSE; 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: ROPE _ NIL] 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: BOOL _ FALSE; 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: BOOL _ TRUE; 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.STREAM _ NIL; source _ FS.StreamOpen[fileName: sourceName, accessOptions: $read !FS.Error => {source _ NIL; CONTINUE}]; exists _ source # NIL; IF debugging THEN Log[(IF exists THEN "Parsing %g" ELSE "Missing %g"), IO.rope[sourceName]]; 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[[SourceFind], front]; END.