-- DesignModelImpl.mesa
-- last edit by Schmidt, January 6, 1983 2:27 pm
-- last edit by Satterthwaite, February 1, 1983 10:12 am

-- construct Models from Bcds
-- Usage:
--   DesignModel [/d] [/h host] [/p path] [/m modelfile] [/w] file1.bcd ... filen.bcd
--	
--	that is, list the bcd's that are part one after the other.
--	The Bcd's are Implementor modules
--
--	/d turn on debugging switch
-- 	/h host	set host name for files,   e.g. "Ivy"
--	/m modelfile	set filename for output model (default = "NewModel.Model")
--	/p path	set path name for files,   e.g. "Schmidt>Model"
--	/w	use defaults in generated model
--

DIRECTORY
  CWF: TYPE USING [SetWriteProcedure, SWF1, SWF3, SWF4, WF0, WF1, WF2, WF3],
  DesModSup: TYPE USING [EnterType, EnterInstAndLoc, FixupExterior,
  		MoveTypesToFront, NeedModuleName, ProcessForStandardOpen,
  		ReorganizeInOrder, SortListOfSymbols],
  DFSubr: TYPE USING [AllocateDFSeq, DF, DFSeq, FreeDFSeq, LookupDF, NextDF, StripLongName],
  Dir: TYPE USING [FileInfo],
  Directory: TYPE USING [Error, GetProps, Handle, ignore, Lookup],
  File: TYPE USING [Capability],
  IO: TYPE USING[Handle, PutChar],
  LongString: TYPE USING [EquivalentString],
  MDModel: TYPE USING [AddToEndOfList, AllocateSymbolSeq, APPLSymbol, CkType, 
  	FreeStringsOf, FreeSymbolSeq, GenerateUniqueName, GetFileInfo, 
	LETSymbol, LISTSymbol, ListType, LOCSymbol, MergeIntoList, 
	MODELSymbol, NarrowToAPPL, NarrowToLET, NarrowToLIST, NarrowToLOC, 
	NarrowToPROC, NarrowToTYPE, NewSymAPPL, NewSymLET, 
	NewSymLOC, NewSymMODEL, NewSymSTRING, NewSymTYPE, 
	PROCSymbol, RemoveFromList, StartMDSupport, StopMDSupport, 
	STRINGSymbol, Sym, Symbol, SymbolSeq, 
	TraverseList, TraverseTree, traversetreecalled, TYPESymbol],
  MDUtil: TYPE USING [AnyR, MakeConfig, PrintNewModelStream, SupportInit],
  ProcBcds: TYPE USING [GetModuleName, Innards, InnardsObject, InstallAddressesBcd, 
  	PrintDepends, ProcDep, ProcMod, ReadInSegmentsBcd, UnstallBcd],
  Rope: TYPE USING[Cat, Fetch, Text],
  RopeInline: TYPE USING[InlineFlatten],
  Space: TYPE USING [Handle, nullHandle],
  Stream: TYPE USING [Delete, Handle],
  String: TYPE USING [AppendString],
  Subr: TYPE USING [AbortMyself, Any, CopyString, debugflg, EndsIn, 
  	errorflg, GetCreateDate, GetRemoteFilenameProp, NewStream, 
	numberofleaders, PackedTime, PrintGreeting, strcpy, SubrInit, SubrStop, Write],
  Time: TYPE USING [Current],
  TimeStamp: TYPE USING[Stamp],
  UECP: TYPE USING[Argv, Parse],
  UserExec: TYPE USING[CommandProc, ExecHandle, GetStreams, RegisterCommand, UserAbort];

DesignModelImpl: PROGRAM
IMPORTS CWF, DesModSup, DFSubr, Directory, IO, LongString,
	MDModel, MDUtil, ProcBcds, RopeInline, Space, Stream, String, Subr,
	Time, UECP, UserExec = {

maxfiles: CARDINAL = 500;		-- the maximum number of files

maxsym: CARDINAL = 5000;		-- the number of symbols

-- MDS USAGE !!!
output: IO.Handle;
-- end of MDS

PutProc: PROC[ch: CHAR] = {
output.PutChar[ch];
};

Main: UserExec.CommandProc  = TRUSTED {
commandline: Rope.Text;
out: IO.Handle;
symbolseq: MDModel.SymbolSeq ← NIL;
sh: Stream.Handle;
dfseq: DFSubr.DFSeq ← NIL;
changes: BOOL;
dontdefault: BOOL ← TRUE;
host: STRING ← [30];
path: STRING ← [100];
time: Subr.PackedTime;
spm: MDModel.PROCSymbol;
modelfile: STRING ← [100];
configfile: STRING ← [100];
procname: STRING ← [100];
n: CARDINAL;
nimpls, parm: CARDINAL;
token: Rope.Text;
argv: UECP.Argv ← UECP.Parse[event.commandLine];

	Cleanup: PROC = {
	MDModel.StopMDSupport[];
	MDModel.FreeSymbolSeq[@symbolseq];
	DFSubr.FreeDFSeq[@dfseq];
	Subr.SubrStop[];
	};

{
	ENABLE {
	Subr.AbortMyself => {
		CWF.WF0["\nDesignModel Aborted.\n"L];
		GOTO leave;
		};
	UNWIND => Cleanup[];
	};

output ← exec.GetStreams[].out;
Subr.SubrInit[256];
Subr.errorflg ← Subr.debugflg ← FALSE;
time ← Time.Current[];
Subr.PrintGreeting["DesignModel"L];

symbolseq ← MDModel.AllocateSymbolSeq[maxsym];
symbolseq.controlv ← MDModel.NewSymTYPE[symbolseq];
symbolseq.controlv.typesym ← Subr.CopyString["CONTROL"L];
symbolseq.controlv.typeName ← Subr.CopyString["CONTROL"L];
symbolseq.toploc ← MDModel.NewSymLOC[symbolseq];
symbolseq.toploc.nestedmodel ← MDModel.NewSymMODEL[symbolseq];
MDUtil.SupportInit[symbolseq, NARROW[exec.viewer], exec.GetStreams[].out];
MDModel.StartMDSupport[];
MDModel.traversetreecalled ← 0;
Subr.numberofleaders ← 0;

dfseq ← DFSubr.AllocateDFSeq[maxEntries: maxfiles, zoneType: shared];

Subr.strcpy[modelfile, "NewModel.Model"L];
parm ← 1;
WHILE parm < argv.argc DO
	token ← RopeInline.InlineFlatten[argv[parm]];
	IF token.Fetch[0] = '/ OR token.Fetch[0] = '- THEN { 
		SELECT token.Fetch[1] FROM
		'd => Subr.debugflg ← TRUE;
		'h => {
			token ← RopeInline.InlineFlatten[argv[parm]];
			parm ← parm + 1;
			Subr.strcpy[host, LOOPHOLE[token]];
			};
		'm => {
			token ← RopeInline.InlineFlatten[argv[parm]];
			parm ← parm + 1;
			Subr.strcpy[modelfile, LOOPHOLE[token]];
			};
		'p => {
			token ← RopeInline.InlineFlatten[argv[parm]];
			parm ← parm + 1;
			Subr.strcpy[path, LOOPHOLE[token]];
			};
		'w => dontdefault ← FALSE;
		ENDCASE => CWF.WF1["Unknown option '%s'\n"L, LOOPHOLE[token]];
		}
	ELSE {
		df: DFSubr.DF;
		IF NOT MDUtil.AnyR[token, '.] THEN 
			token ← RopeInline.InlineFlatten[Rope.Cat[token, ".Bcd"]];
		df ← DFSubr.NextDF[dfseq];
		IF df = NIL THEN ERROR;
		df.shortname ← Subr.CopyString[LOOPHOLE[token], dfseq.dfzone];
		};
	parm ← parm + 1;
	ENDLOOP;
IF dfseq.size = 0 THEN {
	CWF.WF0["Error - no arguments given to DesignModel.\n"L];
	SIGNAL Subr.AbortMyself;
	};
IF NOT Subr.Any[modelfile, '.] THEN String.AppendString[modelfile, ".Model"L];
symbolseq.toploc.nestedmodel.modelfilename ← Subr.CopyString[modelfile];
nimpls ← dfseq.size;
changes ← TRUE;
WHILE changes DO
	changes ← FALSE;
	FOR i: CARDINAL IN [0.. dfseq.size) DO
		IF NOT dfseq[i].eval THEN {
			AddBcd[@dfseq[i], symbolseq, dfseq,
				host, path, (i < nimpls), exec];
			changes ← TRUE;
			dfseq[i].eval ← TRUE;
			};
		ENDLOOP;
	ENDLOOP;
-- at this point symbolseq.toploc.nestedmodel.model
-- is a list of all the Types and APPLs and LEts
-- this takes the TYPEs and LETs, puts a Model: in front of
-- them, and puts TYPEs and APPLs in parameter list
Subr.strcpy[configfile, modelfile];
configfile.length ← configfile.length - 6;
CWF.SWF1[procname, "MODEL%s"L, configfile];
CWF.WF0["Fix up exterior.\n"L];
DesModSup.FixupExterior[symbolseq, procname];

-- symbolseq.toploc.nestedmodel.model is a list of one element, which is a PROC
-- now reorganize the PROC body in sorted order
CWF.WF0["Reorganize procval in order.  "L];
spm ← MDModel.NarrowToPROC[symbolseq.toploc.nestedmodel.model.first];
spm.procval ← DesModSup.ReorganizeInOrder[symbolseq, MDModel.NarrowToLIST[spm.procval], exec];
-- now move all the PLUS's and THEN's to the bottom (for the Binder)
spm.procval ← MoveAllPlus[MDModel.NarrowToLIST[spm.procval], symbolseq];

-- now reorganize the PROC parameter list in topological order
CWF.WF0["Sort parameters in order.  "L];
[spm.procparm, n] ← DesModSup.SortListOfSymbols[symbolseq, spm.procparm];
CWF.WF1["(%u symbols sorted.)\n"L, @n];

CWF.WF0["Figure out exports.  "L];
spm.procret ← FigureOutExports[symbolseq];
[spm.procret, n] ← DesModSup.SortListOfSymbols[symbolseq, spm.procret];
CWF.WF1["(%u symbols sorted.)\n"L, @n];

-- now move the types in the parameter list to before the Model: PROC
CWF.WF0["Moving types to front.\n"L];
DesModSup.MoveTypesToFront[symbolseq];

-- at this point symbolseq.toploc.nestedmodel.model is a LIST of many types, ended by
-- a single PROC; now reorganize those TYPES
CWF.WF0["Reorganizing those types in order.  "L];
symbolseq.toploc.nestedmodel.model ← 
	DesModSup.ReorganizeInOrder[symbolseq, 
		symbolseq.toploc.nestedmodel.model, exec];

CWF.WF0["Process for standard open.\n"L];
-- now run through the list and strip off standard Mesa TYPES, 
-- supplying an @OPEN instead
DesModSup.ProcessForStandardOpen[symbolseq];


-- MDModel.ValidateModel[symbolseq];

-- print out the model
commandline ← RopeInline.InlineFlatten[Rope.Cat["DesignModel ", event.commandLine]];
out ← exec.GetStreams[].out;
CWF.WF1["The new model is in the file '%s'\n\n"L, modelfile];
MDUtil.PrintNewModelStream[symbolseq, symbolseq.toploc.nestedmodel,
	NIL, commandline, dontdefault, NARROW[exec.viewer], out];
sh ← Subr.NewStream[modelfile, Subr.Write];
MDUtil.PrintNewModelStream[symbolseq, symbolseq.toploc.nestedmodel,
		sh, commandline, dontdefault, NARROW[exec.viewer], out];
Stream.Delete[sh];
modelfile.length ← modelfile.length - 5;
CWF.SWF1[configfile, "MODEL%sConfig"L, modelfile];
CWF.WF1["\n\n\nThe New Config File Is '%s'\n\n"L, configfile];
-- prints on the terminal
MDUtil.MakeConfig[symbolseq.toploc.nestedmodel, symbolseq, NIL, 0, out, NIL];
sh ← Subr.NewStream[configfile, Subr.Write];
MDUtil.MakeConfig[symbolseq.toploc.nestedmodel, symbolseq, sh, 0, out, NIL];
Stream.Delete[sh];
EXITS
leave => NULL;
};
CWF.WF3["\nSymbolseq.size %u, leaders: %u, traversetrees: %u.\n"L, 
	@symbolseq.size, @Subr.numberofleaders, @MDModel.traversetreecalled];
time ← Time.Current[] - time;
CWF.WF1["Total elapsed time for DesignModel %lr.\n"L,@time];
Cleanup[];
};


-- take dftop.shortname, add it to the model in symbolseq
-- if usersaysimpl, then the user put this one on the command line
AddBcd: PROC[dftop: DFSubr.DF, symbolseq: MDModel.SymbolSeq, 
	dfseq: DFSubr.DFSeq, defhost, defpath: STRING, usersaysimpl: BOOL,
	exec: UserExec.ExecHandle] = {
spimpl: MDModel.Symbol;	-- is either TYPE or LET
spimplloc: MDModel.LOCSymbol;
mainprog: STRING ← [100];
innardsobject: ProcBcds.InnardsObject ← [bcdheaderspace: Space.nullHandle];
tail: STRING ← [100];
sext: STRING ← [20];

	procMod: ProcBcds.ProcMod = {
	fi: Dir.FileInfo;
	sptype: MDModel.TYPESymbol;
	isc: BOOL ← TRUE;
	uns ← NIL;
	Subr.strcpy[mainprog, smodulename];
	IF sourcefile.length > 0 THEN {
		IF Subr.EndsIn[sourcefile, ".mesa"L] THEN {
			sourcefile.length ← sourcefile.length - 5;
			isc ← FALSE;
			}
		ELSE IF Subr.EndsIn[sourcefile, ".config"L] THEN {
			sourcefile.length ← sourcefile.length - 7;
			isc ← TRUE;
			}
		ELSE ERROR;
		-- in general prefer the capitalization of the modulename
		-- so we can default to a shorter form (:@)
		Subr.strcpy[tail, IF LongString.EquivalentString[sourcefile, 
			smodulename] THEN smodulename ELSE sourcefile];
		Subr.strcpy[sext, IF isc THEN "config"L ELSE "mesa"L];
		}
	ELSE {
		-- for modules which have no source recorded
		-- in them, e.g. TableCompiled
		Subr.strcpy[tail, smodulename];
		Subr.strcpy[sext, "bcd"L];
		};
	IF isdefns THEN {
		bcdfn: STRING ← [100];
		CWF.SWF1[bcdfn, "%s.Bcd"L, tail];
		sptype ← DesModSup.EnterType[bcdfn, smodulename, bcdvers,
			symbolseq, symbolseq.toploc.nestedmodel];
		spimplloc ← MDModel.NarrowToLOC[sptype.typeval];
		-- this resets everything
		MDModel.FreeStringsOf[spimplloc];
		spimplloc.host ← spimplloc.path ← spimplloc.tail ← 
			spimplloc.sext ← NIL;
		}
	ELSE IF NOT usersaysimpl THEN {
		-- must be a pointer to frame
		bcdfn: STRING ← [100];
		CWF.WF1["%s is used as a pointer to frame.\n"L, smodulename];
		CWF.SWF1[bcdfn, "%s.Bcd"L, tail];
		sptype ← DesModSup.EnterType[bcdfn, smodulename, bcdvers,
			symbolseq, symbolseq.toploc.nestedmodel];
		sptype.frameptr ← TRUE;
		-- this way the instance will become a parameter
		spimpl ← sptype;
		RETURN;
		}
	ELSE spimplloc ← MDModel.NewSymLOC[symbolseq];
	-- look at remote name property
	spimplloc.createtime ← sourcevers.time;
	FigureOutRemoteName[spimplloc, sourcefile];
	IF spimplloc.host = NIL AND defhost.length > 0 THEN
		spimplloc.host ← Subr.CopyString[defhost];
	IF spimplloc.path = NIL AND defpath.length > 0 THEN
		spimplloc.path ← Subr.CopyString[defpath];
	spimplloc.tail ← Subr.CopyString[tail];
	spimplloc.sext ← Subr.CopyString[sext];
	-- designmodel can only generate parameters that are defaultable
	spimplloc.parmsdefaultable ← TRUE;
	fi ← MDModel.GetFileInfo[spimplloc];
	fi.bcdVers ← bcdvers;
	fi.moduleName ← Subr.CopyString[smodulename];
	IF isdefns THEN {
		spimpl ← sptype;
		}
	ELSE {
		-- switches don't matter for defs files or config files
		spswitch: MDModel.Symbol;
		splet: MDModel.LETSymbol;
		IF NOT isc THEN {
			spswitch ← SwitchesToSymbol[altoCode, boundsChecks, cedarSwitch, crossJump, 
				linksInCode, nilChecks, sortByUsage, symbolseq];
			spimplloc.parmlist ← MDModel.AddToEndOfList[
				spimplloc.parmlist, spswitch, normal, symbolseq];
			};
		splet ← MDModel.NewSymLET[symbolseq];
		splet.letgrp ← NIL;
		splet.letval ← spimplloc;
		spimpl ← splet;
		IF dftop.isdefns THEN {
			-- oops this is a module that 
			-- is being used for a FRAMEPTR
			CWF.WF1["%s is used as a pointer to frame.\n"L, sourcefile];
			};
		};
	};

	-- spimpl, spimplloc are external
	-- spimpl is either type TYPE or LET
	procDep: ProcBcds.ProcDep = {
	moduleName: STRING ← [100];
	df: DFSubr.DF;
	IF relcode = otherdepends OR relcode = canignore 
	OR relcode = symbolsfile OR relcode = sourcefile THEN RETURN;
	-- if filename is length 0 then ignore
	-- may be result of table-compiled stuff
	IF filename.length = 0 THEN RETURN;
	-- add to DF table, for next iteration
	df ← DFSubr.LookupDF[dfseq, filename];
	IF df = NIL THEN {
		df ← DFSubr.NextDF[dfseq];
		df.shortname ← Subr.CopyString[filename, dfseq.dfzone];
		};
	IF spimpl.stype ~= typeTYPE THEN MDModel.CkType[spimpl, typeLET];
	MDModel.CkType[spimplloc, typeLOC];
	IF relcode = defstype THEN {  
		spaddtype: MDModel.TYPESymbol ← NIL;
		IF smodulename ~= NIL THEN Subr.strcpy[moduleName, smodulename];
		-- added to the end of the model
		spaddtype ← DesModSup.EnterType[filename, moduleName, 
			bcdvers, symbolseq, symbolseq.toploc.nestedmodel
			! DesModSup.NeedModuleName => IF SetModuleName[filename, moduleName] THEN RETRY];
		IF spaddtype ~= NIL THEN
			spimplloc.parmlist ← MDModel.AddToEndOfList[spimplloc.parmlist, 
				spaddtype, normal, symbolseq];
		df.isdefns ← TRUE;
		}
	ELSE {
		-- added to the end of the model at this point
		spi: MDModel.APPLSymbol;
	 	IF spimpl.stype ~= typeLET THEN {
			-- CWF.WF2["Warning: %s is a pointer to frame but has an export '%s'.\n"L,
			-- MDModel.Sym[spimpl], spi.applsym];
			-- ignore this import or export from a FRAMEPTRTYPE
			RETURN;
			};
		IF smodulename ~= NIL THEN Subr.strcpy[moduleName, smodulename];
		spi ← DesModSup.EnterInstAndLoc[filename, moduleName,
			bcdvers, symbolseq, symbolseq.toploc.nestedmodel, NIL 
			! DesModSup.NeedModuleName => IF SetModuleName[filename, moduleName] THEN RETRY];
		IF relcode = imports THEN
			spimplloc.parmlist ← 
				MDModel.AddToEndOfList[spimplloc.parmlist,
				spi, normal, symbolseq]
		ELSE IF relcode = exports THEN {
			splet: MDModel.LETSymbol ← MDModel.NarrowToLET[spimpl];
			IF spi.applval ~= NIL THEN {
				CWF.WF1["Warning: %s is defined by itself and also in a LET stmt.\n"L,
					spi.applsym];
				FixupAPPLtoLET[spi, splet, symbolseq];
				}
			ELSE IF spi.letparent ~= NIL 
				AND spi.letparent.letval ~= NIL THEN {
				CWF.WF1["Warning: %s is defined in two LET stmts.\n"L,
					spi.applsym];
				FixupLETtoLET[spi, splet, symbolseq];
				}
			ELSE {
				spi.letparent ← splet;
				splet.letgrp ← MDModel.AddToEndOfList[splet.letgrp,
					spi, normal, symbolseq];
				};
			}
		ELSE ERROR;
		};
	};

{
splet: MDModel.LETSymbol;
sploc: MDModel.LOCSymbol;
success: BOOL;
IF UserExec.UserAbort[exec] THEN SIGNAL Subr.AbortMyself[];
innardsobject.cap ← Directory.Lookup[fileName: dftop.shortname, 
	permissions: Directory.ignore ! Directory.Error => GOTO err];
[] ← Directory.GetProps[innardsobject.cap, mainprog
	! Directory.Error => {
		Subr.strcpy[mainprog, dftop.shortname];
		CONTINUE;
		}
	];
CWF.WF1["Analyzing %s.\n"L, mainprog];
ProcBcds.ReadInSegmentsBcd[@innardsobject];
ProcBcds.InstallAddressesBcd[@innardsobject];
IF usersaysimpl AND SpecialCase[mainprog, symbolseq, @innardsobject] THEN RETURN;
[success] ← ProcBcds.PrintDepends[@innardsobject, procMod, procDep, 
	FALSE, FALSE, TRUE, mainprog];	-- less is true
ProcBcds.UnstallBcd[@innardsobject];
IF NOT success THEN {
	CWF.WF1["Error - couldn't analyze %s correctly.\n"L, mainprog];
	Subr.errorflg ← TRUE;
	};
-- at this point spimpl is either a TYPE or a LET
-- the type has already been added to the model
IF spimpl.stype = typeTYPE THEN {
	sptype: MDModel.TYPESymbol;
	sptype ← MDModel.NarrowToTYPE[spimpl];
	sploc ← MDModel.NarrowToLOC[sptype.typeval];
	-- doesnt work
	-- [sploc.parmlist] ← SortListOfSymbols[symbolseq, sploc.parmlist];
	RETURN;
	};
splet ← MDModel.NarrowToLET[spimpl];
MDModel.CkType[splet, typeLET];
IF splet.letgrp = NIL THEN {
	-- must be a CONTROL module since it exports nothing
	spappl: MDModel.APPLSymbol;
	spappl ← MDModel.NewSymAPPL[symbolseq];
	spappl.applsym ← Subr.CopyString[mainprog];
	spappl.appltype ← symbolseq.controlv;
	spappl.applval ← NIL;
	splet.letgrp ← MDModel.AddToEndOfList[splet.letgrp, 
		spappl, normal, symbolseq];
	spappl.letparent ← splet;
	};
-- reorganize the parameter list into topological order
sploc ← MDModel.NarrowToLOC[splet.letval];
-- doesnt work
-- [sploc.parmlist] ← SortListOfSymbols[symbolseq, sploc.parmlist];
-- reorganize exports
-- doesnt work
-- [splet.letgrp] ← SortListOfSymbols[symbolseq, splet.letgrp];
spimpl ← SmashLETToAPPL[splet, symbolseq];
-- spimpl may now be an APPL
-- now add spimpl to symbolseq.toploc.nestedmodel.model if not already there
IF NOT usersaysimpl THEN 
	-- remove definition of an impl so it becomes a parameter
	[newlist: symbolseq.toploc.nestedmodel.model] ← 
		MDModel.RemoveFromList[spimpl, symbolseq.toploc.nestedmodel.model]
ELSE LookForMod[symbolseq, spimpl];
EXITS
err => 	CWF.WF2["Analyzing %s.  File %s is not on local disk.\n"L, 
		dftop.shortname, dftop.shortname];
};
};


SetModuleName: PROC[fileName, moduleName: STRING] RETURNS[success: BOOL] = {
innardsobject: ProcBcds.InnardsObject ← [bcdheaderspace: Space.nullHandle];
-- must go and read in bcd to get modulename
success ← FALSE;
innardsobject.cap ← Directory.Lookup[fileName: fileName, 
	permissions: Directory.ignore ! Directory.Error => GOTO out];
ProcBcds.ReadInSegmentsBcd[@innardsobject];
ProcBcds.InstallAddressesBcd[@innardsobject];
IF NOT ProcBcds.GetModuleName[@innardsobject, moduleName] THEN {
	ProcBcds.UnstallBcd[@innardsobject];
	GOTO out;
	};
ProcBcds.UnstallBcd[@innardsobject];
success ← TRUE;
EXITS
out => CWF.WF1["Error - can't get modulename for %s.\n"L, fileName];
};

-- will fixup spappl so that splet may also export it
-- note the PLUS node must follow the splet
FixupAPPLtoLET: PROC[spappl: MDModel.APPLSymbol, splet: MDModel.LETSymbol, 
	symbolseq: MDModel.SymbolSeq] = {
spelem: MDModel.APPLSymbol;

IF spappl.applval.stype ~= typeLIST THEN {
	-- if spappl is not already a PLUS list
	newsp: MDModel.APPLSymbol;
	newsp ← MDModel.NewSymAPPL[symbolseq];
	newsp↑ ← spappl↑;
	newsp.applsym ← MDModel.GenerateUniqueName[spappl];
	newsp.letparent ← NIL;
	spappl.applval ← MDModel.AddToEndOfList[NIL, newsp, plus, symbolseq];
	symbolseq.toploc.nestedmodel.model ← 
		MDModel.AddToEndOfList[symbolseq.toploc.nestedmodel.model,
			newsp, normal, symbolseq];
	};
spappl.letparent ← NIL;
spelem ← MDModel.NewSymAPPL[symbolseq];
spelem.applsym ← MDModel.GenerateUniqueName[spappl];
spelem.appltype ← spappl.appltype;
spelem.applval ← NIL;
spelem.letparent ← splet;
splet.letgrp ← MDModel.AddToEndOfList[splet.letgrp, spelem, normal, symbolseq];
spappl.applval ← MDModel.AddToEndOfList[
	MDModel.NarrowToLIST[spappl.applval], spelem, plus, symbolseq];
};

-- will fixup spappl so that splet may also export it
FixupLETtoLET: PROC[spappl: MDModel.APPLSymbol, spletnew: MDModel.LETSymbol, 
	symbolseq: MDModel.SymbolSeq] = {
spletold: MDModel.LETSymbol;
newsp: MDModel.APPLSymbol;

spletold ← spappl.letparent;
[newlist: spletold.letgrp] ← MDModel.RemoveFromList[spappl, spletold.letgrp];
newsp ← MDModel.NewSymAPPL[symbolseq];
newsp.letparent ← spletold;
newsp.applsym ← MDModel.GenerateUniqueName[spappl];
newsp.applval ← NIL;
newsp.appltype ← spappl.appltype;
spletold.letgrp ← MDModel.AddToEndOfList[spletold.letgrp, newsp, normal, symbolseq];
spappl.applval ← MDModel.AddToEndOfList[NIL, newsp, plus, symbolseq];
spappl.letparent ← NIL;

newsp ← MDModel.NewSymAPPL[symbolseq];
newsp.letparent ← spletnew;
newsp.applsym ← MDModel.GenerateUniqueName[spappl];
newsp.applval ← NIL;
newsp.appltype ← spappl.appltype;
spletnew.letgrp ← MDModel.AddToEndOfList[spletnew.letgrp, newsp, normal, symbolseq];
spappl.applval ← MDModel.AddToEndOfList[
	MDModel.NarrowToLIST[spappl.applval], newsp, plus, symbolseq];
symbolseq.toploc.nestedmodel.model ← 
	MDModel.AddToEndOfList[symbolseq.toploc.nestedmodel.model,
		spappl, normal, symbolseq];
};

-- manually set spimpl's LET to APPL if necessary
-- on return spimpl may be type APPL!!!
SmashLETToAPPL: PROC[splet: MDModel.LETSymbol, symbolseq: MDModel.SymbolSeq]
	RETURNS[spimpl: MDModel.Symbol] = {
spl: MDModel.LISTSymbol;
spimpl ← splet;
spl ← splet.letgrp;
MDModel.CkType[spl, typeLIST];
IF spl.rest = NIL THEN {
	spa: MDModel.APPLSymbol;
	spa ← MDModel.NarrowToAPPL[spl.first];
	IF spa.letparent ~= splet THEN ERROR;
	spa.letparent ← NIL;
	spa.applval ← splet.letval;
	spa.letparent ← NIL;
	spimpl ← spa;
	}
ELSE RemoveTheElements[symbolseq, spl];	-- remove extra appls from the master list
};

LookForMod: PROC[symbolseq: MDModel.SymbolSeq, spimpl1: MDModel.Symbol] = {
spimpltype: MDModel.TYPESymbol;
spimpl: MDModel.APPLSymbol;
done: BOOL ← FALSE;

	-- this is only called if spimpl has been filled in
	Anal: PROC[sp1: MDModel.Symbol] = {
	IF done THEN RETURN;
	WITH sp: sp1 SELECT FROM
	typeAPPL => {
		spt: MDModel.TYPESymbol;
		sploc: MDModel.LOCSymbol;
		listtype: MDModel.ListType;
		IF NOT LongString.EquivalentString[sp.applsym, spimpl.applsym] THEN 
			RETURN;
		spt ← MDModel.NarrowToTYPE[sp.appltype];
		IF spt.typeval = NIL THEN RETURN; 
		sploc ← MDModel.NarrowToLOC[spt.typeval];
		IF NOT LongString.EquivalentString[spimpltype.typesym,spt.typesym] THEN 
			RETURN;
		IF spimpltype.typeval ~= NIL THEN {
			IF MDModel.NarrowToLOC[spimpltype.typeval].createtime 
				~= sploc.createtime THEN RETURN;
			};
		IF sp.applval ~= NIL AND sp.applval ~= spimpl.applval THEN { 
			listtype ← IF sp.applval.stype = typeLIST THEN
				MDModel.NarrowToLIST[sp.applval].listtype ELSE plus;
 			-- modify it
			sp.applval ← MDModel.MergeIntoList[sp.applval,
				spimpl.applval, symbolseq, listtype];
			}
		ELSE sp.applval ← spimpl.applval;
		done ← TRUE;
		};
	typeLET => {
		splist: MDModel.LISTSymbol;
		splist ← sp.letgrp;
		WHILE splist ~= NIL DO
			IF LongString.EquivalentString[MDModel.Sym[splist.first],
				spimpl.applsym] THEN
				CWF.WF1["Warning - %s conflicts with a LET.\n"L, 
					spimpl.applsym];
			splist ← splist.rest;
			ENDLOOP;
		};
	ENDCASE => NULL;
	};

IF spimpl1.stype = typeAPPL THEN {
	spimpl ← MDModel.NarrowToAPPL[spimpl1];
	spimpltype ← MDModel.NarrowToTYPE[spimpl.appltype];
	MDModel.TraverseList[symbolseq.toploc.nestedmodel.model, Anal];
	};
IF NOT done THEN 
	symbolseq.toploc.nestedmodel.model ← 
		MDModel.AddToEndOfList[symbolseq.toploc.nestedmodel.model,
			 spimpl1, normal, symbolseq];
};

MAXAPPL: CARDINAL = 200;

FigureOutExports: PROC[symbolseq: MDModel.SymbolSeq] 
	RETURNS[procret: MDModel.LISTSymbol] = {
appls: ARRAY[0 .. MAXAPPL) OF MDModel.APPLSymbol;
nappls: CARDINAL ← 0;

	GetAllAppls: PROC[sp: MDModel.Symbol, spmodel: MDModel.MODELSymbol]
		 RETURNS[proceed: BOOL ← TRUE]  = {
	spappl: MDModel.APPLSymbol;
	IF sp.stype ~= typeAPPL THEN RETURN;
	spappl ← MDModel.NarrowToAPPL[sp];
	IF spappl.appltype = symbolseq.controlv THEN RETURN;
	FOR i: CARDINAL IN [0 .. nappls) DO
		IF spappl = appls[i] THEN RETURN;
		ENDLOOP;
	IF nappls < LENGTH[appls] THEN {
		appls[nappls] ← spappl;
		nappls ← nappls + 1;
		}
	ELSE CWF.WF0["Too many appls in the model.\n"L];
	};

	RunDownList: PROC[sp: MDModel.Symbol] = {
	IF sp.stype ~= typeAPPL THEN RETURN;
	FOR i: CARDINAL IN [0 .. nappls) DO
		IF sp = appls[i] THEN {
			appls[i] ← NIL;
			RETURN;
			};
		ENDLOOP;
	};
	
	LookForLOC: PROC[sp: MDModel.Symbol, spmodel: MDModel.MODELSymbol]
		 RETURNS[proceed: BOOL ← TRUE]  = {
	WITH spt: sp SELECT FROM 
	typeLOC => MDModel.TraverseList[spt.parmlist, RunDownList];
	typePROC => MDModel.TraverseList[spt.procparm, RunDownList];
	ENDCASE => NULL;
	};
	
-- first, collect all the APPLS
MDModel.TraverseTree[symbolseq.toploc, symbolseq, GetAllAppls];

-- now we look through and eliminate those in the list
-- that are on parameter lists
procret ← NIL;
MDModel.TraverseTree[symbolseq.toploc, symbolseq, LookForLOC];

-- the remains in the list are exports(?)
FOR i: CARDINAL IN [0 .. nappls) DO
	IF appls[i] ~= NIL THEN 
		procret ← MDModel.AddToEndOfList[procret, appls[i], 
			normal, symbolseq];
	ENDLOOP;
};
	

-- the altoCode switch is permanently FALSE
SwitchesToSymbol: PROC[altoCode, boundsChecks, cedarSwitch, crossJump, 
	linksInCode, nilChecks, sortByUsage: BOOL, symbolseq: MDModel.SymbolSeq] 
	RETURNS[sp: MDModel.STRINGSymbol] = {
stemp1: STRING ← [20];
stemp2: STRING ← [20];
CWF.SWF3[stemp1, "%sb%sc%sj"L, 
	IF boundsChecks THEN ""L ELSE "-"L,
	IF cedarSwitch THEN ""L ELSE "-"L,
	IF crossJump THEN ""L ELSE "-"L];
CWF.SWF4[stemp2, "%s%sl%sn%ss"L, stemp1,
	IF linksInCode THEN ""L ELSE "-"L,
	IF nilChecks THEN ""L ELSE "-"L,
	IF sortByUsage THEN ""L ELSE "-"L];
sp ← MDModel.NewSymSTRING[symbolseq];
sp.strval ← Subr.CopyString[stemp2];
};

-- this puts all the PLUS's at the end of the model
MoveAllPlus: PROC[oldlist: MDModel.LISTSymbol, symbolseq: MDModel.SymbolSeq] 
	RETURNS[newlist: MDModel.LISTSymbol]= {
first, second, splist: MDModel.LISTSymbol ← NIL;
splist ← oldlist;
WHILE splist ~= NIL DO
	{
	IF splist.first.stype = typeAPPL THEN {
		spappl: MDModel.APPLSymbol;
		spappl ← MDModel.NarrowToAPPL[splist.first];
		IF spappl.applval ~= NIL 
		AND spappl.applval.stype = typeLIST THEN {
			second ← MDModel.AddToEndOfList[second, splist.first, 
				normal, symbolseq];
			GOTO loop;
			};
		};
	first ← MDModel.AddToEndOfList[first, splist.first, 
		normal, symbolseq];
	GOTO loop;
	EXITS
	loop => splist ← splist.rest;
	};
	ENDLOOP;
newlist ← first;
IF second ~= NIL THEN
	newlist ← MDModel.NarrowToLIST[MDModel.MergeIntoList[first, second, 
		symbolseq, normal]];
};

RemoveTheElements: PROC[symbolseq: MDModel.SymbolSeq, 
	splist: MDModel.LISTSymbol] = {
WHILE splist ~= NIL DO
	MDModel.CkType[splist, typeLIST];
	[newlist: symbolseq.toploc.nestedmodel.model] ←
		 MDModel.RemoveFromList[splist.first, 
		 	symbolseq.toploc.nestedmodel.model];
	splist ← splist.rest;
	ENDLOOP;
};

SpecialCase: PROC[sfn: LONG STRING, symbolseq: MDModel.SymbolSeq,
	innards: ProcBcds.Innards] RETURNS[isaspecialcase: BOOL] = {
sptype: MDModel.TYPESymbol;
spappl: MDModel.APPLSymbol;
sploclet: MDModel.LOCSymbol;
splet: MDModel.LETSymbol;

	procMod: ProcBcds.ProcMod = {
	fi: Dir.FileInfo ← MDModel.GetFileInfo[sploclet];
	fi.bcdVers ← bcdvers;
	fi.moduleName ← Subr.CopyString[smodulename];
	uns ← NIL;
	};
	
	procDep: ProcBcds.ProcDep = {
	};
	
IF NOT LongString.EquivalentString[sfn, "ModelParseData.Bcd"L] THEN 
	RETURN[FALSE];
sploclet ← MDModel.NewSymLOC[symbolseq];
sploclet.tail ← Subr.CopyString["ModelParseData"L];
sploclet.sext ← Subr.CopyString["Bcd"L];
[] ← ProcBcds.PrintDepends[innards, procMod, procDep, FALSE, FALSE, TRUE, sfn];	-- less is TRUE
ProcBcds.UnstallBcd[innards];
sptype ← MDModel.NewSymTYPE[symbolseq];
sptype.typesym ← Subr.CopyString["ModelParseData"L];
sptype.typeName ← Subr.CopyString["ModelParseData"L];
sptype.typeval ← NIL;
sptype.frameptr ← TRUE;
spappl ← MDModel.NewSymAPPL[symbolseq];
spappl.applsym ← Subr.CopyString["ModelParseDataImpl"L];
spappl.appltype ← sptype;
spappl.applval ← NIL;
splet ← MDModel.NewSymLET[symbolseq];
splet.letgrp ← MDModel.AddToEndOfList[NIL, sptype, normal, symbolseq];
splet.letgrp ← MDModel.AddToEndOfList[splet.letgrp, spappl, normal, symbolseq];
splet.letval ← sploclet;
spappl.letparent ← splet;
sptype.letparent ← splet;
symbolseq.toploc.nestedmodel.model ← 
	MDModel.AddToEndOfList[symbolseq.toploc.nestedmodel.model,
		splet, normal, symbolseq];
RETURN[TRUE];
};

-- this will involve a directoy lookup and sourcefile check!
FigureOutRemoteName: PROC[sploc: MDModel.LOCSymbol, sourcefile: STRING] = {
cap: File.Capability;
create: LONG CARDINAL;
fullname: STRING ← [125];
host: STRING ← [100];
directory: STRING ← [100];
cap ← Directory.Lookup[fileName: sourcefile, permissions: Directory.ignore
	! Directory.Error => GOTO out];
[create: create] ← Subr.GetCreateDate[cap];
IF create ~= sploc.createtime THEN GOTO out;
-- they are equal, now get remote property
Subr.GetRemoteFilenameProp[cap, fullname];
IF fullname.length = 0 THEN GOTO out;
[] ← DFSubr.StripLongName[fullname, host, directory, NIL];
IF host.length > 0 THEN sploc.host ← Subr.CopyString[host];
IF directory.length > 0 THEN sploc.path ← Subr.CopyString[directory];
EXITS
out => NULL;
};

-- main program
Init: PROC = {
-- set up WF stuff
[] ← CWF.SetWriteProcedure[PutProc];
UserExec.RegisterCommand["DesignModel.~", Main];
-- the main program exits at this point
-- SimpleExec will call Main when the user invokes it
};

Init[];
}.


-- NO LONGER NEEDED
-- move sptype from the procval field to the procparm field
-- assumes symbolseq.toploc.nestedmodel.model is a list of one element, which is a PROC
SpliceType: PROC[symbolseq: MDModel.SymbolSeq, sptype: MDModel.TYPESymbol] = {
spm: MDModel.PROCSymbol;
spl: MDModel.LISTSymbol;
splast: MDModel.Symbol;
spm ← MDModel.NarrowToPROC[symbolseq.toploc.nestedmodel.model.first];
spl ← MDModel.NarrowToLIST[spm.procval];
splast ← symbolseq.toploc.nestedmodel.model.first;
WHILE spl ~= NIL DO
	MDModel.CkType[spl, typeLIST];
	IF spl.first = sptype THEN {
		MDModel.AddToEndOfList[@spm.procparm, sptype, normal,
			symbolseq];
		WITH splast1: splast SELECT FROM
		typePROC => splast1.procval ← spl.rest;
		typeLIST => splast1.rest ← spl.rest;
		ENDCASE => ERROR;
		}
	ELSE splast ← spl;
	spl ← spl.rest;
	ENDLOOP;
};

-- take the module described by spimpl and look for it in symbolseq
-- if not already there, add it, if there, stick in spimpl's value
OldLookForMod: PROC[symbolseq: MDModel.SymbolSeq, spimpl1: MDModel.Symbol] = {
spimpltype: MDModel.TYPESymbol;
spimpl: MDModel.APPLSymbol;
done: BOOL ← FALSE;

	-- this is only called if spimpl has been filled in
	RunDownList: PROC[sp1: MDModel.Symbol, spmodel: MDModel.MODELSymbol]
		RETURNS[proceed: BOOL ← TRUE] = {
	IF done THEN RETURN[FALSE];
	WITH sp: sp1 SELECT FROM
	typeAPPL => {
		spt: MDModel.TYPESymbol;
		sploc: MDModel.LOCSymbol;
		listtype: MDModel.ListType;
		IF NOT LongString.EquivalentString[sp.applsym, spimpl.applsym] THEN 
			RETURN;
		spt ← MDModel.NarrowToTYPE[sp.appltype];
		IF spt.typeval = NIL THEN RETURN; 
		sploc ← MDModel.NarrowToLOC[spt.typeval];
		IF NOT LongString.EquivalentString[spimpltype.typesym,spt.typesym] THEN 
			RETURN;
		IF spimpltype.typeval ~= NIL THEN {
			IF MDModel.NarrowToLOC[spimpltype.typeval].createtime 
				~= sploc.createtime THEN RETURN;
			};
		IF sp.applval ~= NIL AND sp.applval ~= spimpl.applval THEN { 
			listtype ← IF sp.applval.stype = typeLIST THEN
				MDModel.NarrowToLIST[sp.applval].listtype ELSE plus;
 			-- modify it
			sp.applval ← MDModel.MergeIntoList[sp.applval,
				spimpl.applval, symbolseq, listtype];
			}
		ELSE sp.applval ← spimpl.applval;
		-- this records the new place for the value
		spimpl ← MDModel.NarrowToAPPL[sp1];
		done ← TRUE;
		};
	typeLET => {
		splist: MDModel.LISTSymbol;
		splist ← sp.letgrp;
		WHILE splist ~= NIL DO
			IF LongString.EquivalentString[MDModel.Sym[splist.first],
				spimpl.applsym] THEN
				CWF.WF1["Warning - %s conflicts with a LET.\n"L, 
					spimpl.applsym];
			splist ← splist.rest;
			ENDLOOP;
		};
	ENDCASE => NULL;
	};

IF spimpl1.stype = typeAPPL THEN {
	spimpl ← MDModel.NarrowToAPPL[spimpl1];
	spimpltype ← MDModel.NarrowToTYPE[spimpl.appltype];
	MDModel.TraverseTree[symbolseq.toploc.nestedmodel.model, symbolseq, RunDownList];
	IF done AND spimpl.letparent = NIL 
	AND NOT MDModel.IsOnList[spimpl, symbolseq.toploc.nestedmodel.model] THEN
		done ← FALSE;
	};
IF NOT done THEN 
	symbolseq.toploc.nestedmodel.model ← 
		MDModel.AddToEndOfList[symbolseq.toploc.nestedmodel.model,
			 spimpl1, normal, symbolseq];
};

	-- symbolseq is passed in
	ConvertLetToAppl: PROC[sp1: MDModel.Symbol] = {
	splist: MDModel.LISTSymbol;
	splet: MDModel.LETSymbol;
	nlist: CARDINAL ← 0;
	IF sp1.stype ~= typeLET THEN RETURN;
	splet ← MDModel.NarrowToLET[sp1];
 	splist ← splet.letgrp;
	WHILE splist ~= NIL DO
		MDModel.CkType[splist, typeLIST];
		nlist ← nlist + 1;
		IF nlist > 100 THEN ERROR;		-- cycling
	 	splist ← splist.rest
		ENDLOOP;
	IF nlist = 1 THEN {	-- only one thing in LET
		spt: MDModel.Symbol;
		spl: MDModel.LISTSymbol;
		spsave: MDModel.APPLSymbol;
		spt ← splet.letval;
		spl ← splet.letgrp;
		MDModel.CkType[spl, typeLIST];
		spsave ← MDModel.NarrowToAPPL[spl.first];
		IF spsave.letparent = splet THEN
			spsave.letparent ← NIL;
		-- smash on top, this changes the type of splet
		SmashOnTopOf[spsave, splet];
		-- should not free strings of spsave 
		-- they are also in splet↑
		spsave.applsym ← NIL;
		-- splet is now of type APPL
		MDModel.NarrowToAPPL[splet].applval ← spt;
		MDModel.ReplaceBy[spsave, splet, symbolseq];
		-- this will detect erroneous references to this
		MDModel.ZeroOut[spsave];
		MDModel.ZeroOut[spl];
		}
	ELSE 	-- remove from the master list, 
		-- as they are defined in the LET[]
		RemoveTheElements[symbolseq, splet.letgrp];
	};

SmashOnTopOf: PROC[sp, ontopof: MDModel.Symbol] = {
ontopof↑ ← sp↑;
};

	-- not called!
	SeeIfUndefined: PROC[sp: MDModel.Symbol, spmodel: MDModel.MODELSymbol]
		RETURNS[proceed: BOOL ← TRUE] = {
	IF sp.stype ~= typeAPPL THEN RETURN;
	spa ← MDModel.NarrowToAPPL[sp];
	-- looking for an impl that has no value and is
	-- not exported by a LET
	IF spa.applval = NIL AND spa.letparent = NIL THEN {
		sptype: MDModel.TYPESymbol;
		sploc: MDModel.LOCSymbol;
		sptemp: MDModel.LISTSymbol;
		stemp: STRING ← [100];
		-- SpliceType[symbolseq, spa.appltype];
		spm.procparm ← MDModel.AddToEndOfList[spm.procparm, 
			spa.appltype, normal, symbolseq];
		sptemp ← MDModel.NarrowToLIST[spm.procval];
		[newlist: spm.procval] ← 
			MDModel.RemoveFromList[spa.appltype, sptemp];
		-- now add the IMPL
		spm.procparm ← MDModel.AddToEndOfList[spm.procparm, 
			spa, normal, symbolseq];
		sptype ← MDModel.NarrowToTYPE[spa.appltype];
		sploc ← MDModel.NarrowToLOC[sptype.typeval];
		CWF.SWF2[stemp, "%s.%s"L, sploc.tail, sploc.sext];
		IF NOT Subr.IsASystemFile[stemp] THEN 
			CWF.WF1["Warning - %s is a parameter but is not a Mesa System file.\n"L, 
				stemp];
		};
	};