-- TexMain.mesa
-- Mesa 6 version
-- Last changed by Doug Wyatt, September 23, 1980 5:32 PM

DIRECTORY
TexAlignDefs: FROM "TexAlignDefs",
TexDebugDefs: FROM "TexDebugDefs",
TexDefs: FROM "TexDefs",
TexErrorDefs: FROM "TexErrorDefs",
TexGlueDefs: FROM "TexGlueDefs",
TexIODefs: FROM "TexIODefs",
TexJustifyDefs: FROM "TexJustifyDefs",
TexMainDefs: FROM "TexMainDefs",
TexMathDefs: FROM "TexMathDefs",
TexNodeDefs: FROM "TexNodeDefs",
TexOutputDefs: FROM "TexOutputDefs",
TexPackDefs: FROM "TexPackDefs",
TexSynDefs: FROM "TexSynDefs",
TexTableDefs: FROM "TexTableDefs",
TexTokenDefs: FROM "TexTokenDefs",
ImageDefs: FROM "ImageDefs",
TimeDefs: FROM "TimeDefs";

TexMain: PROGRAM
IMPORTS TexAlignDefs,TexDebugDefs,TexErrorDefs,TexGlueDefs,
TexIODefs,TexJustifyDefs,TexMainDefs,TexMathDefs,TexNodeDefs,
TexOutputDefs,TexPackDefs,TexSynDefs,TexTableDefs,
ImageDefs,TimeDefs
EXPORTS TexMainDefs =
BEGIN OPEN TexMainDefs,TexTableDefs,TexNodeDefs,TexSynDefs,TexDefs;

CursorArray: TYPE = ARRAY [0..16) OF CARDINAL;

-- here’s the TEX cursor
texCursor: CursorArray = [
000000B, 176167B, 132062B, 030074B, 037734B, 033130B, 033014B, 033234B,
033636B, 033246B, 077167B, 003000B, 003100B, 007700B, 000000B, 000000B
];

cursorLoc: POINTER TO CursorArray = LOOPHOLE[431B];

Unimplemented: PUBLIC SIGNAL = CODE;
Continue: PUBLIC SIGNAL = CODE;
Reswitch: PUBLIC SIGNAL = CODE;

FallThru: PUBLIC SIGNAL = CODE;
Undefined: PUBLIC SIGNAL = CODE;
CantDoThat: PUBLIC SIGNAL[mode: ModeType, restricted: BOOLEAN] = CODE;

Herald: PROCEDURE =
BEGIN OPEN TexIODefs,TimeDefs;
time: STRING←[20];
AppendDayTime[time,UnpackDT[ImageDefs.BcdTime[]]];
Cr; Ws["Mesa\TEX of "L]; Ws[time]; Cr;
END;

MainControl: PUBLIC PROCEDURE =
BEGIN OPEN TexIODefs,TexErrorDefs;
page: CARDINAL←0;
cursorLoc↑ ← texCursor;
TexDebugDefs.ShowMem["entering MainControl"];
TexOutputDefs.InitOut;
Herald;
BEGIN ENABLE
BEGIN
EndOfTex => CONTINUE;
PageEnd => RESUME;
Undefined =>
BEGIN Error["Undefined control sequence"L]; RESUME END;
Unimplemented =>
BEGIN Error["This case not implemented"L]; RESUME END;
CantDoThat =>
BEGIN
BeginError; Ws["You can’t do that in "L];
WMode[mode, restricted]; Ws[" mode"L];
Error[EndError[]];
RESUME;
END;
UndefinedFont =>
BEGIN
PausingOnErrors[FALSE];
Error["Whoa--you have to define a font first"L];
CONTINUE;
END;
TexPackDefs.OverfullBox =>
BEGIN
IF CurTracing[].reportoverfull THEN
BEGIN OPEN TexIODefs;
Ps["Overfull box, "L]; Wn[excess]; Ws[" micas too "L];
Ws[SELECT box.dir FROM
hlist=>"wide"L, vlist=>"high"L, ENDCASE=>ERROR];
Wc[’:]; -- *** then display the box
END;
RESUME;
END;
END;
mainvlist: NodeListPtr ← InitNodeList[];
mainvhead: VHead←[mainvlist,pflag];
PausingOnErrors[TRUE];
VMode[@mainvhead, FALSE !EndOfTex => CONTINUE]; -- call the page builder
END;
TexOutputDefs.CloseOut[1];
TexDebugDefs.ShowMem["leaving MainControl"];
END;


-- ****************************
-- mode-independent commands
-- ****************************

Ddt: SIGNAL = CODE;

CommonCmd: PUBLIC PROCEDURE =
BEGIN
WITH cc:curchar SELECT curcmd FROM
undefined => SIGNAL Undefined;
tabmrk,carret => SIGNAL Unimplemented;
call => MacroCall[];
assignreal => {
t: DimnParamType=cc.dimnparam;
SetDimnParam[t, ScanLength[]];
};
assignglue => {
t: GlueParamType=cc.glueparam;
SetGlueParam[t, ScanGlueSpec[]];
};
def => MacroDef[cc.deftype];
output => SetOutputRoutine[ScanToks[output]];
ddt => SIGNAL Ddt;
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; -- couldn’t handle curcmd
END;

DoChng: PUBLIC PROCEDURE[type: ChngType] =
BEGIN
a,b: CARDINAL;
a←ScanNumber[]; -- which parameter or character code
GetNCTok; -- this token is ignored, it might be space or = or ←, etc.
IF curcmd=endv THEN TexErrorDefs.BackError["Missing } inserted"L];
b←ScanNumber[]; -- new value *** can this be negative?
SELECT type FROM
code => ChangeCode[a, b];
par => ChangePar[a, b];
ENDCASE => ERROR; -- bad ChngType
END;

savedbox: ARRAY Digit OF BoxNodePtr ← ALL[NIL];

nCharTypes: CARDINAL=13;
CharTypeIndex: TYPE = [0..nCharTypes);
mapCharType: ARRAY CharTypeIndex OF CharType =
[escape,lbrace,rbrace,mathbr,tabmrk,carret,macprm,
supmrk,submrk,ignore,spacer,letter,otherchar];

ChangeCode: PROCEDURE[char, val: CARDINAL] =
BEGIN
CharRange: TYPE = CARDINAL[LOOPHOLE[FIRST[Char]]..LOOPHOLE[LAST[Char]]];
IF char NOT IN CharRange THEN
BEGIN TexErrorDefs.Error["bad character code"L]; RETURN END;
IF val NOT IN CharTypeIndex THEN
BEGIN TexErrorDefs.Error["bad character type"L]; RETURN END;
SetChType[LOOPHOLE[char,Char], mapCharType[val]];
END;

nTexPars: CARDINAL=10;
TexParIndex: TYPE = [0..nTexPars);
mapTexParType: ARRAY TexParIndex OF TexParamType =
[tracing,jpar,hpen,penpen,wpen,bpen,mbpen,mrpen,ragged,disppen];

ChangePar: PROCEDURE[par, val: CARDINAL] =
BEGIN
typ: TexParamType;
texpar: TexPar;
IF par NOT IN TexParIndex THEN
BEGIN TexErrorDefs.Error["bad texpar code"L]; RETURN END;
typ←mapTexParType[par];
WITH tp:texpar SELECT typ FROM
jpar,hpen,penpen,wpen,bpen,mbpen,mrpen,disppen => tp.penalty←val;
ragged => tp.ragged←val;
tracing => tp.tracing←LOOPHOLE[val,TraceInfo];
ENDCASE => ERROR; -- bad TexParamType
SetTexParam[typ, texpar];
END;

DoSetCount: PUBLIC PROCEDURE =
BEGIN
d: Digit←ScanDigit[];
SetKount[d,ScanInteger[]];
END;

DoAdvCount: PUBLIC PROCEDURE =
BEGIN
d: Digit←ScanDigit[];
inc: INTEGER;
oldkount: INTEGER←Kount[d];
IF ScanString["by"L] THEN inc←ScanInteger[]
ELSE inc←(IF oldkount<0 THEN -1 ELSE 1);
SetKount[d,oldkount+inc];
END;

DoCount: PUBLIC PROCEDURE =
BEGIN
d: Digit←ScanDigit[];
InsNum[Kount[d], decimal];
END;

DoIfEven: PUBLIC PROCEDURE[t: IfType] =
BEGIN
d: Digit←ScanDigit[];
SELECT t FROM
even => ScanCond[(Kount[d] MOD 2)=0];
pos => ScanCond[Kount[d]>0];
ENDCASE => ERROR; -- bad IfType
END;

DoIfT: PUBLIC PROCEDURE =
BEGIN
t: TexTokenDefs.Token;
GetNCTok; t←curtok;
GetNCTok; ScanCond[curtok=t];
-- *** several tests for endv have been omitted here
-- Although an endv code is unlikely here, it is best to make sure that
-- endv can’t be gobbled up under any circumstances
END;

ScanCond: PROCEDURE[b: BOOLEAN] =
BEGIN
IF b THEN BEGIN ScanLB; NewSaveLevel[trueend] END
ELSE
BEGIN
PassBlock; -- skip the true part
GetNCTok;
IF curcmd#elsecode THEN TexErrorDefs.BackError["\else required here"L];
ScanLB; NewSaveLevel[falseend];
END;
END;

TrueEnd: PUBLIC PROCEDURE =
BEGIN
UnSave[trueend]; -- unwind the savestack
ScanNonSpacer; -- find the next non-spacer, which should be \else
IF curcmd#elsecode THEN TexErrorDefs.BackError["Missing \else inserted"L];
PassBlock; -- bypass the else clause
END;

FalseEnd: PUBLIC PROCEDURE =
BEGIN
UnSave[falseend]; -- just unwind the savestack
ScanSpacer; -- and ignore any spacers after }
END;

DoSave: PUBLIC PROCEDURE =
BEGIN
d: Digit←ScanDigit[];
oldbox, newbox: BoxNodePtr;
newbox←ScanBox[]; -- may SIGNAL Reswitch
IF (oldbox←savedbox[d])#NIL THEN DsNode[oldbox];
savedbox[d]←newbox;
END;

DoTopBotMark: PUBLIC PROCEDURE[t: MarkType] =
BEGIN
p: TexTokenDefs.TokenListPtr←Mark[t];
IF p#NIL THEN BEGIN InsRCList[p]; NewSaveLevel[simpleblock] END;
END;

DoFntFam: PUBLIC PROCEDURE[mfont: TexMathDefs.MFont] =
BEGIN OPEN TexMathDefs;
msize: MathSize;
f: Font;
IF mfont=ex THEN
BEGIN
f←ScanFont[];
FOR msize IN MathSize
DO
TexTableDefs.SetMathFont[MFTIndex[mfont,msize],f];
ENDLOOP;
END
ELSE
BEGIN
FOR msize IN MathSize
DO
f←ScanFont[];
TexTableDefs.SetMathFont[MFTIndex[mfont,msize],f];
ENDLOOP;
END;
END;

ScanPenltyNode: PUBLIC PROCEDURE RETURNS[NodePtr] =
BEGIN
n: Penalty←ScanInteger[];
-- ***** test for overflow/underflow?
RETURN[MakePenaltyNode[n]];
END;

-- ***** miscellaneous procedures

InterLineGlue: PUBLIC PROCEDURE[delta: Dimn, p: TexGlueDefs.GluePtr]
RETURNS[TexGlueDefs.GluePtr] =
BEGIN OPEN TexGlueDefs;
-- returns a pointer to glue specification that makes up for
-- baseline distance deficiency of delta, when p points to the
-- \baselineskip glue
q: GluePtr;
IF delta>=DimnParam[lineskiplimit] THEN
BEGIN -- the normal baseline spacing was not exceeded
q←MakeGlue[]; q.space←delta; q.flex←p.flex;
END
ELSE -- use lineskip glue if baseline distance is already large
q←GlueParam[lineskip];
RETURN[q];
END;

ScanRuleNode: PUBLIC PROCEDURE RETURNS[NodePtr] =
BEGIN
w,h,d: Dimn;
[width: w, height: h, depth: d]←ScanRuleSpec[];
RETURN[MakeRuleNode[width: w, height: h, depth: d]];
END;

ScanGlueSpec: PUBLIC PROCEDURE RETURNS[TexGlueDefs.GluePtr] =
BEGIN OPEN TexGlueDefs;
p: GluePtr←MakeGlue[];
ScanGlue[p];
RETURN[p];
END;

ScanMovedBox: PUBLIC PROCEDURE[neg: BOOLEAN] RETURNS[BoxNodePtr] =
BEGIN
shft: Dimn←ScanLength[];
box: BoxNodePtr←ScanBox[];
IF neg THEN shft←-shft;
box.shiftamt←shft;
RETURN[box];
END;

SkipGlue: PUBLIC PROCEDURE[gluetype: TexGlueDefs.GlueType] RETURNS[NodePtr] =
BEGIN OPEN TexGlueDefs;
p: GluePtr;
IF gluetype=skip THEN p←ScanGlueSpec[] ELSE p←CommonGlue[gluetype];
RETURN[MakeGlueNode[p]];
END;

DoTopBotIns: PUBLIC PROCEDURE[tb: TopBotType, dir: Direction,
Store: PROCEDURE[NodePtr]] =
BEGIN OPEN TexGlueDefs;
ec: EndingCode←SELECT tb FROM top=>topinsend,bot=>botinsend,ENDCASE=>ERROR;
inslist: NodeListPtr←InitNodeList[];
vhead: VHead←[inslist,pflag];
p: InsNodePtr;
b: BoxNodePtr;
q: GluePtr;
flexsums: FlexSums;
TotalFlex: PROCEDURE[sums: FlexSumsPtr]
RETURNS[f: ARRAY FlexDir OF Flex] =
BEGIN
dir: FlexDir;
FOR dir IN FlexDir DO f[dir]←DominantFlex[@sums[dir]] ENDLOOP;
END;
ScanLB; -- *** perhaps this should not skip spacers
NewSaveLevel[ec]; VMode[@vhead, TRUE]; UnSave[ec];
SELECT tb FROM
top => -- append topskip glue at end of vlist
BEGIN
p: NodePtr←MakeGlueNode[GlueParam[topskip]];
StoreNode[inslist, p];
END;
bot => -- insert botskip glue at beginning of vlist
BEGIN
p: NodePtr←MakeGlueNode[GlueParam[botskip]];
p.link←inslist.link; inslist.link←p;
END;
ENDCASE => ERROR; -- bad TopBotType
b←TexPackDefs.VPackage[list: inslist, desiredheight: 0, xpand: TRUE,
page: FALSE, flexsums: @flexsums];
q←MakeGlue[]; AddGlueLink[q];
q.space←b.height+b.depth;
q.flex←TotalFlex[@flexsums];
p←MakeInsNode[tb, dir, b.head, q]; b.head←NIL; DsNode[b];
Store[p];
END;

ScanLeaders: PUBLIC PROCEDURE RETURNS[NodePtr] =
BEGIN
p: NodePtr;
GetNCNext[];
WITH cc:curchar SELECT curcmd FROM
hrule,vrule => p←ScanRuleNode[];
box => p←GetBox[cc.boxtype];
ENDCASE => BEGIN
TexErrorDefs.Error["A box or rule specification was supposed to be here"L];
ERROR Reswitch;
END;
RETURN[MakeLeaderNode[p]];
END;

ScanBox: PROCEDURE RETURNS[BoxNodePtr] =
BEGIN
GetNCNext[];
WITH cc:curchar SELECT curcmd FROM
box => RETURN[GetBox[cc.boxtype]];
ENDCASE;
TexErrorDefs.Error["A box specification was supposed to be here"L];
ERROR Reswitch;
END;

GetBox: PUBLIC PROCEDURE[type: BoxType] RETURNS[p: BoxNodePtr] =
BEGIN
SELECT type FROM
hbox,vbox =>
BEGIN len: Dimn; xpand, break: BOOLEAN;
list: NodeListPtr←InitNodeList[];
size: Dimn←DimnParam[IF type=vbox THEN vsize ELSE hsize];
[len,xpand,break]←ScanSpec[type=hbox, size];
IF break THEN SetHangIndent[nullHang]; -- reset hanging indent
NewSaveLevel[justend];
SELECT type FROM
vbox =>
BEGIN vhead: VHead←[list,pflag];
VMode[@vhead, TRUE];
p←TexPackDefs.VPack[list,len,xpand];
END;
hbox =>
BEGIN hhead: HHead←[list,sfOne];
HMode[@hhead, TRUE];
IF break THEN p←Justify[list,len]
ELSE p←TexPackDefs.HPack[list,len,xpand];
END;
ENDCASE => ERROR; -- bad BoxType
UnSave[justend];
-- box shifting to be done by caller
END;
box =>
BEGIN d: Digit←ScanDigit[];
p←savedbox[d];
IF p=NIL THEN p←NullBox[];
savedbox[d]←NIL;
END;
page => p←Page[];
ENDCASE => ERROR; -- bad BoxType
END;

Justify: PROCEDURE[q: NodeListPtr, width: Dimn] RETURNS[BoxNodePtr] =
BEGIN OPEN TexGlueDefs,TexJustifyDefs;
pflag: Dimn=FIRST[Dimn];
prevdepth: Dimn←pflag;
begin: CARDINAL; width0,width1,indent0,indent1: Dimn;
blskip: GluePtr←GlueParam[baselineskip];
nlines: CARDINAL←0;
vlist: NodeListPtr←InitNodeList[];
LineWidth: LineWidthProc =
BEGIN RETURN[IF line<begin THEN width0 ELSE width1] END;
NewLine: NewLineProc =
BEGIN
IF prevdepth#pflag THEN
BEGIN
StoreNode[vlist, MakeGlueNode[InterLineGlue[
blskip.space-prevdepth-linebox.height,blskip]]];
END;
linebox.shiftamt←IF nlines<begin THEN indent0 ELSE indent1;
StoreNode[vlist, linebox]; nlines←nlines+1;
prevdepth←linebox.depth;
FreeNodeList[inserts];
END;
-- note that this uses CurHangIndent BEFORE UnSave is done
[begin,width0,width1,indent0,indent1]←HangVals[CurHangIndent[],width];
StoreNode[q, MakeGlueNode[CommonGlue[fil]]];
[]←Justification[list: q, LineWidth: LineWidth, NewLine: NewLine];
RETURN[TexPackDefs.VBox[vlist]];
END;

HangVals: PUBLIC PROCEDURE[hang: HangSpec, width: Dimn]
RETURNS[begin: CARDINAL, width0,width1,indent0,indent1: Dimn] =
BEGIN
hwidth: Dimn←width-ABS[hang.width];
hindent: Dimn←MAX[hang.width,0];
IF hang.first THEN RETURN[hang.begin,hwidth,width,hindent,0]
ELSE RETURN[hang.begin,width,hwidth,0,hindent];
END;

CheckAlignment: PUBLIC PROCEDURE =
BEGIN
IF NOT TexAlignDefs.alignmentInProgress THEN
BEGIN
TexErrorDefs.Error["There’s no \halign or \valign going on"];
ERROR Reswitch;
END;
END;

MissingBrace: PUBLIC PROCEDURE =
BEGIN
SELECT SaveCode[] FROM
mathcode => MissingMathbr;
mathleft => MissingRight;
ENDCASE => MissingRB;
END;

MissingMathbr: PUBLIC PROCEDURE =
BEGIN
TexErrorDefs.BackError["Missing $ inserted"];
curcmd←mathbr; curchar←[mathbr[’$]]; curtok←[undefined[]];
ERROR Reswitch;
END;

MissingRight: PUBLIC PROCEDURE =
BEGIN
TexErrorDefs.BackError["Missing \right. inserted"];
InsToken[[otherchar[’.]]];
curcmd←leftright; curchar←[leftright[right]]; curtok←[undefined[]];
ERROR Reswitch;
END;

MissingRB: PUBLIC PROCEDURE =
BEGIN
TexErrorDefs.BackError["Missing } inserted"];
curcmd←rbrace; curchar←[rbrace[’}]]; curtok←[undefined[]];
ERROR Reswitch;
END;

-- write the name of the specified mode on the current output stream
WMode: PROCEDURE[m: ModeType, r: BOOLEAN] =
BEGIN OPEN TexIODefs;
SELECT m FROM
v,h => IF r THEN Ws["restricted "L];
m => IF NOT r THEN Ws["display "L];
ENDCASE;
SELECT m FROM
v => Ws["vertical"L];
h => Ws["horizontal"L];
m => Ws["math"L];
ENDCASE;
END;

END.