-- SMBcdImpl.mesa
-- last edit by Schmidt, June 2, 1983 5:23 pm
-- last edit by Satterthwaite, July 26, 1983 12:41 pm

-- code to produce and read .modelBcd files

DIRECTORY
  CS: TYPE USING [
    CardFromRope, EndsIn, RopeFromStamp, RootName, StampFromRope],
  FileIO: TYPE USING [Open],
  IO: TYPE USING [
    card, Close, EndOf, GetChar, GetSequence, LineAtATime, PutF, RIS, rope, STREAM],
  Rope: TYPE USING [Cat, Fetch, Flatten, Length, ROPE, Substr, Text],
  SMBcd: TYPE USING [],
  SMCommentTableOps: TYPE USING [CommentM],
  SMEval: TYPE USING [Eval],
  SMFI: TYPE USING [BcdFileInfo, SrcFileInfo],
  SMFIOps: TYPE USING [FindBcd, FindSource],
  SMOps: TYPE USING [MS],
  SMProj: TYPE USING [Proj, Analyzed, Find],
  SMTree: TYPE Tree USING [ApplOp, Handle, Link],
  SMTreeOps: TYPE USING [GetExt, OpName, Scan, ScanSons],
  SMUtil: TYPE USING [ParseStream, PrettyPrint],
  TimeStamp: TYPE USING [Stamp];

SMBcdImpl: CEDAR PROGRAM 
    IMPORTS CS, FileIO, IO, Rope, SMEval, SMFIOps, SMProj, SMTreeOps, SMUtil 
    EXPORTS SMBcd ~ {
  OPEN Tree~~SMTree, TreeOps~~SMTreeOps;

  SrcFIList: TYPE ~ LIST OF SMFI.SrcFileInfo;
  BcdFIList: TYPE ~ LIST OF SMFI.BcdFileInfo;
  ProjList: TYPE ~ LIST OF SMProj.Proj;

 -- making .modelBcd files

  WriteModelBcd: PUBLIC PROC[
	ms: SMOps.MS, t: Tree.Link, modelFileName, bcdFileName: Rope.Text] ~ {
    ENABLE UNWIND => {NULL};
    srcFIList: SrcFIList ← NIL;
    bcdFIList: BcdFIList ← NIL;
    
    AddSrcFi: PROC[fiSrc: SMFI.SrcFileInfo] ~ {
      FOR l: SrcFIList ← srcFIList, l.rest UNTIL l = NIL DO
	IF fiSrc = l.first THEN RETURN;
	ENDLOOP;
      srcFIList ← CONS[fiSrc, srcFIList]};
    
    AddBcdFi: PROC[fiBcd: SMFI.BcdFileInfo] ~ {
      FOR l: BcdFIList ← bcdFIList, l.rest UNTIL l = NIL DO
	IF fiBcd = l.first THEN RETURN;
	ENDLOOP;
      bcdFIList ← CONS[fiBcd, bcdFIList]};
    
    projList: ProjList ← NIL;

    AddProj: PROC[proj: SMProj.Proj] ~ {
      FOR l: ProjList ← projList, l.rest UNTIL l = NIL DO
	IF proj = l.first THEN RETURN;
	ENDLOOP;
      projList ← CONS[proj, projList]};
    
    FindFiles: TreeOps.Scan ~ {
      WITH t SELECT FROM
        node: Tree.Handle => {
	  TreeOps.ScanSons[node, FindFiles];
	  IF TreeOps.OpName[node] IN Tree.ApplOp THEN
	    WITH TreeOps.GetExt[node] SELECT FROM
	      proj: SMProj.Proj => AddProj[proj];
	      ENDCASE;
	  };
        fiSrc: SMFI.SrcFileInfo => AddSrcFi[fiSrc];
        fiBcd: SMFI.BcdFileInfo => AddBcdFi[fiBcd];
        ENDCASE => NULL;
      };

    outName: Rope.Text ~ (IF CS.EndsIn[bcdFileName, ".modelBcd"]
	THEN bcdFileName ELSE CS.RootName[bcdFileName].Cat[".modelBcd"].Flatten[]);
    outFile: IO.STREAM;
    FindFiles[t];
    outFile ← FileIO.Open[outName, $overwrite];
    BuildTypeTables[outFile, srcFIList, bcdFIList];
    BuildProjTable[outFile, projList];
    SMUtil.PrettyPrint[out~outFile, root~ms.tree, comments~NIL];
    outFile.Close[];
    ms.out.PutF[".modelBcd file written on %s\n", IO.rope[outName]]};
	
  BuildTypeTables: PROC[outFile: IO.STREAM, srcFIList: SrcFIList, bcdFIList: BcdFIList] ~ {
    FOR l: SrcFIList ← srcFIList, l.rest UNTIL l = NIL DO
      fiSrc: SMFI.SrcFileInfo ~ l.first;
      outFile.PutF["%d %s\n", IO.card[fiSrc.create], IO.rope[fiSrc.shortName]];
      SMUtil.PrettyPrint[out~outFile, root~fiSrc.type, comments~NIL];
      outFile.PutF[".\n"];
      ENDLOOP;
    outFile.PutF[".\n"];
    FOR l: BcdFIList ← bcdFIList, l.rest UNTIL l = NIL DO
      fiBcd: SMFI.BcdFileInfo ~ l.first;
      outFile.PutF["%s %s\n", IO.rope[CS.RopeFromStamp[fiBcd.stamp]], IO.rope[fiBcd.shortName]];
      SMUtil.PrettyPrint[out~outFile, root~fiBcd.type, comments~NIL];
      outFile.PutF[".\n"];
      ENDLOOP;
    outFile.PutF[".\n"]};
   
  BuildProjTable: PROC[outFile: IO.STREAM, projList: ProjList] ~ {
    FOR l: ProjList ← projList, l.rest UNTIL l = NIL DO
      proj: SMProj.Proj ~ l.first;
      outFile.PutF["%s %s", IO.rope[CS.RopeFromStamp[proj.stamp]], IO.rope[proj.localName]];
      outFile.PutF[IF proj.interface THEN " T " ELSE " F "];
      outFile.PutF["%d %d\n",
	  IO.card[proj.symbolPages.base], IO.card[proj.symbolPages.pages]];
      ENDLOOP;
    outFile.PutF[".\n"]};
   

 -- reading .modelBcd files

  GetLine: PROC[input: IO.STREAM] RETURNS[line: Rope.ROPE] ~ {
     line ← input.GetSequence[IO.LineAtATime];
     [] ← input.GetChar[]};		-- skip '\n
     
  ReadModelBcdPrefix: PUBLIC PROC[ms: SMOps.MS, input: IO.STREAM] ~ {
    ENABLE UNWIND => {NULL};
   -- reconstruct the type table
    ended: BOOL;
    header, line: Rope.ROPE;
    typeRope: Rope.ROPE;
    WHILE ~input.EndOf DO
     -- first line has the filename and create time (or terminator)
      header ← GetLine[input];
      typeRope ← NIL;
      IF header.Fetch[0] = '. AND header.Length = 1 THEN EXIT;
      ended ← input.EndOf;
      WHILE ~ended DO
	line ← GetLine[input];
	IF line.Fetch[line.Length-1] = '. THEN ended ← TRUE
	ELSE typeRope ← typeRope.Cat["\n", line];
	ENDLOOP;
      AddSrcFI[ms, header, typeRope];
      ENDLOOP;
    WHILE ~input.EndOf DO
     -- first line has the filename and version stamp (or terminator)
      header ← GetLine[input];
      typeRope ← NIL;
      IF header.Fetch[0] = '. AND header.Length = 1 THEN EXIT;
      ended ← input.EndOf;
      WHILE ~ended DO
	line ← GetLine[input];
	IF line.Fetch[line.Length-1] = '. THEN ended ← TRUE
	ELSE typeRope ← typeRope.Cat["\n", line];
	ENDLOOP;
      AddBcdFI[ms, header, typeRope];
      ENDLOOP;
   -- reconstruct the projection table
    WHILE ~input.EndOf DO
      line ← GetLine[input];
      IF line.Fetch[0] = '. AND line.Length = 1 THEN EXIT;
      AddProj[ms, line];
      ENDLOOP;
    };
       

  AddSrcFI: PROC[ms: SMOps.MS, header, typeRope: Rope.ROPE] ~ {
    createTime: LONG CARDINAL;
    fiSrc: SMFI.SrcFileInfo;
    start, next: INT ← 0;
    length: INT ~ header.Length;
    
    NextToken: PROC ~ {
      WHILE next < length AND header.Fetch[next] = '  DO next ← next + 1 ENDLOOP;
      start ← next;
      WHILE next < length AND header.Fetch[next] # '  DO next ← next + 1 ENDLOOP};
      
    NextToken[];
    createTime ← CS.CardFromRope[header.Substr[start, next-start]];
    fiSrc ← SMFIOps.FindSource[createTime];
    IF fiSrc.state < $analyzed THEN {	-- move to SMFIImpl
      fileName: Rope.Text;
      NextToken[];
      fileName ← header.Flatten[start, next];
      fiSrc.host ← fiSrc.directory ← NIL; fiSrc.shortName ← fileName; fiSrc.version ← 0;
      fiSrc.localName ← fiSrc.shortName;  fiSrc.new ← FALSE;
      fiSrc.type ← SMEval.Eval[ms, SMUtil.ParseStream[ms, IO.RIS[typeRope]], NIL];
      fiSrc.state ← $analyzed;
      IF ms.debugFlag THEN ms.out.PutF["Insert %s into fi\n", IO.rope[fileName]]};
    };
    
  AddBcdFI: PROC[ms: SMOps.MS, header, typeRope: Rope.ROPE] ~ {
    stamp: TimeStamp.Stamp;
    fiBcd: SMFI.BcdFileInfo;
    start, next: INT ← 0;
    length: INT ~ header.Length;
    
    NextToken: PROC ~ {
      WHILE next < length AND header.Fetch[next] = '  DO next ← next + 1 ENDLOOP;
      start ← next;
      WHILE next < length AND header.Fetch[next] # '  DO next ← next + 1 ENDLOOP};
      
    NextToken[];
    stamp ← CS.StampFromRope[header.Substr[start, next-start]];
    fiBcd ← SMFIOps.FindBcd[stamp];
    IF fiBcd.state < $analyzed THEN {	-- move to SMFIImpl
      fileName: Rope.Text;
      NextToken[];
      fileName ← header.Flatten[start, next];
      fiBcd.host ← fiBcd.directory ← NIL; fiBcd.shortName ← fileName; fiBcd.version ← 0;
      fiBcd.localName ← fiBcd.shortName;
      fiBcd.type ← SMEval.Eval[ms, SMUtil.ParseStream[ms, IO.RIS[typeRope]], NIL];
      fiBcd.state ← $analyzed;
      IF ms.debugFlag THEN ms.out.PutF["Insert %s into fi\n", IO.rope[fileName]]};
    };
    
    
  AddProj: PROC[ms: SMOps.MS, line: Rope.ROPE] ~ {
    stamp: TimeStamp.Stamp;
    proj: SMProj.Proj;
    start, next: INT ← 0;
    length: INT ~ line.Length;
    
    NextToken: PROC ~ {
      WHILE next < length AND line.Fetch[next] = '  DO next ← next + 1 ENDLOOP;
      start ← next;
      WHILE next < length AND line.Fetch[next] # '  DO next ← next + 1 ENDLOOP};
      
    NextToken[];
    stamp ← CS.StampFromRope[line.Substr[start, next-start]];
    proj ← SMProj.Find[stamp];
    IF ~proj.Analyzed THEN {	-- move to SMProjImpl
      fileName: Rope.Text;
      NextToken[];
      fileName ← line.Flatten[start, next];
      proj.host ← proj.directory ← NIL; proj.shortName ← fileName; proj.version ← 0;
      NextToken[];
      proj.interface ← (line.Fetch[start] = 'T);
      NextToken[];
      proj.symbolPages.base ← CS.CardFromRope[line.Substr[start, next-start]];
      NextToken[];
      proj.symbolPages.pages ← CS.CardFromRope[line.Substr[start, next-start]];
      proj.localName ← proj.shortName;
      proj.state ← $analyzed;
      IF ms.debugFlag THEN ms.out.PutF["Insert %s into proj\n", IO.rope[fileName]]};
    };
    
  }.