-- 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.