-- TexMMode.mesa

-- last written by Doug Wyatt, December 13, 1979  4:39 PM

DIRECTORY
	TexDefs: FROM "TexDefs",
	TexErrorDefs: FROM "TexErrorDefs",
	TexIODefs: FROM "TexIODefs",
	TexMainDefs: FROM "TexMainDefs",
	TexMathDefs: FROM "TexMathDefs",
	TexMathOpDefs: FROM "TexMathOpDefs",
	TexNoadDefs: FROM "TexNoadDefs",
	TexNodeDefs: FROM "TexNodeDefs",
	TexPackDefs: FROM "TexPackDefs",
	TexSynDefs: FROM "TexSynDefs",
	TexTableDefs: FROM "TexTableDefs";

TexMMode: PROGRAM
IMPORTS TexErrorDefs,TexIODefs,TexMainDefs,TexMathOpDefs,TexNoadDefs,
	TexNodeDefs,TexPackDefs,TexSynDefs,TexTableDefs
EXPORTS TexMainDefs =
BEGIN OPEN TexTableDefs,TexSynDefs,TexMainDefs,TexNodeDefs,
	TexNoadDefs,TexMathDefs;

-- *************
-- * Math mode *
-- *************

eqnobox: BoxNodePtr; -- box containing \eqno for display MMode

MMode: PROCEDURE[mlist: NoadListPtr, restricted: BOOLEAN] =
	BEGIN
	incompleatnoad: AboveNoadPtr←NIL;
	-- if not NIL, incompleatnoad points to a partly completed AboveNoad
	head: NoadPtr←mlist.last;
	AddToMlist: PROCEDURE[p: NoadPtr] = --INLINE--
		BEGIN StoreNoad[mlist, p] END;
	MathChar: PROCEDURE[t: TMChar] = --INLINE--
		BEGIN StoreNoad[mlist, MakeCharNoad[t]] END;
	MakeMathBox: PROCEDURE[ff: MField] = --INLINE--
		BEGIN StoreNoad[mlist, MakeScriptedNoad[ff]] END;
	Store: PROCEDURE[p: NodePtr] =
		BEGIN StoreNoad[mlist, MakeNodeNoad[p]] END;
	ScanMlist: PROCEDURE[ec: EndingCode] RETURNS[MField] =
		BEGIN
		block: NoadListPtr←InitNoadList[];
		NewSaveLevel[ec]; MMode[block, restricted]; UnSave[ec];
		RETURN[[mlist[FinishNoadList[block]]]];
		END;
	ScanMath: PROCEDURE[f: MFieldPtr] =
		BEGIN
		-- At this point the next portion of the input should be
		-- either a single character or "{<mlist>}".
		-- We want to fill the	MField pointed to by f.
		ScanNonSpacer[]; -- get next non-spacer
		WITH cc:curchar SELECT curcmd FROM
			letter,otherchar => f↑←[mchar[MModeCode[cc.char].mchar]];
			mathonly => f↑←[mchar[cc.tmchar.mchar]];
			ascii => f↑←[mchar[ScanMChar[]]];
			ENDCASE =>
				BEGIN
				IF curcmd#lbrace THEN TexErrorDefs.BackError["Missing { inserted"];
				f↑←ScanMlist[endscanmath];
				END;
		END;

	DO
	GetNext;
	BEGIN
	ENABLE
		BEGIN
		FallThru => BEGIN SIGNAL CantDoThat[m, restricted]; CONTINUE END;
		Reswitch => RETRY;
		Continue => CONTINUE;
		END;

	WITH cc:curchar SELECT curcmd FROM

	spacer => NULL; -- ignore spacers in math mode

	undefined => SIGNAL Undefined; -- unknown control sequence

	call => MacroCall[]; -- defined control sequence

	lbrace => MakeMathBox[ScanMlist[mathblock]]; -- Append a subformula

	rbrace => SELECT SaveCode[] FROM
		simpleblock => UnSave[simpleblock]; -- just pop the savestack
				-- (topbotmark can start simpleblocks)
		trueend => TrueEnd[]; -- skip over the \else part
		falseend => FalseEnd[]; -- skip over a spacer, if any
		mathcode,mathleft => TexErrorDefs.Error["Extra }"]; -- ignore it
		mathblock,endscanmath => EXIT; -- we're done
		aligncode,noalignend => SIGNAL Unimplemented; -- *** figure out later
		bottomlevel,justend,outputend,topinsend,botinsend,endvcenter =>
			ERROR TexErrorDefs.Confusion; -- invalid endcode in MMode
		ENDCASE => ERROR; -- bad EndingCode

	leftright => SELECT cc.lr FROM
		left =>
			BEGIN
			dlist: NoadListPtr←InitNoadList[];
			StoreNoad[dlist,MakeDelimNoad[left,ScanDelim[]]]; -- left delimiter
			NewSaveLevel[mathleft]; MMode[dlist,restricted]; UnSave[mathleft];
			StoreNoad[dlist,MakeDelimNoad[right,ScanDelim[]]]; -- right delimiter
			MakeMathBox[[mlist[FinishNoadList[dlist]]]];
			END;
		right => SELECT SaveCode[] FROM
			mathleft => EXIT; -- all is well
			mathcode =>
				BEGIN
				[]←ScanDelim[]; -- eat the delimiter
				TexErrorDefs.Error["Extra \right"]; -- complain, then press on
				END;
			ENDCASE => MissingRB; -- will SIGNAL Reswitch
		ENDCASE => ERROR; -- bad DelimType

	mathbr =>
		BEGIN
		IF SaveCode[]#mathcode THEN MissingBrace; -- will SIGNAL Reswitch
		IF restricted THEN EXIT; -- end of restricted math mode
		-- end of display mode, look for another $
		GetNCTok;
		IF curcmd#mathbr THEN
			TexErrorDefs.BackError["Display math should end with $$"];
		EXIT; -- end of display math mode
		END;

	letter,otherchar => MathChar[TexTableDefs.MModeCode[cc.char]];
	mathonly => MathChar[cc.tmchar];
	ascii => MathChar[[Ord,ScanMChar[]]];

	supmrk,submrk =>
		BEGIN
		scriptfield: MFieldPtr;
		Fp: PROCEDURE[p: ScriptedNoadPtr] RETURNS[MFieldPtr] = --INLINE--
			BEGIN RETURN[IF curcmd=supmrk THEN @p.supscr ELSE @p.subscr] END;
		ScriptFieldPtr: PROCEDURE RETURNS[MFieldPtr] = --INLINE--
			BEGIN
			p: ScriptedNoadPtr;
			-- is the last noad in mlist a ScriptedNoad?
			WITH qq:mlist.last SELECT FROM
				common => WITH qqq:qq SELECT FROM
					scripted =>
						BEGIN
						-- We have a scripted noad. Check the script field.
						f: MFieldPtr←Fp[@qqq];
						IF EmptyField[f] THEN RETURN[f] -- all is well
						ELSE -- oops, the super or subscript has already been set
							BEGIN OPEN TexErrorDefs,TexIODefs;
							BeginError; Ws["Double "];
							Ws[IF curcmd=supmrk THEN "super" ELSE "sub"];
							Ws["script"]; Error[EndError[]];
							-- after complaining, append a dummy noad
							END;
						END;
					ENDCASE;
				ENDCASE;
			-- add a dummy noad to mlist to receive the super or subscript
			p←MakeScriptedNoad[]; AddToMlist[p]; RETURN[Fp[p]];
			END;
		-- get a pointer to the MField for the super or subscript
		scriptfield←ScriptFieldPtr[];
		-- now scan a character or subformula to put in the field
		ScanMath[scriptfield];
		END;

	mathinput =>
		BEGIN
		q: ScriptedNoadPtr←MakeScriptedNoad[mtype: cc.mtype, stype: cc.stype];
		ScanMath[@q.operand]; AddToMlist[q];
		END;

	accent =>
		BEGIN
		q: ScriptedNoadPtr←MakeAccentNoad[[rm,cc.char]];
		ScanMath[@q.operand]; AddToMlist[q];
		END;

	eqno => IF restricted THEN SIGNAL FallThru -- allowed only in displays
		ELSE BEGIN
		hlist: NodeListPtr←InitNodeList[];
		ScanFormula[hlist,TRUE]; -- scan the eqno in restricted math mode
		eqnobox←TexPackDefs.HBox[hlist]; -- box the resulting hlist
		-- the $ that terminated the equation number is the beginning of
		-- the $$ that should terminate the display, so scan it again
		BackInput;
		END;

	mathspace => AddToMlist[MakeSpaceNoad[cc.space]];
	exspace => AddToMlist[MakeSpaceNoad[user]];
	mathstyle => AddToMlist[MakeStyleNoad[[norm,cc.style]]];

	hskip => Store[SkipGlue[cc.gluetype]];

	vmove => MakeMathBox[[box[ScanMovedBox[cc.neg]]]];

	box => MakeMathBox[[box[GetBox[cc.boxtype]]]];

	parend,endv => MissingMathbr; -- will SIGNAL Reswitch

	mdiscr => AddToMlist[MakeDiscNoad[cc.mchar]];

	halign => SIGNAL Unimplemented;

	above =>
		BEGIN
		IF incompleatnoad#NIL THEN
			ExtraAbove[cc.abovetype] -- two \above's in same mlist
		ELSE
			BEGIN
			incompleatnoad←ScanAbove[cc.abovetype];
			incompleatnoad.numerator←[mlist[head.link]];
			head.link←incompleatnoad; mlist.last←head←incompleatnoad;
			END;
		END;

	limsw => DoLimsw[mlist.last];

	vcenter => AddToMlist[ScanVcenter[cc.vctr]];

	penlty => Store[ScanPenltyNode[]];

	eject => IF restricted THEN Store[MakeEjectNode[]] ELSE SIGNAL FallThru;

	tabmrk,carret => SIGNAL Unimplemented;
	assignreal => SetDimnParam[cc.dimnparam, ScanLength[]];
	assignglue => SetGlueParam[cc.glueparam, ScanGlueSpec[]];
	def => MacroDef[cc.deftype];
	output => SetOutputRoutine[ScanToks[output]];
--	ddt => Debug;
	chcode => DoChng[cc.chngtype];
	setcount => DoSetCount[];
	advcount => DoAdvCount[];
	count => DoCount[];
	ifeven => DoIfEven[cc.iftype];
	ifT => DoIfT[];
	save => DoSave[];
	topbotmark => DoTopBotMark[cc.marktype];
	newaccent => SIGNAL Unimplemented;

	ENDCASE => SIGNAL FallThru;
	END;
	ENDLOOP;
	-- Now finish the AboveNoad, if one has been made.
	IF incompleatnoad#NIL THEN
		BEGIN
		incompleatnoad.denominator←[mlist[head.link]];
		mlist.last←head; head.link←NIL;
		END;
	RETURN;
	END;

EmptyField: PROCEDURE[f: MFieldPtr] RETURNS[BOOLEAN] = --INLINE--
	BEGIN
	WITH ff:f SELECT FROM
		box => RETURN[ff.box=NIL];
		ENDCASE => RETURN[FALSE];
	END;

DoLimsw: PROCEDURE[q: NoadPtr] = --INLINE--
	BEGIN
	WITH qq:q SELECT FROM
		common => WITH qqq:qq SELECT FROM
			scripted => WITH qqq SELECT FROM
				op => BEGIN limitswitch←NOT limitswitch; RETURN END;
				ENDCASE;
			ENDCASE;
		ENDCASE;
	TexErrorDefs.Error["Limit switch must follow math operator"];
	END;

ExtraAbove: PROCEDURE[type: AboveType] =
	BEGIN
	SELECT type FROM
		above => []←ScanLength[];
		atop,over => NULL;
		comb => BEGIN []←ScanDelim[]; []←ScanDelim[] END;
		ENDCASE => ERROR; -- bad AboveType
	TexErrorDefs.Error["Ambiguous; you need another { and }"];
	END;

ScanAbove: PROCEDURE[type: AboveType] RETURNS[AboveNoadPtr] =
	BEGIN
	p: AboveNoadPtr←MakeAboveNoad[];
	SELECT type FROM
		above => p.aboverule←ScanLength[];
		atop => NULL;
		over => p.aboverule←TexMathOpDefs.MathExPar[defaultrulethickness];
		comb => BEGIN p.ldelim←ScanDelim[]; p.rdelim←ScanDelim[] END;
		ENDCASE => ERROR; -- bad AboveType
	RETURN[p];
	END;

ScanVcenter: PROCEDURE[vc: VctrType] RETURNS[ScriptedNoadPtr] =
	BEGIN
	q: ScriptedNoadPtr;
	vlist: NodeListPtr←InitNodeList[];
	vhead: VHead←[vlist,pflag];
	ScanLB;
	NewSaveLevel[endvcenter];
	VMode[@vhead,TRUE]; -- scan a list in restricted vmode
	UnSave[endvcenter];
	q←MakeScriptedNoad[[box[TexPackDefs.VBox[vlist]]]];
	q.svariant←vctr[vc]; -- vc is vtop or vcenter
	RETURN[q];
	END;

ScanFormula: PROCEDURE[tlist: NodeListPtr, restricted: BOOLEAN] =
	BEGIN OPEN TexMathOpDefs;
	mlist: NoadListPtr←NIL;
	style: MathStyle←[norm,IF restricted THEN text ELSE disp];
	StartNoadAllocation;
	mlist←InitNoadList[];
	NewSaveLevel[mathcode]; MMode[mlist, restricted]; UnSave[mathcode];
	-- the third parameter to EvalMlist controls whether penalty nodes are
	-- inserted after top level Bin and Rel operators
	-- restricted math mode: text style, penalty nodes at possible breaks
	-- display math mode: disp style, no breaks allowed
	tlist.link←EvalMlist[FinishNoadList[mlist], style, restricted];
	FinishNoadAllocation;
	tlist.last←NIL; CompactList[tlist];
	END;

GetFormula: PUBLIC PROCEDURE[flist: NodeListPtr] =
	BEGIN
	ScanFormula[flist, TRUE]; -- scan a formula in restricted MMode
	END;

GetDisplay: PUBLIC PROCEDURE[dlist: NodeListPtr] RETURNS[eqno: BoxNodePtr] =
	BEGIN
	eqnobox←NIL; -- eqnobox will be set by MMode if it sees \eqno
	ScanFormula[dlist, FALSE]; -- scan a formula in display MMode
	RETURN[eqnobox];
	END;

MathQuad: PUBLIC PROCEDURE RETURNS[TexDefs.Dimn] =
	BEGIN
	RETURN[TexMathOpDefs.MathFontPar[quad,text]];
	END;

END.