-- TexAlign.mesa
-- Last changed by Doug Wyatt, September 23, 1980  3:39 PM

DIRECTORY
	TexDefs: FROM "TexDefs",
	TexErrorDefs: FROM "TexErrorDefs",
	TexGlueDefs: FROM "TexGlueDefs",
	TexAlignDefs: FROM "TexAlignDefs",
	TexMainDefs: FROM "TexMainDefs",
	TexMemDefs: FROM "TexMemDefs",
	TexNodeDefs: FROM "TexNodeDefs",
	TexPackDefs: FROM "TexPackDefs",
	TexSynDefs: FROM "TexSynDefs",
	TexTableDefs: FROM "TexTableDefs",
	TexTokenDefs: FROM "TexTokenDefs";

TexAlign: PROGRAM
IMPORTS TexErrorDefs,TexGlueDefs,TexMainDefs,TexMemDefs,
	TexNodeDefs,TexPackDefs,TexSynDefs,TexTableDefs,TexTokenDefs
EXPORTS TexAlignDefs =
BEGIN OPEN TexErrorDefs,TexMainDefs,TexPackDefs,TexTableDefs,TexNodeDefs,
	TexGlueDefs,TexSynDefs,TexTokenDefs,TexAlignDefs,TexDefs;

alignmentInProgress: PUBLIC BOOLEAN←FALSE;

MakeAlignRecord: PROCEDURE RETURNS [p: POINTER TO AlignRecord] =
	BEGIN
	p←TexMemDefs.AllocMem[SIZE[AlignRecord]];
	p↑←[NIL,NIL,0,NIL,NIL];
	END;

FreeAlignRecord: PROCEDURE[p: POINTER TO AlignRecord] =
	BEGIN
	TexMemDefs.FreeMem[p,SIZE[AlignRecord]];
	END;

GetNonTabskipTok: PROCEDURE =
	BEGIN g: GluePtr←MakeGlue[];
	AddGlueLink[g];
	GetTok[];
		DO -- get non-tabskip token
		WITH cc:curchar SELECT curcmd FROM
			assignglue => IF cc.glueparam=tabskip THEN
									BEGIN
									ScanGlue[g];
									SetGlueParam[tabskip,g];
									END;
			ENDCASE => EXIT;
		GetTok[];
		ENDLOOP;
	DelGlueLink[g];
	END;

HAlign: PUBLIC PROCEDURE[vhead: VHeadPtr,math: BOOLEAN] =
	BEGIN
	Alignment[NIL,vhead,vlist,math];
	END;

VAlign: PUBLIC PROCEDURE[hhead: HHeadPtr] =
	BEGIN
	Alignment[hhead,NIL,hlist,FALSE];
	END;

Alignment: PROCEDURE[hhead: HHeadPtr, vhead: VHeadPtr,
			d: Direction, math: BOOLEAN] =
	BEGIN list: NodeListPtr;
	tempPtr,alignListPtr,alignRecPtr: POINTER TO AlignRecord;
	temp,len: Dimn; xpand, break: BOOLEAN;
	size: Dimn;
	lastBeforeAlign: NodePtr;
	prevPtr,tobesetPtr: NodePtr;
	carretseen: BOOLEAN;
	holdhead: TokenLEntry; q: TokenPtr;
	imbalance: INTEGER;

	-- first do the initalign code

	SELECT d FROM
		hlist => list←hhead.hlist;
		vlist => list←vhead.vlist;
		ENDCASE;
	size←DimnParam[hsize];
	lastBeforeAlign←list.last;

	alignmentInProgress←TRUE;
	alignListPtr←alignRecPtr←MakeAlignRecord[];
	[len,xpand,break]←ScanSpec[FALSE,size]; -- throw break away
	NewSaveLevel[aligncode]; -- guard against extra }'s

	DO BEGIN
		imbalance←0;
		AddGlueLink[alignRecPtr.tabglue←GlueParam[tabskip]];
		IF curcmd=carret THEN EXIT; -- \cr sensed
		alignRecPtr←(alignRecPtr.link←MakeAlignRecord[]);
		q←@holdhead; holdhead←nilTokenLEntry;
			DO -- get U part
			GetNonTabskipTok[];
			SELECT curcmd FROM
			tabmrk,carret => BEGIN
				BackError["Missing # inserted in alignment preamble"];
				EXIT;
				END;
			macprm => EXIT;
			lbrace => imbalance←imbalance+1;
			rbrace => imbalance←imbalance-1;
			ENDCASE;
			StoreTok[@q,curtok];
			ENDLOOP;
		alignRecPtr.UPart←holdhead.link;
		IF imbalance<0 THEN TexErrorDefs.Confusion;
		-- now start again
		q←@holdhead; holdhead←nilTokenLEntry;
			DO -- get V part
			GetNonTabskipTok[];
			SELECT curcmd FROM
			tabmrk,carret => IF imbalance=0 THEN EXIT;
			macprm => BEGIN
				Error["Only one # allowed per tab"];
				LOOP;
				END;
			lbrace => imbalance←imbalance+1;
			rbrace => imbalance←imbalance-1;
			ENDCASE;
			StoreTok[@q,curtok];
			ENDLOOP;
		StoreTok[@q,[endv[]]];
		alignRecPtr.VPart←holdhead.link;
		alignRecPtr.maxsofar←0;
		END;	
	ENDLOOP;

	-- next process an arbitrary mixture of table rows and \noaligns

	DO
	ScanNonSpacer[];
	SELECT curcmd FROM
	noalign => -- a \noalign
		BEGIN
		ScanLB[]; -- do skipspacers
		NewSaveLevel[noalignend];
		IF d=vlist THEN VMode[vhead,TRUE] ELSE HMode[hhead,TRUE];
		UnSave[noalignend];
		END;
	rbrace => EXIT; -- end of alignment
	ENDCASE => -- a table row
		BEGIN rowPtr: NodeListPtr←InitNodeList[];
		BackInput[]; -- curtok to be rescanned after the U part
		alignRecPtr←alignListPtr.link; -- "rewind" alignRecPtr
		carretseen←(curcmd=carret); -- catch an empty row
		WHILE NOT carretseen DO
			BEGIN
			unsetPtr: NodeListPtr←InitNodeList[];
			totalFlex: FlexSums; flexPtr: FlexSumsPtr←@totalFlex;
			p: BoxNodePtr;
			IF alignRecPtr=NIL THEN
				BEGIN
				Error["Extra alignment tab"];
				EXIT;
				END;
			NewSaveLevel[alignentry];
			q←@holdhead; holdhead←nilTokenLEntry;
			imbalance←0;
			DO -- get a table entry
				GetTok[];
				SELECT curcmd FROM
					lbrace => imbalance←imbalance+1;
					rbrace =>
						IF imbalance=0 THEN
							BEGIN
							BackError["Missing \cr inserted"];
							carretseen←TRUE;
							EXIT;
							END
						ELSE imbalance←imbalance-1;
					tabmrk => IF imbalance=0 THEN EXIT;
					carret => IF imbalance=0 THEN BEGIN carretseen←TRUE; EXIT; END;
					ENDCASE;
			StoreTok[@q,curtok];
			ENDLOOP;
			InsVPart[alignRecPtr.VPart];
			InsList[holdhead.link];
			InsUPart[alignRecPtr.UPart];
			IF d=vlist THEN
				BEGIN hh: HHead←[unsetPtr,sfOne];
				HMode[@hh,TRUE]; -- claim: unsetPtr=hh.hlist
				END
			ELSE BEGIN vh: VHead←[unsetPtr,pflag];
				VMode[@vh,TRUE]; -- claim: unsetPtr=vh.vlist
				END;
			IF curcmd#endv THEN TexErrorDefs.Confusion;
			UnSave[alignentry];
			IF d=vlist THEN
				BEGIN
				insertsPtr: NodeListPtr←InitNodeList[];
				p←HPackage[unsetPtr,0,TRUE,insertsPtr,flexPtr];
				IF (temp←p.width)>alignRecPtr.maxsofar THEN
					alignRecPtr.maxsofar←temp;
				FreeNodeList[insertsPtr];
				END
				ELSE BEGIN
				p←VPackage[unsetPtr,0,TRUE,FALSE,flexPtr];
				IF (temp←p.height+p.depth)>alignRecPtr.maxsofar THEN
					alignRecPtr.maxsofar←temp;
				END;
			StoreNode[rowPtr,MakeUnsetNode[p,flexPtr]];
			alignRecPtr←alignRecPtr.link;
			END;
		ENDLOOP;
		IF d=vlist THEN VAppend[vhead,HBox[rowPtr]]
		ELSE HAppend[hhead,VBox[rowPtr]];
		END;
	ENDLOOP;
	UnSave[aligncode];

	-- now do the endalign code

	prevPtr←tobesetPtr←lastBeforeAlign;
	WHILE tobesetPtr#NIL DO
		WITH cp:tobesetPtr SELECT FROM
		box => IF cp.head#NIL THEN BEGIN
			WITH unsetBox:cp.head SELECT FROM
			unset =>
				BEGIN -- unsetBox contains an unset box; time to set it
				setRowPtr: NodeListPtr←InitNodeList[];
				unsetRowPtr: UnsetNodePtr←@unsetBox;
				alignedBoxPtr,tempBoxPtr: BoxNodePtr;
				unsetListExhausted: BOOLEAN←FALSE; -- there is always one unset box
				IF cp.dir=d THEN TexErrorDefs.Confusion;
				alignRecPtr←alignListPtr;
					DO -- do a row
					StoreNode[setRowPtr,MakeGlueNode[alignRecPtr.tabglue]];
					alignRecPtr←alignRecPtr.link;
					IF alignRecPtr=NIL THEN EXIT;
					IF unsetListExhausted THEN
						BEGIN tempBoxPtr←NullBox[];
						IF d=vlist THEN tempBoxPtr.width←alignRecPtr.maxsofar
						ELSE tempBoxPtr.height←alignRecPtr.maxsofar;
						END
					ELSE BEGIN curboxPtr: BoxNodePtr←unsetRowPtr.box;
						delta: Dimn; l: Dimn;
						IF d=vlist THEN
							BEGIN
							l←curboxPtr.width;
							curboxPtr.width←alignRecPtr.maxsofar;
							END
						ELSE BEGIN
							l←curboxPtr.height+curboxPtr.depth;
							curboxPtr.height←alignRecPtr.maxsofar-curboxPtr.depth;
							END;
						IF (delta←alignRecPtr.maxsofar-l)#0 THEN
							curboxPtr.glueset←SetGlue[IF delta>0 THEN str ELSE shr,
															ABS[delta],@unsetRowPtr.totalStretch];
						(tempBoxPtr←curboxPtr).link←NIL;
						cp.head←curboxPtr.link; unsetRowPtr.box←NIL;
						END;
					StoreNode[setRowPtr,tempBoxPtr];
					IF unsetRowPtr.link=NIL THEN unsetListExhausted←TRUE
					ELSE WITH ur:unsetRowPtr.link SELECT FROM
						unset => unsetRowPtr←@ur;
						ENDCASE => TexErrorDefs.Confusion;
					ENDLOOP;
				IF d=vlist THEN alignedBoxPtr←HPack[setRowPtr,len,xpand]
				ELSE alignedBoxPtr←VPack[setRowPtr,len,xpand];
				prevPtr.link←alignedBoxPtr; alignedBoxPtr.link←tobesetPtr.link;
				tobesetPtr.link←NIL;
				DsNode[tobesetPtr];
				tobesetPtr←alignedBoxPtr;
				END;
			ENDCASE;
			END;
		ENDCASE;
		prevPtr←tobesetPtr; tobesetPtr←prevPtr.link;
		ENDLOOP;
	list.last←prevPtr; -- just to be sure

	alignRecPtr←alignListPtr;
		DO -- free the alignment records
		DelGlueLink[alignRecPtr.tabglue];
		DsList[alignRecPtr.UPart];
		DsList[alignRecPtr.VPart];
		tempPtr←alignRecPtr.link;
		FreeAlignRecord[alignRecPtr];
		IF tempPtr=NIL THEN EXIT ELSE alignRecPtr←tempPtr;
		ENDLOOP;

	IF math THEN -- insert dispskip glue before and after
		BEGIN r,s: GlueNodePtr;
		r←MakeGlueNode[GlueParam[dispskip]];
		r.link←lastBeforeAlign.link;
		lastBeforeAlign.link←r;
		s←MakeGlueNode[GlueParam[dispskip]];
		list.last←prevPtr.link←s;
		END;

	alignmentInProgress←FALSE;

	END;

END.