-- MDDBImpl.mesa
-- last edit by Schmidt, January 6, 1983 2:25 pm
-- last edit by Satterthwaite, January 31, 1983 9:24 am
-- Pilot 6.0/ Mesa 7.0
-- data base stuff for the system modeller

DIRECTORY
  CWF: TYPE USING [FWF2, WF0, WF1, WF2, WF3, WF4, WFCR],
  DBStash: TYPE USING [BcdVersLookup, GetTemporaryDepSeq, Insert, Lookup],
  Dir: TYPE USING [AddToDep, ADepRecord, DepSeq, DepSeqRecord, FileInfo, MaximumDepSize,
     ModType],
  Directory: TYPE USING [UpdateDates],
  File: TYPE USING [Capability, read],
  FileStream: TYPE USING [Create],
  LongString: TYPE USING [EqualString, EquivalentString],
  MDComp: TYPE USING [SetVersAndModulename],
  MDDB: TYPE USING [],
  MDMain: TYPE USING [DebugWP],
  MDModel: TYPE USING [AddToEndOfList, APPLSymbol,  
  	FreeStringsOf, GetBcdCreate, GetFileInfo, GetSrcCreate, 
	LETSymbol, LISTSymbol, LOCSymbol, LocForAppl, LocForType, 
	LookForTypeBcd, LookForTypeSource,  LookForInstBcd, LookForInstSource,
	MergeIntoList, MODELSymbol, NarrowToAPPL, NarrowToLET, 
	NarrowToLIST, NarrowToLOC, NarrowToPROC, 
	NarrowToTYPE, NewSymLOC, numberofbcdsmapped, 
	numberofsourcesparsed, ParseUnit, PROCSymbol, 
	Sym, Symbol, SymbolSeq, TraverseAndRemove, TraverseList, TraverseTree, TYPESymbol],
  MoveFiles: TYPE USING [BringOverRemoteFile],
  ProcBcds: TYPE USING [InnardsObject, InstallAddressesBcd, InvalidBcd, PrintDepends, 
  	ProcDep, ProcMod, ReadInSegmentsBcd, UnstallBcd],
  Space: TYPE USING [Handle, nullHandle],
  Stream: TYPE USING [Delete, Handle],
  Subr: TYPE USING [AbortMyself, CopyString, debugflg, EndsIn, errorflg, FreeString, 
  	LongZone, TTYProcs],
  Time: TYPE USING [Current],
  TimeStamp: TYPE USING [Null, Stamp],
  TypeScript: TYPE USING[TS, UserAbort];

MDDBImpl: PROGRAM
IMPORTS CWF, DBStash, Dir, Directory, FileStream, LongString, MDComp, MDMain, MDModel, 
	MoveFiles, ProcBcds, Space, Stream, Subr, Time, TypeScript 
EXPORTS MDDB = {

-- no MDS usage!!!

-- as a side effect sets sploc.moduleName if possible
BringOverFilesAndCheckAllParms: PUBLIC PROC[symbolseq: MDModel.SymbolSeq,
	makethismodel: BOOL, typeScript: TypeScript.TS, ttywindow: Subr.TTYProcs] = {
parmsfilledin: BOOL;
time: LONG CARDINAL;
checkcalled: CARDINAL ← 0;

	-- done preorder
	ProcAnalyze: PROC[sptop: MDModel.Symbol, spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL ← TRUE] = {
	
		-- calls itself recursively
		-- sptop is passed in
		ProcLoc: PROC[spl: MDModel.Symbol] = {
		IF spl = NIL THEN RETURN;
		IF spl.stype = typeLIST THEN 
			MDModel.TraverseList[MDModel.NarrowToLIST[spl], ProcLoc]
		ELSE IF spl.stype = typeLOC THEN {
			sploc: MDModel.LOCSymbol = MDModel.NarrowToLOC[spl];
			IF CheckAndFillInParameters[sptop, sploc, symbolseq, 
				spmodel, makethismodel, typeScript, ttywindow] THEN 
					parmsfilledin ← TRUE;
			checkcalled ← checkcalled + 1;
			};
		};
		
	WITH spt~~sptop SELECT FROM
	typeTYPE => ProcLoc[spt.typeval];
	typeAPPL => ProcLoc[spt.applval];
	typeLET => ProcLoc[spt.letval];
	typeMODEL => CWF.WF1["Examining files in model %s:\n"L, 
		spt.modelfilename];
	ENDCASE => NULL;
	RETURN[TRUE];
	};

IF TypeScript.UserAbort[typeScript] THEN SIGNAL Subr.AbortMyself;
-- Dir.ReadInUnsavedFiles[ttywindow];
parmsfilledin ← FALSE;
time ← Time.Current[];
-- preorder is important here
MDModel.TraverseTree[symbolseq.toploc, symbolseq, ProcAnalyze, TRUE];
time ← Time.Current[] - time;
CWF.WF0["\n"L];
MoveUndefinedsToParms[symbolseq];
IF parmsfilledin THEN CWF.WF0["Parms filled in during StartModel.\n"L];
IF TypeScript.UserAbort[typeScript] THEN Subr.AbortMyself[];
CWF.FWF2[MDMain.DebugWP, "CheckAndFillInParameters called %u times, totaltime %lu seconds.*N"L,
	@checkcalled, @time];
};

-- this will cause the source file to be parsed
-- sp may be type TYPE, APPL, LET
CheckAndFillInParameters: PUBLIC PROC[sp: MDModel.Symbol, 
	sploc: MDModel.LOCSymbol, symbolseq: MDModel.SymbolSeq, 
	spmodel: MDModel.MODELSymbol, makethismodel: BOOL,
	typeScript: TypeScript.TS, ttywindow: Subr.TTYProcs] RETURNS[modelchanged: BOOL]= {
fdepseq: Dir.DepSeq ← NIL;
isincache: BOOL ← TRUE;
fi: Dir.FileInfo;

IF spmodel = NIL THEN ERROR;
modelchanged ← FALSE;
IF sploc = NIL THEN RETURN;
IF TypeScript.UserAbort[typeScript] THEN SIGNAL Subr.AbortMyself;
fi ← MDModel.GetFileInfo[sploc];
[fdepseq, isincache] ← MakeDepSeqForFile[symbolseq, sploc, spmodel, makethismodel,
	typeScript, ttywindow];
IF fdepseq = NIL THEN RETURN;
modelchanged ← MatchUpAndAddParameters[fdepseq, sp, sploc, symbolseq, spmodel];
SetModuleName[fi, fdepseq.moduleName];
IF fi.isBcd AND fi.bcdVers = TimeStamp.Null THEN 
	fi.bcdVers ← [net: 0, host: 0, time: sploc.createtime];
-- check correspondence between TYPE and modulename
IF sp.stype = typeTYPE THEN {
	sptype: MDModel.TYPESymbol = MDModel.NarrowToTYPE[sp];
	IF NOT LongString.EqualString[fi.moduleName, sptype.typeName] THEN 
		CWF.WF3["Warning - ModuleName for %s is %s, but model says it is %s.\n"L,
			fi.bcdFileName, fi.moduleName, sptype.typeName];
	};
-- never free an fdepseq if is in the cache
IF NOT isincache THEN FreeDepSeq[@fdepseq];
};

-- called by CheckAndFillInParameters, MakeADepRecord, and ReorganizeDefinitions
--
-- the depseq this returns is used to determine parameters to the model or element in model
-- if the bcd is listed in the model, we use the bcd
-- if the src is listed in the model, we use the src,
-- or if the source is not on the disk, we use the bcd
MakeDepSeqForFile: PROC[symbolseq: MDModel.SymbolSeq, sploc: MDModel.LOCSymbol, 
	spmodel: MDModel.MODELSymbol, makethismodel: BOOL, 
	typeScript: TypeScript.TS, ttywindow: Subr.TTYProcs] 
	RETURNS[fdepseq: Dir.DepSeq, isincache: BOOL] = {
fi: Dir.FileInfo = MDModel.GetFileInfo[sploc];
IF NOT fi.alreadyLookedFor THEN
   MoveFiles.BringOverRemoteFile[sploc, makethismodel, typeScript, ttywindow];
IF sploc.nestedmodel ~= NIL 
AND LongString.EquivalentString[sploc.sext, "model"L] 
AND spmodel ~= NIL THEN {
	fdepseq ← FillInFromModel[sploc, symbolseq, spmodel, 
		makethismodel, ttywindow];
	isincache ← FALSE;
	}
ELSE {
	-- make an fdepseq; prefer the bcd
	fdepseq ← NIL;
	isincache ← TRUE;
	IF NOT fi.bcdPresent AND NOT fi.srcPresent THEN GOTO out;
	IF fi.isBcd THEN {
		IF NOT fi.bcdPresent THEN GOTO out;
		fdepseq ← GetBcdDepSeq[fi, sploc.createtime];
		GOTO out;
		};
	IF fi.srcPresent THEN 
		fdepseq ← GetSrcDepSeq[fi, sploc.createtime]
	ELSE fdepseq ← GetBcdDepSeq[fi, 0];
	EXITS
	out => NULL
	};
IF fdepseq ~= NIL THEN
	SetModuleName[fi, fdepseq.moduleName];
};
	
-- called by MakeDepSeqForFile
SetModuleName: PROC[fi: Dir.FileInfo, moduleName: LONG STRING] = {
IF fi.moduleName = NIL AND moduleName ~= NIL THEN
	fi.moduleName ← Subr.CopyString[moduleName];
};

MatchUpAndAddParameters: PROC[fdepseq: Dir.DepSeq, 
	sptop: MDModel.Symbol, sploc: MDModel.LOCSymbol, 
	symbolseq: MDModel.SymbolSeq, spmodel: MDModel.MODELSymbol]
	RETURNS[modelchanged: BOOL] = {
newlist: MDModel.LISTSymbol;
fakesploc: MDModel.LOCSymbol;
fentry: LONG POINTER TO Dir.ADepRecord;
found: BOOL ← TRUE;
processingExports: BOOL ← FALSE;
 
	-- uses fentry and fakesploc
	SetMod: PROC = {
	dep: Dir.DepSeq;
	IF fentry.bcdVers ~= TimeStamp.Null 
	AND (dep ← DBStash.BcdVersLookup[fentry.bcdVers]) ~= NIL THEN
		fentry.moduleName ← fdepseq.CopyString[dep.moduleName]
	ELSE IF Subr.EndsIn[fentry.bcdFileName, ".bcd"L] THEN {
		-- this is a very slow way to figure out modulenames
		fakesploc.tail ← Subr.CopyString[fentry.bcdFileName];
		fakesploc.tail.length ← fakesploc.tail.length - 4;
		fakesploc.sext ← Subr.CopyString["Bcd"L];
		MDComp.SetVersAndModulename[fakesploc];
		IF fakesploc.fi.moduleName ~= NIL THEN 
			fentry.moduleName ← fdepseq.CopyString[fakesploc.fi.moduleName];
		MDModel.FreeStringsOf[fakesploc];
		fakesploc.tail ← NIL;
		fakesploc.sext ← NIL;
		};
	};
	
	-- uses fentry
	PrintRel: PROC = {
	IF Subr.debugflg THEN
		CWF.WF3["\n\tIn file, but not model: %s (%s), relation %s."L,
			fentry.bcdFileName, fentry.moduleName,
			SELECT fentry.relation FROM
			imports => "Imports"L,
			exports => "Exports"L,
			directory => "Directory"L,
			ENDCASE => ERROR];
	};
	
	MatchTYPE: PROC[sp: MDModel.Symbol] RETURNS[remove: BOOLEAN] = {
	sptype: MDModel.TYPESymbol;
	IF sp.stype ~= typeTYPE OR found THEN RETURN[FALSE];
	sptype ← MDModel.NarrowToTYPE[sp];
	IF fentry.moduleName = NIL THEN SetMod[];
	IF LongString.EqualString[sptype.typeName, fentry.moduleName] THEN {
		newlist ← MDModel.AddToEndOfList[newlist, sptype, normal, symbolseq];
		found ← TRUE;
		RETURN[TRUE];
		};
	RETURN[FALSE];
	};
	
	RemoveTYPE: PROC[sp: MDModel.Symbol] RETURNS[remove: BOOLEAN] = {
	RETURN[sp.stype = typeTYPE];
	};

	MatchAPPL: PROC[sp: MDModel.Symbol] RETURNS[remove: BOOLEAN] = {
	spappl: MDModel.APPLSymbol;
	sptype: MDModel.TYPESymbol;
	IF sp.stype ~= typeAPPL OR found THEN RETURN[FALSE];
	spappl ← MDModel.NarrowToAPPL[sp];
	sptype ← MDModel.NarrowToTYPE[spappl.appltype];
	IF fentry.moduleName = NIL THEN SetMod[];
	IF LongString.EqualString[sptype.typeName, fentry.moduleName] THEN {
		newlist ← MDModel.AddToEndOfList[newlist, spappl, normal, symbolseq];
		found ← TRUE;
		RETURN[TRUE];
		};
	RETURN[FALSE];
	};
	
	RemoveAPPL: PROC[sp: MDModel.Symbol] RETURNS[remove: BOOLEAN] = {
	spappl: MDModel.APPLSymbol;
	IF sp.stype ~= typeAPPL THEN RETURN[FALSE];
	spappl ← MDModel.NarrowToAPPL[sp];
	IF spappl.appltype = symbolseq.controlv THEN RETURN[FALSE];	-- CONTROL parameter
	IF processingExports  
	AND MDModel.LocForType[MDModel.NarrowToTYPE[spappl.appltype]] 
		= MDModel.LocForAppl[spappl] THEN RETURN[FALSE];	-- exported ptr to frame
	RETURN[TRUE];
	};

{
fi: Dir.FileInfo;
splet: MDModel.LETSymbol;
skipTypes: BOOLEAN ← FALSE;
IF sploc = NIL THEN ERROR;
modelchanged ← FALSE;
fi ← MDModel.GetFileInfo[sploc];
fakesploc ← MDModel.NewSymLOC[symbolseq];
IF Subr.debugflg THEN
	CWF.WF2["ModuleName %s, File %s"L,fdepseq.moduleName, fdepseq.srcFileName];
-- directory
-- skipTypes: if the model refers to a source file, but the source
-- is not on the local disk and we have an fdepseq for the .Bcd, then don't muck with the
-- TYPES since we don't have enough information
skipTypes ← NOT fi.isBcd AND NOT fdepseq.fromsource;
newlist ← NIL;
IF NOT fi.isBcd AND NOT LongString.EquivalentString[sploc.sext, "config"L] AND NOT skipTypes THEN {
	-- TYPEs are not valid parameters for .Bcd file or .Config files
	-- they are removed 
	FOR i: CARDINAL IN [0 .. fdepseq.size) DO
		fentry ← @fdepseq[i];
		IF fentry.relation ~= directory THEN LOOP;
		found ← FALSE;
		sploc.parmlist ← MDModel.TraverseAndRemove[sploc.parmlist, MatchTYPE];
		IF NOT found THEN {
			sptype: MDModel.TYPESymbol;
			PrintRel[];
			IF fdepseq.fromsource THEN {
				[sptype] ← MDModel.LookForTypeSource[fentry.bcdFileName, fentry.moduleName, 
					symbolseq, spmodel];
				IF sptype = NIL THEN {
					CWF.WF3["Error - %s requires a variable declared as  '%s: TYPE %s', and there is no such declaration in the model.\n"L,
						fi.srcFileName, fentry.bcdFileName, fentry.moduleName];
					CWF.WF0["Edit the model and start over.\n"L];
					};
				}
			ELSE {
				-- this is screwy since we may have an entry with a bcdfilename and bcdvers,
				-- and cannot search the model since all the bcdvers have not been 
				-- filled in
				[sptype] ← MDModel.LookForTypeBcd[fentry.bcdFileName, fentry.bcdVers, 
					symbolseq, spmodel];
				IF sptype = NIL THEN {
					CWF.WF4["Error - %s requires %s of %v as a parameter, but no parameter to %s is of that type.\n"L,
						fdepseq.bcdFileName, fentry.bcdFileName, @fentry.bcdVers, 
						IF fi.srcFileName = NIL THEN fi.bcdFileName ELSE fi.srcFileName];
					CWF.WF0["Edit the model and start over.\n"L];
					};
				};
			IF sptype ~= NIL THEN {
				newlist ← MDModel.AddToEndOfList[newlist, sptype, normal, symbolseq];
				modelchanged ← TRUE;
				};
			};
		ENDLOOP;
	};
IF NOT skipTypes THEN {
	sploc.parmlist ← MDModel.TraverseAndRemove[sploc.parmlist, RemoveTYPE];
	-- now add reorganized definitions
	IF sploc.parmlist ~= NIL AND newlist ~= NIL THEN
		sploc.parmlist ← MDModel.NarrowToLIST[MDModel.MergeIntoList[
			newlist, sploc.parmlist, symbolseq, normal]]
	ELSE IF newlist ~= NIL THEN sploc.parmlist ← newlist;
	};
--
-- now do imports
newlist ← NIL;
-- skip imports to definitions files and imports to files only used as FRAMEPTRTYPEs
IF NOT fdepseq.isdefns AND sptop.stype ~= typeTYPE THEN {
	FOR i: CARDINAL IN [0 .. fdepseq.size) DO
		fentry ← @fdepseq[i];
		IF fentry.relation ~= imports THEN LOOP;
		found ← FALSE;
		sploc.parmlist ← MDModel.TraverseAndRemove[sploc.parmlist, MatchAPPL];
		IF NOT found THEN {
			spappl: MDModel.APPLSymbol;
			PrintRel[];
			IF fdepseq.fromsource THEN {
				[spappl] ← MDModel.LookForInstSource[fentry.bcdFileName, fentry.moduleName,
					symbolseq, spmodel, NIL];
				IF spappl = NIL THEN {
					CWF.WF3["Error - %s imports a variable declared as '%sImpl: %s', and there is no such declaration in the model.\n"L,
						fi.srcFileName, fentry.bcdFileName, fentry.moduleName];
					CWF.WF0["Edit the model and start over.\n"L];
					};
				}
			ELSE {
				[spappl] ← MDModel.LookForInstBcd[fentry.bcdFileName, fentry.bcdVers,
					symbolseq, spmodel, NIL];
				IF spappl = NIL THEN {
					CWF.WF4["Error - %s imports %s of %v as a parameter, but no parameter to %s is of that type.\n"L,
						fdepseq.bcdFileName, fentry.bcdFileName, 
						@fentry.bcdVers, 
						IF fi.srcFileName = NIL THEN fi.bcdFileName ELSE fi.srcFileName];
					CWF.WF0["Edit the model and start over.\n"L];
					};
				};
			IF spappl ~= NIL THEN {
				newlist ← MDModel.AddToEndOfList[newlist, spappl, normal, symbolseq];
				modelchanged ← TRUE;
				};
			};
		ENDLOOP;
	};
sploc.parmlist ← MDModel.TraverseAndRemove[sploc.parmlist, RemoveAPPL];
-- now add reorganized definitions
IF sploc.parmlist ~= NIL AND newlist ~= NIL THEN
	sploc.parmlist ← MDModel.NarrowToLIST[MDModel.MergeIntoList[
		sploc.parmlist, newlist, symbolseq, normal]]
ELSE IF newlist ~= NIL THEN sploc.parmlist ← newlist;
-- 
-- now do exports
IF sptop.stype ~= typeLET THEN GOTO leave;	-- won't massage into LET stmt
processingExports ← TRUE;
splet ← MDModel.NarrowToLET[sptop];
newlist ← NIL;
FOR i: CARDINAL IN [0 .. fdepseq.size) DO
	fentry ← @fdepseq[i];
	IF fentry.relation ~= exports THEN LOOP;
	found ← FALSE;
	splet.letgrp ← MDModel.TraverseAndRemove[splet.letgrp, MatchAPPL];
	IF NOT found THEN {
		PrintRel[];
		IF fdepseq.fromsource THEN {
			CWF.WF3["Warning - %s exports a variable with type '%s', and there is no variable of that type in the LET stmt for %s.\n"L,
				fi.srcFileName, fentry.moduleName, 
				IF fi.srcFileName = NIL THEN fi.bcdFileName ELSE fi.srcFileName];
			}
		ELSE {
			CWF.WF4["Warning - %s exports %s of %v, but no variable in the LET statement for %s is of that type.\n"L,
				fdepseq.bcdFileName, fentry.bcdFileName, @fentry.bcdVers,  
				IF fi.srcFileName = NIL THEN fi.bcdFileName ELSE fi.srcFileName];
			}
		};
	ENDLOOP;
splet.letgrp ← MDModel.TraverseAndRemove[splet.letgrp, RemoveAPPL];
-- now add reorganized definitions
IF splet.letgrp ~= NIL AND newlist ~= NIL THEN
	splet.letgrp ← MDModel.NarrowToLIST[MDModel.MergeIntoList[
		splet.letgrp, newlist, symbolseq, normal]]
ELSE IF newlist ~= NIL THEN splet.letgrp ← newlist;
--
GOTO leave;
EXITS
leave => IF Subr.debugflg THEN {
		IF NOT modelchanged THEN CWF.WF0[": Agree."L];
		CWF.WFCR[];
		};
}};

MakeADepRecord: PROC[sptype: MDModel.TYPESymbol, depseq: Dir.DepSeq,
	spmodel: MDModel.MODELSymbol, makethismodel: BOOL, 
	ttywindow: Subr.TTYProcs, symbolseq: MDModel.SymbolSeq] 
	RETURNS[adeprecord: Dir.ADepRecord] = {
-- fi: Dir.FileInfo;
-- fdepseq: Dir.DepSeq;
-- sploc: MDModel.LOCSymbol;
-- isincache: BOOL;
adeprecord ← [moduleName: depseq.CopyString[sptype.typeName],
	bcdFileName: NIL, bcdVers: TimeStamp.Null];
-- adeprecord ← [];
-- sploc ← MDModel.LocForType[sptype];
-- IF sploc = NIL THEN RETURN;
-- fi ← MDModel.GetFileInfo[sploc];
-- [fdepseq, isincache] ← MakeDepSeqForFile[symbolseq, sploc, NIL, makethismodel, ttywindow];
-- MDComp.SetVersAndModulename[sploc];
-- IF fdepseq = NIL AND fi.moduleName = NIL THEN 
	-- fi.moduleName ← Subr.CopyString[sptype.typesym];  make up name
-- adeprecord ← [moduleName: depseq.CopyString[fi.moduleName],
	-- bcdFileName: depseq.CopyString[fi.bcdFileName], 
	-- bcdVers: fi.bcdVers];
};

-- only needed in MDDBImpl
FreeDepSeq: PROC[pdepseq: LONG POINTER TO Dir.DepSeq] = {
longzone: UNCOUNTED ZONE = Subr.LongZone[];
IF pdepseq↑ = NIL THEN RETURN;
Subr.FreeString[pdepseq↑.bcdFileName];
Subr.FreeString[pdepseq↑.srcFileName];
Subr.FreeString[pdepseq↑.moduleName];
FOR i: CARDINAL IN [0.. pdepseq↑.size) DO
	Subr.FreeString[pdepseq↑[i].moduleName];
	Subr.FreeString[pdepseq↑[i].bcdFileName];
	ENDLOOP;
longzone.FREE[pdepseq];
};

-- this returns a DepSeq that is not in the cache
FillInFromModel: PROC[sploc: MDModel.LOCSymbol, symbolseq: MDModel.SymbolSeq,
	spmodel: MDModel.MODELSymbol, makethismodel: BOOL, 
	ttywindow: Subr.TTYProcs] 
	RETURNS[depseq: Dir.DepSeq] = {
stemp: STRING ← [100];
adeprecord: Dir.ADepRecord;
spproc: MDModel.PROCSymbol ← NIL;
splist: MDModel.LISTSymbol;
relation: Dir.ModType;
longzone: UNCOUNTED ZONE = Subr.LongZone[];

	ProcAddName: PROC[sp: MDModel.Symbol] = {
	WITH spt~~sp SELECT FROM
	typeAPPL => {
		sptype: MDModel.TYPESymbol;
		IF spt.applsym = NIL THEN RETURN;
		sptype ← MDModel.NarrowToTYPE[spt.appltype];
		adeprecord ← MakeADepRecord[sptype, depseq, spmodel, 
			makethismodel, ttywindow, symbolseq];
		adeprecord.relation ← relation;
		Dir.AddToDep[depseq, @adeprecord];
		};
	typeLET, typeTYPE, typeSTRING => NULL;	-- this is an error, but ...
	ENDCASE => ERROR;
	};

depseq ← NIL;
splist ← sploc.nestedmodel.model;
WHILE splist ~= NIL DO
	IF splist.first.stype = typePROC THEN {
		spproc ← MDModel.NarrowToPROC[splist.first];
		EXIT;
		};
	splist ← splist.rest;
	ENDLOOP;
IF spproc = NIL THEN RETURN;
depseq ← longzone.NEW[Dir.DepSeqRecord[Dir.MaximumDepSize]];
depseq.CopyString ← Subr.CopyString;
relation ← imports;
MDModel.TraverseList[spproc.procparm, ProcAddName];
relation ← exports;
MDModel.TraverseList[spproc.procret, ProcAddName];
-- CWF.WF1["Nested model, imports and exports %u.\n"L, @depseq.size];
};

MoveUndefinedsToParms: PROC[symbolseq: MDModel.SymbolSeq] = {

	ProcAnal: PROC[sptop: MDModel.Symbol, spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL ← TRUE] = {
	spm: MDModel.PROCSymbol; 
		
		AddIt: PROC[spa1: MDModel.Symbol] RETURNS[remove: BOOL] = {
		IF spa1.stype = typeAPPL 
		AND MDModel.NarrowToAPPL[spa1].applval = NIL THEN {
			spm.procparm ← MDModel.AddToEndOfList[spm.procparm, 
				spa1, normal, symbolseq];
			CWF.WF2["%s has been added as a parameter to the model %s.\n"L,
				MDModel.Sym[spa1], spmodel.modelfilename];
			RETURN[TRUE];
			};
		IF spa1.stype = typeLET THEN {
			spadd: MDModel.APPLSymbol;
			splet: MDModel.LETSymbol = MDModel.NarrowToLET[spa1];
			IF splet.letval = NIL AND splet.letgrp.rest = NIL THEN {
				IF splet.letgrp.first.stype = typeLET THEN 
					spadd ← MDModel.NarrowToAPPL[splet.letgrp.first]
				ELSE spadd ← MDModel.NarrowToAPPL[splet.letgrp.first];
				spm.procparm ← MDModel.AddToEndOfList[spm.procparm, 
					spadd, normal, symbolseq];
				CWF.WF2["%s has been added as a parameter to the model %s.\n"L,
					spadd.applsym, spmodel.modelfilename];
				RETURN[TRUE];
				};
			};
		RETURN[FALSE];
		};
		
	IF sptop.stype ~= typePROC THEN RETURN;
	spm ← MDModel.NarrowToPROC[sptop];
	spm.procval ← MDModel.TraverseAndRemove[MDModel.NarrowToLIST[spm.procval],
		 AddIt];
	};
	
MDModel.TraverseTree[symbolseq.toploc, symbolseq, ProcAnal];
};

-- srcCreate is treated as a hint
-- this parses source files
-- returns NIL if can't parse source
GetSrcDepSeq: PUBLIC PROC[fi: Dir.FileInfo, srcCreate: LONG CARDINAL] RETURNS[srcDepSeq: Dir.DepSeq] = {
IF fi.srcDepSeq ~= NIL THEN {
	-- not a hit DBStash.BumpNHits[];
	RETURN[fi.srcDepSeq];
	};
{
cap: File.Capability;
stin: Stream.Handle;
IF NOT Subr.EndsIn[fi.srcFileName, ".mesa"L] 
AND NOT Subr.EndsIn[fi.srcFileName, ".config"L] THEN RETURN[NIL];
IF NOT fi.srcPresent THEN GOTO err;
IF srcCreate ~= 0 AND (fi.srcDepSeq ← DBStash.Lookup[srcCreate, fi.srcFileName]) ~= NIL THEN 
	RETURN[fi.srcDepSeq];
IF (fi.srcDepSeq ← DBStash.Lookup[MDModel.GetSrcCreate[fi], fi.srcFileName]) ~= NIL THEN 
	RETURN[fi.srcDepSeq];
IF Subr.debugflg THEN CWF.WF1["AnalSource: %s\n"L, fi.srcFileName];
srcDepSeq ← DBStash.GetTemporaryDepSeq[];
srcDepSeq.srcCreate ← fi.srcCreate;
srcDepSeq.fromsource ← TRUE;
cap ← Directory.UpdateDates[fi.srcCap, File.read];
stin ← FileStream.Create[cap];
srcDepSeq.srcFileName ← srcDepSeq.CopyString[fi.srcFileName];
MDModel.ParseUnit[stin, srcDepSeq, fi.srcFileName];
Stream.Delete[stin];
srcDepSeq ← DBStash.Insert[srcDepSeq.srcCreate, fi.srcFileName, srcDepSeq];
fi.srcDepSeq ← srcDepSeq;
MDModel.numberofsourcesparsed ← MDModel.numberofsourcesparsed + 1;
RETURN[srcDepSeq];
EXITS
err => RETURN[NIL];
-- CWF.WF1["SourceParser: Can't open file %s\n"L, srcsfn];
}};

-- this reads in bcd files
-- bcdDepSeq is returned, never free it
-- bcdCreate is treated as a hint
GetBcdDepSeq: PUBLIC PROC[fi: Dir.FileInfo, bcdCreate: LONG CARDINAL] RETURNS[bcdDepSeq: Dir.DepSeq] = {
adeprecord: Dir.ADepRecord ← [];

	procMod: ProcBcds.ProcMod = {
	uns ← NIL;
	IF smodulename ~= NIL AND smodulename.length = 0 THEN RETURN;	-- garbage
	bcdDepSeq.bcdFileName ← bcdDepSeq.CopyString[fi.bcdFileName];
	bcdDepSeq.bcdVers ← bcdvers;
	bcdDepSeq.srcFileName ← bcdDepSeq.CopyString[sourcefile];
	bcdDepSeq.srcCreate ← sourcevers.time;
	bcdDepSeq.moduleName ← IF smodulename = NIL THEN NIL ELSE bcdDepSeq.CopyString[smodulename];
	bcdDepSeq.switches['a] ← FALSE;	-- always
	bcdDepSeq.switches['b] ← boundsChecks;
	bcdDepSeq.switches['c] ← cedarSwitch;
	bcdDepSeq.switches['j] ← crossJump;
	bcdDepSeq.switches['l] ← linksInCode;
	bcdDepSeq.switches['n] ← nilChecks;
	bcdDepSeq.switches['s] ← sortByUsage;
	bcdDepSeq.isdefns ← isdefns;
	bcdDepSeq.isconfig ← isconfig;
	bcdDepSeq.istablecompiled ← istablecompiled;
	bcdDepSeq.symbolSpace ← symbolSpace;
	-- since the symbolSpace may not have the cap filled in
	bcdDepSeq.symbolSpace.file ← fi.bcdCap;
	};

	-- filename will probably have ".bcd" at the end
	procDep: ProcBcds.ProcDep = {
	IF smodulename ~= NIL AND smodulename.length = 0 THEN RETURN;	-- garbage
	adeprecord ← [];
	adeprecord.moduleName ← IF smodulename = NIL THEN NIL ELSE bcdDepSeq.CopyString[smodulename];
	adeprecord.bcdFileName ← bcdDepSeq.CopyString[filename];
	adeprecord.bcdVers ← bcdvers;
	SELECT relcode FROM
	defstype => {
		adeprecord.relation ← directory;
		Dir.AddToDep[bcdDepSeq, @adeprecord];
		};
	imports =>  {
		adeprecord.relation ← imports;
		Dir.AddToDep[bcdDepSeq, @adeprecord];
		};
	exports =>  {
		adeprecord.relation ← exports;
		Dir.AddToDep[bcdDepSeq, @adeprecord];
		};
	ENDCASE => NULL;		-- ignore other stuff
	};

IF fi.bcdDepSeq ~= NIL THEN {
	-- not a hit DBStash.BumpNHits[];
	RETURN[fi.bcdDepSeq];
	};
{
-- others are defaulted
innardsobject: ProcBcds.InnardsObject ← [bcdheaderspace: Space.nullHandle];
success: BOOL;
bcdDepSeq ← NIL;
IF bcdCreate ~= 0 AND (fi.bcdDepSeq ← DBStash.Lookup[bcdCreate, fi.bcdFileName]) ~= NIL THEN 
	RETURN[fi.bcdDepSeq];
IF NOT fi.bcdPresent THEN GOTO err;
IF (fi.bcdDepSeq ← DBStash.Lookup[MDModel.GetBcdCreate[fi], fi.bcdFileName]) ~= NIL THEN 
	RETURN[fi.bcdDepSeq];
bcdDepSeq ← DBStash.GetTemporaryDepSeq[];
bcdDepSeq.fromsource ← FALSE;
-- cannot use loaded bcd since it may not be the same as the one on the disk
-- under the name fi.bcdFileName
-- IF fi.loadInfoSeq ~= NIL AND fi.loadInfoSeq.bcdbase ~= NIL THEN 
	-- innardsobject.bcd ← fi.loadInfoSeq.bcdbase
-- ELSE {
	MDModel.numberofbcdsmapped ← MDModel.numberofbcdsmapped + 1;
	innardsobject.cap ← fi.bcdCap;
	ProcBcds.ReadInSegmentsBcd[@innardsobject
		! ProcBcds.InvalidBcd => {
			CWF.WF1["Error - %s is not a valid bcd file.\n"L, fi.bcdFileName];
			GOTO out;
			}
		];
--	};
ProcBcds.InstallAddressesBcd[@innardsobject];
[success] ← ProcBcds.PrintDepends[@innardsobject, procMod, procDep, 
		FALSE, FALSE, TRUE, fi.bcdFileName];	-- less is TRUE
IF NOT success THEN {
	CWF.WF1["Error - couldn't analyze %s correctly.\n"L, fi.bcdFileName];
	Subr.errorflg ← TRUE;
	};
IF innardsobject.bcdheaderspace ~= Space.nullHandle THEN
	ProcBcds.UnstallBcd[@innardsobject];
bcdDepSeq ← DBStash.Insert[MDModel.GetBcdCreate[fi], fi.bcdFileName, bcdDepSeq];
fi.bcdDepSeq ← bcdDepSeq;
EXITS
err => CWF.WF1["Couldn't find %s.\n"L, fi.bcdFileName];
out => bcdDepSeq ← NIL;
}};

ValList: PROC[splist: MDModel.LISTSymbol] = {
nelem: CARDINAL ← 0;
WHILE splist ~= NIL DO
	nelem ← nelem + 1;
	IF nelem > 1000 THEN ERROR;	-- cycling
	splist ← splist.rest;
	ENDLOOP;
};

}.


[identical, modelchanged] ← CompareModelAndFile[mdepseq, fdepseq, 
		sptop, sploc, symbolseq, spmodel];
-- reorganize the Directory stmt into order for the quick check
-- not necessary if we've already analyzed the bcd unless there are 
-- extra, uneccesary parameters we want to remove
IF LongString.EquivalentString[sploc.sext, "mesa"L] 
AND (fdepseq.fromsource OR mdirs ~= fdirs) THEN
	ReorganizeDefinitions[symbolseq, sploc, fdepseq];
-- reorganize the imports and exports
-- claim: this is no longer necessary
-- but users like the xtra stuff deleted
ReorganizeImportsAndExports[symbolseq, sptop, fdepseq];
mdepseq ← longzone.NEW[Dir.DepSeqRecord[Dir.MaximumDepSize]];
mdepseq.CopyString ← Subr.CopyString;
MakeSymDesc[sp, mdepseq, symbolseq, spmodel, makethismodel, ttywindow];
IF mdepseq.bcdFileName = NIL THEN GOTO leave;
MakeSymDesc: PROC[sp: MDModel.Symbol, depseq: Dir.DepSeq,  
	symbolseq: MDModel.SymbolSeq,
	spmodel: MDModel.MODELSymbol, makethismodel: BOOL, 
	ttywindow: Subr.TTYProcs] ={
stemp: STRING ← [100];
adeprecord: Dir.ADepRecord;

	ProcAddName: PROC[sp: MDModel.Symbol] = {
	WITH spt~~sp SELECT FROM
	typeTYPE => {
		IF spt.typesym = NIL THEN RETURN;
		adeprecord ← MakeADepRecord[MDModel.NarrowToTYPE[sp], depseq,
			spmodel, makethismodel, ttywindow, symbolseq];
		adeprecord.relation ← directory;
		Dir.AddToDep[depseq, @adeprecord];
		};
	typeAPPL => {
		IF spt.applsym = NIL THEN RETURN;
		adeprecord ← MakeADepRecord[
			MDModel.NarrowToTYPE[spt.appltype], depseq,
			spmodel, makethismodel, ttywindow, symbolseq];
		adeprecord.relation ← imports;
		Dir.AddToDep[depseq, @adeprecord];
		};
	typeSTRING => {
		depseq.switches ← MDModel.FoldInParms[spt.strval];
		};
	typeLET => NULL;	-- this is an error, but ...
	ENDCASE => ERROR;
	};

IF sp = NIL THEN CWF.WF0["Error - sp is nil.\n"L];
-- set default compiler switches
depseq.switches ← MDModel.FoldInParms[NIL];
WITH spt~~sp SELECT FROM
typeTYPE => {
	spval: MDModel.LOCSymbol;
	CWF.SWF1[stemp, "%s.bcd"L, spt.typesym];
	depseq.bcdFileName ← depseq.CopyString[stemp];
	spval ← MDModel.LocForType[MDModel.NarrowToTYPE[sp]];
	IF spval ~= NIL THEN MDModel.TraverseList[spval.parmlist,ProcAddName];
	};
typeAPPL => {
	spval: MDModel.LOCSymbol;
	-- first, enter its export(s)
	-- don't bother if sp is of type LIST, etc.
	IF sp.stype NOT IN MDModel.HasAStringName THEN RETURN;
	IF MDModel.Sym[spt.appltype] ~= NIL AND
	NOT LongString.EquivalentString[MDModel.Sym[spt.appltype], "CONTROL"L]
		THEN {
		adeprecord ← MakeADepRecord[MDModel.NarrowToTYPE[spt.appltype], depseq,
			spmodel, makethismodel, ttywindow, symbolseq];
		adeprecord.relation ← exports;
		Dir.AddToDep[depseq, @adeprecord];
		};
	-- has no value?
	IF spt.applval = NIL THEN RETURN;
	-- now run down its parm list
	-- if its a LIST (actually a plus LIST)then its already 
	-- been looked at
	IF spt.applval.stype = typeLIST THEN RETURN;
	spval ← MDModel.NarrowToLOC[spt.applval];
	CWF.SWF1[stemp, "%s.bcd"L, spval.tail];
	depseq.bcdFileName ← depseq.CopyString[stemp];
	MDModel.TraverseList[spval.parmlist,ProcAddName];
	};
typeLET => {
	spval: MDModel.LOCSymbol;
	splist: MDModel.LISTSymbol;
	spappl: MDModel.APPLSymbol;
	sptype: MDModel.TYPESymbol;
	-- first add exports
	splist ← spt.letgrp;
	WHILE splist ~= NIL DO
		IF splist.first.stype ~= typeAPPL THEN {
			splist ← splist.rest;
			LOOP;
			};
		spappl ← MDModel.NarrowToAPPL[splist.first];
		sptype ← MDModel.NarrowToTYPE[spappl.appltype];
		IF sptype.typesym ~= NIL AND
		NOT LongString.EquivalentString[sptype.typesym, "CONTROL"L]
		THEN {
			adeprecord ← MakeADepRecord[sptype, depseq,
				spmodel, makethismodel, ttywindow, symbolseq];
			adeprecord.relation ← exports;
			Dir.AddToDep[depseq, @adeprecord];
			};
		splist ← splist.rest;
		ENDLOOP;
	-- has no value?
	IF spt.letval = NIL THEN RETURN;
	-- now run down its parm list
	-- if its a LIST (actually a plus LIST)then its already 
	-- been looked at
	IF spt.letval.stype = typeLIST THEN RETURN;
	spval ← MDModel.NarrowToLOC[spt.letval];
	CWF.SWF1[stemp, "%s.bcd"L, spval.tail];
	depseq.bcdFileName ← depseq.CopyString[stemp];
	MDModel.TraverseList[spval.parmlist,ProcAddName];
	};
ENDCASE => ERROR;
};

-- return the string name of the module you import
-- sp is of type TYPE, APPL, or LET
NumOfDirs: PROC[pd: Dir.DepSeq] RETURNS[numdirs: CARDINAL] = {
numdirs ← 0;
FOR i: CARDINAL IN [0 .. pd.size) DO
	IF pd[i].relation = directory THEN 
		numdirs ← numdirs + 1;
	ENDLOOP;
};

-- this procedure may change the model by adding parameters
CompareModelAndFile: PROC[mdepseq, fdepseq: Dir.DepSeq, 
	sptop: MDModel.Symbol, sploc: MDModel.LOCSymbol, 
	symbolseq: MDModel.SymbolSeq, spmodel: MDModel.MODELSymbol, typeScript: TypeScript.TS]
	RETURNS[identical, modelchanged: BOOL] = {
defshints: ARRAY[0 .. Dir.MaximumDepSize) OF MDModel.TYPESymbol ← ALL[NIL];
modelnoparms, found: BOOL;
skipimports, skiptypes: BOOL;
fi: Dir.FileInfo;
fakesploc: MDModel.LOCSymbol;
dep: Dir.DepSeq;

	LookFor: PROC[mod: LONG STRING, bcdVers: TimeStamp.Stamp] 
		RETURNS[sptype: MDModel.TYPESymbol] = {
	FOR i: CARDINAL IN [0 .. fdepseq.size) DO
		IF fdepseq[i].relation ~= directory THEN LOOP;
		IF (bcdVers ~= TimeStamp.Null AND fdepseq[i].bcdVers = bcdVers) 
		OR LongString.EqualString[mod, fdepseq[i].moduleName] THEN 
			RETURN[defshints[i]];
		ENDLOOP;
	RETURN[NIL];
	};
	
MDModel.CkType[sploc, typeLOC];
-- this handles the case of a module with string parameters
modelnoparms ← (mdepseq.size = 0) AND (sploc.parmlist = NIL);
identical ← TRUE;
modelchanged ← FALSE;
fi ← MDModel.GetFileInfo[sploc];
skiptypes ← fi.isBcd;
-- first we do a little magic to make sure that the fdepseq is filled
-- with modulenames
fakesploc ← MDModel.NewSymLOC[symbolseq];
FOR j: CARDINAL IN [0..fdepseq.size) DO
	IF fdepseq[j].moduleName ~= NIL OR fdepseq[j].relation ~= directory THEN LOOP;
	IF fdepseq[j].bcdVers ~= TimeStamp.Null 
	AND (dep ← DBStash.BcdVersLookup[fdepseq[j].bcdVers]) ~= NIL THEN
		fdepseq[j].moduleName ← fdepseq.CopyString[dep.moduleName]
	ELSE {
		-- this is a very slow way to figure out modulenames
		fakesploc.tail ← Subr.CopyString[fdepseq[j].bcdFileName];
		fakesploc.tail.length ← fakesploc.tail.length - 4;
		fakesploc.sext ← Subr.CopyString["Bcd"L];
		MDComp.SetVersAndModulename[fakesploc];
		IF fakesploc.fi.moduleName ~= NIL THEN 
			fdepseq[j].moduleName ← fdepseq.CopyString[fakesploc.fi.moduleName];
		MDModel.FreeStringsOf[fakesploc];
		};
	ENDLOOP;
fakesploc.tail ← NIL;
fakesploc.sext ← NIL;
FOR reltype: Dir.ModType DECREASING IN [imports .. directory] DO
	-- check the file against the model
	-- add parameters as necessary to the model
	IF skiptypes AND reltype = directory THEN LOOP;
	FOR j: CARDINAL IN [0..fdepseq.size) DO
		IF fdepseq[j].relation ~= reltype THEN LOOP;
		IF TypeScript.UserAbort[typeScript] THEN SIGNAL Subr.AbortMyself;
		found ← FALSE;
		FOR i: CARDINAL IN [0.. mdepseq.size) DO
			IF mdepseq[i].relation ~= reltype THEN LOOP;
			IF mdepseq[i].moduleName = NIL THEN LOOP;
			IF LongString.EqualString[mdepseq[i].moduleName,
				fdepseq[j].moduleName] THEN {
					found ← TRUE;
					EXIT;
					};
			ENDLOOP;
		IF NOT found THEN {
			sptype: MDModel.TYPESymbol ← NIL;
			IF Subr.debugflg THEN
				CWF.WF2["\n\tIn file, but not model: %s, relation %s."L,
					fdepseq[j].bcdFileName,
					SELECT reltype FROM
					imports => "Imports"L,
					exports => "Exports"L,
					directory => "Directory"L,
					ENDCASE => ERROR];
			identical ← FALSE;
			modelchanged ← TRUE;
			IF reltype ~= directory THEN
				sptype ← LookFor[fdepseq[j].moduleName, fdepseq[j].bcdVers];
			-- now add it to the model
			-- this IF test is temporary
			-- all it does is avoid problems
			-- with Defs files with IMPORTS
			skipimports ← fdepseq.isdefns;
			-- this skips imports for FRAMEPTRTYPES
			IF sptop.stype = typeTYPE 
			AND MDModel.NarrowToTYPE[sptop].frameptr THEN
				skipimports ← TRUE;
			IF NOT (reltype=imports AND skipimports) THEN
				defshints[j] ← AddToModel[symbolseq, sptop, sploc,
					fdepseq[j].moduleName, fdepseq[j].bcdFileName, 
					fdepseq[j].bcdVers, reltype, spmodel, sptype];
			};
		ENDLOOP;
	ENDLOOP;
IF modelnoparms AND modelchanged THEN  -- must be defaultable
	sploc.parmsdefaultable ← TRUE;
-- questionable
IF identical THEN
	sploc.parmsdefaultable ← TRUE;
};


ReorganizeDefinitions: PROC[symbolseq: MDModel.SymbolSeq,
	sploc: MDModel.LOCSymbol, fdepseq: Dir.DepSeq] = {
splist: MDModel.LISTSymbol;
sptype: MDModel.TYPESymbol;
sptypeloc: MDModel.LOCSymbol;
spp: MDModel.Symbol;
newlist: MDModel.LISTSymbol ← NIL;

	IsBad: PROC[sp: MDModel.Symbol] RETURNS[remove: BOOL] = {
	IF sp.stype = typeTYPE THEN RETURN[TRUE];
	IF sp.stype = typeAPPL THEN {
		sptype: MDModel.TYPESymbol = MDModel.NarrowToTYPE[
			MDModel.NarrowToAPPL[sp].appltype];
		-- remove if type of appl is not on newlist
		RETURN[NOT MDModel.IsOnList[sptype, newlist]];
		};
	RETURN[FALSE];
	};
	
-- for each defs file, delete it and append to end this puts it into
-- order for the binder
FOR i: CARDINAL IN [0 .. fdepseq.size) DO
	IF fdepseq[i].relation ~= directory THEN LOOP;
	splist ← sploc.parmlist;
	WHILE splist ~= NIL DO
		IF splist.first.stype ~= typeTYPE THEN {
			splist ← splist.rest;
			LOOP;
			};
		sptype ← MDModel.NarrowToTYPE[splist.first];
		sptypeloc ← MDModel.LocForType[sptype];
		IF sptypeloc = NIL THEN {
			splist ← splist.rest;
			LOOP;
			};
		-- fi ← MDModel.GetFileInfo[sptypeloc];
		-- IF fi.moduleName = NIL THEN {
			-- fdepseq: Dir.DepSeq;
			-- may be nil if this parameter was added by AddToModel
			-- [fdepseq, ] ← MakeDepSeqForFile[symbolseq, sptypeloc, NIL, 
				-- FALSE, NIL];
			-- IF fdepseq = NIL THEN 
				-- SetModuleName[fi, sptype.typesym];  make up name;
			-- };
		IF LongString.EquivalentString[sptype.typeName, 
		fdepseq[i].moduleName] THEN{
			[spp, sploc.parmlist] ← MDModel.RemoveFromList[sptype, 
				sploc.parmlist];
			newlist ← MDModel.AddToEndOfList[newlist, sptype, 
				normal, symbolseq];
			-- should free spp
			EXIT;	-- the inner loop
			};
		splist ← splist.rest;
		ENDLOOP;
	ENDLOOP;
-- now remove any definitions that aren't actually in the file
-- and any APPL's that depend on this
sploc.parmlist ← MDModel.TraverseAndRemove[sploc.parmlist, IsBad];
-- now add reorganized definitions
IF sploc.parmlist ~= NIL THEN
	sploc.parmlist ← MDModel.NarrowToLIST[MDModel.MergeIntoList[
		newlist, sploc.parmlist, symbolseq, normal]]
ELSE sploc.parmlist ← newlist;
};

ReorganizeImportsAndExports: PROC[symbolseq: MDModel.SymbolSeq,
	sptop1: MDModel.Symbol, fdepseq: Dir.DepSeq] = {
sploc: MDModel.LOCSymbol;
splist: MDModel.LISTSymbol;
spappl: MDModel.APPLSymbol;
sptop: MDModel.LETSymbol;
spp: MDModel.Symbol;
sptype: MDModel.TYPESymbol;
newlist: MDModel.LISTSymbol ← NIL;

	IsAppl: PROC[sp: MDModel.Symbol] RETURNS[remove: BOOL] = {
	spappl: MDModel.APPLSymbol;
	IF sp.stype ~= typeAPPL THEN RETURN[FALSE];
	spappl ← MDModel.NarrowToAPPL[sp];
	-- remove it if it is NOT a frame pointer variable
	-- and if it is not a CONTROL module entry
	RETURN[spappl.appltype ~= symbolseq.controlv
		AND NOT MDModel.NarrowToTYPE[spappl.appltype].frameptr];
	};
	
-- IMPORTS
-- for each imports, delete it and append to end this puts it into
-- order for the binder
spp ← MDModel.LocForSp[sptop1];
IF spp = NIL THEN ERROR;
sploc ← MDModel.NarrowToLOC[spp];
FOR i: CARDINAL IN [0 .. fdepseq.size) DO
	IF fdepseq[i].relation ~= imports THEN LOOP;
	splist ← sploc.parmlist;
	WHILE splist ~= NIL DO
		IF splist.first.stype ~= typeAPPL THEN {
			splist ← splist.rest;
			LOOP;
			};
		spappl ← MDModel.NarrowToAPPL[splist.first];
		sptype ← MDModel.NarrowToTYPE[spappl.appltype];
		-- need to look at the typename since the moduleName
		-- in the fdepseq is an interface name you import or export
		IF LongString.EquivalentString[sptype.typeName, fdepseq[i].moduleName] THEN{
			[spp, sploc.parmlist] ← MDModel.RemoveFromList[spappl, 
				sploc.parmlist];
			newlist ← MDModel.AddToEndOfList[newlist, spappl, 
				normal, symbolseq];
			-- should free spp
			EXIT;	-- the inner loop
			};
		splist ← splist.rest;
		ENDLOOP;
	ENDLOOP;
-- now remove any imports that aren't actually in the file
sploc.parmlist ← MDModel.TraverseAndRemove[sploc.parmlist, IsAppl];
-- now add reorganized imports
IF newlist ~= NIL THEN
	sploc.parmlist ← MDModel.NarrowToLIST[MDModel.MergeIntoList[
		sploc.parmlist, newlist, symbolseq, normal]];

-- now for multiple exports
IF sptop1.stype ~= typeLET THEN RETURN;
sptop ← MDModel.NarrowToLET[sptop1];
newlist ← NIL;
-- for each exports, delete it and append to end this puts it into
-- order for the binder
FOR i: CARDINAL IN [0 .. fdepseq.size) DO
	IF fdepseq[i].relation ~= exports THEN LOOP;
	splist ← sptop.letgrp;
	WHILE splist ~= NIL DO
		IF splist.first.stype ~= typeAPPL THEN {
			splist ← splist.rest;
			LOOP;
			};
		spappl ← MDModel.NarrowToAPPL[splist.first];
		sptype ← MDModel.NarrowToTYPE[spappl.appltype];
		-- need to look at the typename since the moduleName
		-- in the fdepseq is an interface name you import 
		-- or export
		IF LongString.EquivalentString[sptype.typeName,fdepseq[i].moduleName] THEN {
			[spp, sptop.letgrp] ← 
				MDModel.RemoveFromList[splist.first, 
					sptop.letgrp];
			newlist ← MDModel.AddToEndOfList[newlist, splist.first, 
				normal, symbolseq];
			-- should free spp
			EXIT;	-- the inner loop
			};
		splist ← splist.rest;
		ENDLOOP;
	ENDLOOP;
-- now remove any exports that aren't actually in the file
sptop.letgrp ← MDModel.TraverseAndRemove[sptop.letgrp, IsAppl];
IF newlist ~= NIL THEN
	sptop.letgrp ← MDModel.NarrowToLIST[MDModel.MergeIntoList[
		sptop.letgrp, newlist, symbolseq, normal]];
};

-- looks for sname explicitely on the parm list
-- sname is the moduleName, w/o .bcd or .mesa
LookSym: PROC[sname: LONG STRING, sp: MDModel.Symbol] RETURNS[MDModel.Symbol] = {
son: MDModel.Symbol ← NIL;

	ProcAnal: PROC[sp: MDModel.Symbol] = {
	son ← IF son ~= NIL THEN son ELSE LookSym[sname, sp];
	};

IF sp = NIL THEN RETURN[NIL];
WITH spt~~sp SELECT FROM
typeLOC => {
	IF LongString.EquivalentString[spt.tail,sname] THEN RETURN[sp];
	-- CWF.WF1["looksym %s\n", sp.tail];
	};
typeLIST => NULL;
ENDCASE => RETURN[NIL];
MDModel.TraverseList[MDModel.NarrowToLIST[sp], ProcAnal];
RETURN[son];
};

-- this fills in parameter defaults if necessary
CompareDesc: PROC[fdepseq: Dir.DepSeq, 
	sptop: MDModel.Symbol, sploc: MDModel.LOCSymbol, 
	symbolseq: MDModel.SymbolSeq, spmodel: MDModel.MODELSymbol] 
	RETURNS[modelchanged: BOOL] = {
identical: BOOL;
modelchanged ← FALSE;
IF Subr.debugflg THEN
	CWF.WF2["ModuleName %s, File %s"L,fdepseq.moduleName,
		fdepseq.srcFileName];
[identical, modelchanged] ← MatchUpAndAddParameters[fdepseq, 
		sptop, sploc, symbolseq, spmodel];
};

-- sptype may be NIL
-- spaddtype may be NIL
-- sptop is either an APPL, TYPE, or LET
AddToModel: PUBLIC PROC[symbolseq: MDModel.SymbolSeq, sptop: MDModel.Symbol, 
	sploc: MDModel.LOCSymbol, moduleName, bcdFileName: LONG STRING, 
	bcdVers: TimeStamp.Stamp, reltype: Dir.ModType, spmodel: MDModel.MODELSymbol,
	sptype: MDModel.TYPESymbol] RETURNS[spaddtype: MDModel.TYPESymbol] = {
modName: STRING ← [100];
spaddtype ← NIL;
IF bcdFileName = NIL THEN RETURN;
IF moduleName ~= NIL THEN Subr.strcpy[modName, moduleName];
IF reltype = directory THEN {  
	-- yes, add this to end of proc's procval list
	[spaddtype,] ← MDModel.EnterType[bcdFileName, modName, bcdVers,
		symbolseq, spmodel, TRUE
			! MDModel.NeedModuleName => {
				CWF.FWF1[MDMain.DebugWP, "Making up modulename for %s.\n"L, bcdFileName];
				Subr.strcpy[modName, bcdFileName];
				IF Subr.EndsIn[modName, ".bcd"L] THEN
					modName.length ← modName.length - 4;
				RETRY;
				}];
	sploc.parmlist ← MDModel.AddToEndOfList[sploc.parmlist, 
		spaddtype, normal,symbolseq]
	}
ELSE {
	-- yes, add this to end of proc's procval list
	spi: MDModel.APPLSymbol = MDModel.EnterInstAndLoc[
		bcdFileName, modName, bcdVers, symbolseq, spmodel, sptype, TRUE
			! MDModel.NeedModuleName => {
				CWF.FWF1[MDMain.DebugWP, "Making up modulename for %s.\n"L, bcdFileName];
				Subr.strcpy[modName, bcdFileName];
				IF Subr.EndsIn[modName, ".bcd"L] THEN
					modName.length ← modName.length - 4;
				RETRY;
				}];
	IF reltype = imports THEN {
		sploc.parmlist ← MDModel.AddToEndOfList[sploc.parmlist,
			spi, normal, symbolseq]
		}
	ELSE IF reltype = exports THEN {
		-- must massage into a LET if not already in it
		WITH spt~~sptop SELECT FROM
		typeLET => {
			spt.letgrp ← MDModel.AddToEndOfList[spt.letgrp,
				spi, normal, symbolseq];
			};
		typeAPPL => {
			spnew: MDModel.LETSymbol = MDModel.NewSymLET[symbolseq];
			spnew.letval ← spt.applval;
			spt.applval ← NIL;
			MDModel.ReplaceBy[sptop, spnew, symbolseq];
			spnew.letgrp ← MDModel.AddToEndOfList[NIL, sptop, normal,
				symbolseq];
			spnew.letgrp ← MDModel.AddToEndOfList[spnew.letgrp, 
				spi, normal, symbolseq];
			spt.letparent ← spnew;
			spi.letparent ← spnew;
			};
		-- exports are ok if the TYPE is a frame ptr type
		typeTYPE => IF NOT spt.frameptr THEN 
			CWF.WF3["Error - %s.%s is a TYPE (not a FRAMEPTRTYPE) but it exports %s!\n"L, 
				sploc.tail, sploc.sext, bcdFileName];
		ENDCASE => ERROR;
		}
	ELSE ERROR;
	};
};