-- TexGet.mesa
-- Mesa 6 version
-- Last changed by Doug Wyatt, September 23, 1980 7:19 PM

DIRECTORY
TexDefs: FROM "TexDefs",
TexErrorDefs: FROM "TexErrorDefs",
TexFileDefs: FROM "TexFileDefs",
TexHashDefs: FROM "TexHashDefs"
USING[maxidsize,IdLookup,NoNewControlSeq],
TexInpDefs: FROM "TexInpDefs",
TexIODefs: FROM "TexIODefs",
TexMemDefs: FROM "TexMemDefs",
TexStringDefs: FROM "TexStringDefs"
USING[AppendChar,AppendString,CopyString],
TexSynDefs: FROM "TexSynDefs",
TexTableDefs: FROM "TexTableDefs"
USING[Cmd,CmdInfo,HashIndex,ChType,SetChType,HashEquiv,CurTracing],
TexTokenDefs: FROM "TexTokenDefs"
USING[Token,TokenPtr,TokenListPtr,MakeTokenLEntry,DsList,DelRCLink],
AltoFileDefs USING [CFA],
Ascii USING [NUL,TAB,LF,CR,SP],
MiscDefs USING [CommandLineCFA],
SegmentDefs USING [FindFile, Read],
StreamDefs;

TexGet: PROGRAM
IMPORTS TexErrorDefs,TexFileDefs,TexHashDefs,TexIODefs,TexMemDefs,
TexStringDefs,TexSynDefs,TexTableDefs,TexTokenDefs,
MiscDefs,SegmentDefs,StreamDefs
EXPORTS TexInpDefs,TexSynDefs =
BEGIN OPEN TexErrorDefs,TexTableDefs,TexTokenDefs,TexInpDefs;

Char: TYPE = TexDefs.Char;

--Global variables to be exported to TEXSEM
hashentry: PUBLIC HashIndex;
curcmd: PUBLIC Cmd;
curchar: PUBLIC CmdInfo;
curtok: PUBLIC Token;

PageEnd: PUBLIC SIGNAL = CODE;

-- The input stack

instack: PUBLIC POINTER TO InStack;
inptr: PUBLIC [0..instacksize];
instate: PUBLIC InStackEntry;

parstack,pstack: PUBLIC POINTER TO ParStack;
parptr: PUBLIC ParStackIndex;

firstfile: STRING←NIL; -- name of first input file

keyboard: StreamDefs.KeyboardHandle;

undefinedChar: Char=0C;
escapechar: Char←undefinedChar;

SetEscapeChar: PROC[char: CHARACTER] = {
escapechar←char;
SetChType[escapechar,escape];
TexIODefs.SetEsc[escapechar];
};

PushInput: PUBLIC PROCEDURE =
BEGIN
IF inptr>=instacksize THEN ERROR OverFlow["instack"L]
ELSE BEGIN instack[inptr]←instate; inptr←inptr+1; END;
END -- of PushInput --;

PopInput: PROCEDURE =
BEGIN
WITH instate SELECT FROM
charlist =>
BEGIN
TexMemDefs.FreeString[inbuf]; TexMemDefs.FreeString[filename];
END;
tokenlist => NULL;
ENDCASE;
inptr←inptr-1; instate←instack[inptr];
END -- of PopInput --;

InsRCList: PUBLIC PROCEDURE[l: TokenListPtr] =
BEGIN
PushInput[]; instate←[tokenlist[l.link,[prune[parptr,l]]]];
l.refs←l.refs+1;
END -- of InsRCList --;

InsList: PUBLIC PROCEDURE[p: TokenPtr] =
BEGIN
PushInput[]; instate←[tokenlist[p,[destroy[p]]]];
END -- of InsList --;

InsUPart: PUBLIC PROCEDURE[p: TokenPtr] =
BEGIN
PushInput[]; instate←[tokenlist[p,[endulist[p]]]];
END -- of InsUPart --;

InsVPart: PUBLIC PROCEDURE[p: TokenPtr] =
BEGIN
PushInput[]; instate←[tokenlist[p,[endvlist[p]]]];
END -- of InsVPart --;

InsToken: PUBLIC PROCEDURE[token: Token] =
BEGIN
InsList[MakeTokenLEntry[token]];
END;

-- accessing user’s files

CharListState: TYPE = POINTER TO charlist InStackEntry;

Eof: SIGNAL = CODE;
Pop: SIGNAL = CODE;

ibctx: CARDINAL=20; -- number of characters to retain for context

GetChar: PROCEDURE[cls: CharListState]
RETURNS[Char] = --INLINE--
BEGIN OPEN cls;
IF ibptr+1=ibmark THEN Input[cls];
RETURN[inbuf[ibptr←ibptr+1]];
END;

TerminateLine: PROCEDURE[cls: CharListState] = --INLINE--
BEGIN OPEN cls;
UNTIL ibmark=inbuf.length DO Input[cls] ENDLOOP;
ibptr←(ibmark←inbuf.length)-1;
END;

Input: PROCEDURE[cls: CharListState] =
BEGIN OPEN TexIODefs, cls;
i,j,k: CARDINAL; c: CHARACTER;
IF ibmark=inbuf.length THEN
BEGIN
IF inbuf[ibptr]=FF THEN BEGIN page←page+1; line←0; Sp; Wn[page] END;
line←line+1; ibptr←(k←0)-1;
END
ELSE
BEGIN
k←inbuf.length-(j←ibmark-ibctx);
FOR i IN[0..k) DO inbuf[i]←inbuf[j+i] ENDLOOP;
ibptr←ibptr-j;
END;
IF channel=keyboard THEN
BEGIN OPEN TexIODefs;
IF inptr>0 AND inbuf.length>0 THEN ERROR Pop;
IF ibptr#177777B THEN ERROR;
Pc[’*]; ReadLine[inbuf]; TexStringDefs.AppendChar[inbuf,CR];
ibmark←inbuf.length;
IF escapechar=undefinedChar AND inbuf.length#1 THEN
SetEscapeChar[inbuf[0]];
RETURN;
END;
FOR i IN [k..inbufsize)
DO
c←inbuf[i]←channel.get[channel !StreamDefs.StreamError =>
IF channel.endof[channel] THEN ERROR Eof];
SELECT c FROM
CR => GOTO EndLine;
FF => BEGIN SIGNAL PageEnd; GOTO EndLine END;
ENDCASE;
REPEAT
EndLine => ibmark←inbuf.length←i+1;
FINISHED => ibmark←(inbuf.length←inbufsize)-ibctx;
ENDLOOP;
IF CurTracing[].showinput THEN Ps[inbuf];
END -- of Input --;

InputFile: PUBLIC PROCEDURE =
BEGIN OPEN TexIODefs,StreamDefs,TexFileDefs;
filename: STRING←[filenamesize];
fh: FileHandle;
TexSynDefs.ScanFileName[filename]; DefaultExtension[filename,"TEX"L];
fh←OpenFile[filename,read !LookupFailed =>
BEGIN
BeginError; Ws["Lookup failed on "L]; Ws[filename];
Error[EndError[]]; GOTO Fail;
END];
Sp; Wc[’(]; Ws[filename];
PushInput[];
instate←[charlist[inbuf: TexMemDefs.AllocString[inbufsize],
ibptr: 0, ibmark: 1,
filename: TexStringDefs.CopyString[filename],
state: newline, page: 0, line: 0,
channel: CreateByteStream[fh,Read]]];
WITH instate SELECT FROM
charlist => BEGIN inbuf[ibptr]←FF; inbuf.length←ibmark END;
ENDCASE => ERROR;
IF firstfile=NIL THEN firstfile←TexStringDefs.CopyString[filename];
EXITS Fail => NULL;
END -- of InputFile --;

AppendOutputFileName: PUBLIC PROCEDURE[s,default: STRING] =
BEGIN OPEN TexStringDefs;
IF firstfile#NIL THEN TexFileDefs.NameAndExtension[firstfile,s,NIL]
ELSE TexStringDefs.AppendString[s,default];
END;

-- the basic input procedure getnext and its cousins

chr: Char;

GetNext: PUBLIC PROCEDURE =
BEGIN OPEN TexIODefs;
DO
WITH s:instate SELECT FROM
charlist =>
BEGIN OPEN s;
ENABLE
BEGIN
Eof =>
BEGIN
SIGNAL PageEnd; Wc[’)];
channel.destroy[channel];
PopInput[];
curcmd←parend; curchar←[parend[]];
CONTINUE; -- end-of-file ends a paragraph
END;
Pop => BEGIN PopInput[]; LOOP END;
END;
curtok←[undefined[]];
SELECT ChType[chr←GetChar[@s]] FROM
escape => BEGIN
ControlSeq[@s]; state←skipblanks;
curtok←[ctrlseq[hashentry]];
[curcmd,curchar]←HashEquiv[hashentry] END;
lbrace => BEGIN
state←midline;
curcmd←lbrace; curchar←[lbrace[chr]] END;
rbrace => BEGIN
state←midline;
curcmd←rbrace; curchar←[rbrace[chr]] END;
ignore => LOOP;
spacer => IF state#midline THEN LOOP
ELSE BEGIN state←skipblanks;
curcmd←spacer; curchar←[spacer[’ ]] END;
carret =>
BEGIN
TerminateLine[@s];
SELECT state FROM
midline => BEGIN state←newline;
curcmd←spacer; curchar←[spacer[’ ]] END;
skipblanks => BEGIN state←newline; LOOP; END;
newline => BEGIN curcmd←parend; curchar←[parend[]]; END;
ENDCASE;
END;
mathbr => BEGIN state←midline;
curcmd←mathbr; curchar←[mathbr[chr]] END;
tabmrk => BEGIN state←midline;
curcmd←tabmrk; curchar←[tabmrk[chr]] END;
macprm => BEGIN state←midline;
curcmd←macprm; curchar←[macprm[chr]] END;
supmrk => BEGIN state←midline;
curcmd←supmrk; curchar←[supmrk[chr]] END;
submrk => BEGIN state←midline;
curcmd←submrk; curchar←[submrk[chr]] END;
letter => BEGIN state←midline;
curcmd←letter; curchar←[letter[chr]] END;
otherchar => BEGIN state←midline;
curcmd←otherchar; curchar←[otherchar[chr]] END;
ENDCASE => ERROR Confusion;
END;
tokenlist =>
BEGIN OPEN s;
IF loc#NIL THEN
BEGIN
curtok←loc.token; loc←loc.link;
WITH x:curtok SELECT FROM
ctrlseq => hashentry←x.index;
outpar => BEGIN
PushInput[];
WITH y:recovery SELECT FROM
prune => loc←parstack[y.p+x.paramnum-1];
ENDCASE => ERROR Confusion;
recovery←[donothing[loc]];
LOOP;
END;
ENDCASE;
[curcmd,curchar]←UnpackTok[curtok];
END
ELSE BEGIN
WITH recov:recovery SELECT FROM
donothing,endulist,endvlist => NULL;
destroy => DsList[recov.l];
prune => BEGIN pn: CARDINAL;
DelRCLink[recov.l];
pn←recov.p;
WHILE parptr>pn DO
parptr←parptr-1;
DsList[parstack[parptr]];
ENDLOOP;
END;
ENDCASE;
PopInput[];
LOOP;
END;
END;
ENDCASE;
EXIT;
ENDLOOP;
END -- of GetNext --;


GetNCNext: PUBLIC PROCEDURE =
BEGIN OPEN TexHashDefs;
DO
NoNewControlSeq[TRUE]; GetNext[]; NoNewControlSeq[FALSE];
SELECT curcmd FROM
undefined => Error["Undefined control sequence"L];
call => TexSynDefs.MacroCall[];
ENDCASE => EXIT;
ENDLOOP;
END -- of GetNCNext --;

SetCurtok: PROCEDURE =
BEGIN
IF curtok.cmd=undefined THEN WITH cc:curchar SELECT curcmd FROM
macprm => curtok←[macprm[cc.char]];
lbrace => curtok←[lbrace[cc.char]];
rbrace => curtok←[rbrace[cc.char]];
mathbr => curtok←[mathbr[cc.char]];
tabmrk => curtok←[tabmrk[cc.char]];
supmrk => curtok←[supmrk[cc.char]];
submrk => curtok←[submrk[cc.char]];
spacer => curtok←[spacer[cc.char]];
letter => curtok←[letter[cc.char]];
otherchar => curtok←[otherchar[cc.char]];
parend => curtok←[parend[]];
endv => curtok←[endv[]];
ENDCASE => ERROR Confusion;
END;

GetTok: PUBLIC PROCEDURE =
BEGIN
GetNext[]; SetCurtok;
SELECT curcmd FROM
parend => curtok←[parend[]]; -- make \par and <cr><cr> equivalent
endv => -- *** does this belong here?
BEGIN
BackError["Missing } inserted"L];
[curcmd,curchar]←UnpackTok[curtok←[rbrace[’}]]];
END;
ENDCASE;
END -- of GetTok --;

GetNCTok: PUBLIC PROCEDURE =
BEGIN OPEN TexHashDefs;
DO
NoNewControlSeq[TRUE]; GetNext[]; NoNewControlSeq[FALSE]; SetCurtok;
SELECT curcmd FROM
undefined => Error["Undefined control sequence"L];
call => TexSynDefs.MacroCall[];
parend => BEGIN curtok←[parend[]]; RETURN END;
ENDCASE => RETURN;
ENDLOOP;
END -- of GetNCTok --;

UnpackTok: PUBLIC PROCEDURE[token: Token] RETURNS[cmd: Cmd, info: CmdInfo] =
BEGIN
cmd←token.cmd;
WITH tok:token SELECT FROM
ctrlseq => [cmd,info]←HashEquiv[tok.index];
macprm => info←[macprm[tok.char]];
lbrace => info←[lbrace[tok.char]];
rbrace => info←[rbrace[tok.char]];
mathbr => info←[mathbr[tok.char]];
tabmrk => info←[tabmrk[tok.char]];
supmrk => info←[supmrk[tok.char]];
submrk => info←[submrk[tok.char]];
spacer => info←[spacer[tok.char]];
letter => info←[letter[tok.char]];
otherchar => info←[otherchar[tok.char]];
parend => info←[parend[]];
endv => info←[endv[]];
outpar,match => ERROR;
ENDCASE;
END;

ControlSeq: PROCEDURE[cls: CharListState] =
BEGIN OPEN TexHashDefs, cls;
id: STRING←[maxidsize];
BEGIN c: Char;
TexStringDefs.AppendChar[id, c←GetChar[cls]];
IF ChType[c]#letter THEN GOTO Done; -- single non-letter
WHILE ChType[c←GetChar[cls]]=letter
DO
IF c IN[’A..’Z] THEN c←c+(’a-’A); -- change upper to lower case
TexStringDefs.AppendChar[id,c];
ENDLOOP;
ibptr←ibptr-1;
EXITS Done => NULL END;
hashentry←IdLookup[id];
END -- of ControlSeq --;

BackInput: PUBLIC PROCEDURE =
BEGIN SetCurtok; InsToken[curtok] END;

GetInit: PROCEDURE =
BEGIN OPEN TexMemDefs;
instack←AllocMem[SIZE[InStack]];
parstack←AllocMem[SIZE[ParStack]];
pstack←AllocMem[SIZE[ParStack]];
keyboard←StreamDefs.GetDefaultKey[];
instate←[charlist[inbuf: AllocString[inbufsize], ibptr: 0, ibmark: 1,
filename: NIL, state: newline, page: 0, line: 0, channel: keyboard]];
WITH instate SELECT FROM
charlist => {
GetCommandLine[inbuf];
TexStringDefs.AppendChar[inbuf,TexIODefs.CR];
IF inbuf.length>1 THEN {
SetEscapeChar[inbuf[0]];
ibptr←ibptr-1; ibmark←inbuf.length;
};
};
ENDCASE => ERROR;
inptr←0; parptr←0;
firstfile←NIL;
escapechar←undefinedChar;
END -- of InitIn --;

GetCommandLine: PROC[s: STRING] = {
cfa: POINTER TO AltoFileDefs.CFA←MiscDefs.CommandLineCFA[];
stream: StreamDefs.StreamHandle←StreamDefs.CreateByteStream[
file: SegmentDefs.FindFile[@cfa.fp], access: SegmentDefs.Read];
skip: BOOLEAN←TRUE;
StreamDefs.JumpToFA[stream, @cfa.fa];
UNTIL stream.endof[stream] DO
c: CHARACTER=stream.get[stream];
IF skip THEN SELECT c FROM
Ascii.NUL,Ascii.TAB,Ascii.LF,Ascii.CR,Ascii.SP => LOOP;
ENDCASE => skip←FALSE;
TexStringDefs.AppendChar[s,c];
ENDLOOP;
stream.destroy[stream];
};

-- initialization
GetInit;

END -- of TexGet --.