-- TexScan.mesa

-- last written by Doug Wyatt, January 19, 1980  12:20 AM

DIRECTORY
	TexDefs: FROM "TexDefs"
		USING[Char,Font,Dimn,nilDimn,HangSpec,Digit,NumberStyle],
	TexErrorDefs: FROM "TexErrorDefs",
	TexFileDefs: FROM "TexFileDefs",
	TexFontDefs: FROM "TexFontDefs",
	TexGlueDefs: FROM "TexGlueDefs",
	TexMathDefs: FROM "TexMathDefs",
	TexStringDefs: FROM "TexStringDefs",
	TexSynDefs: FROM "TexSynDefs",
	TexTableDefs: FROM "TexTableDefs",
	TexTokenDefs: FROM "TexTokenDefs",
	InlineDefs: FROM "InlineDefs";

TexScan: PROGRAM
IMPORTS TexErrorDefs,TexFileDefs,TexFontDefs,TexStringDefs,TexSynDefs,
	TexTableDefs,TexTokenDefs,InlineDefs
EXPORTS TexSynDefs =

BEGIN OPEN TexErrorDefs,TexSynDefs,TexGlueDefs,TexDefs;

nbrlength: INTEGER←0;
nbrsign: CHARACTER←'+;

ScanSpacer: PUBLIC PROCEDURE = --INLINE--
	BEGIN
	GetNCTok[]; IF curcmd#spacer THEN BackInput;
	END;

ScanNonSpacer: PUBLIC PROCEDURE = --INLINE--
	BEGIN
	DO GetNCTok[]; IF curcmd#spacer THEN EXIT ENDLOOP;
	END;

ScanDigit: PUBLIC PROCEDURE RETURNS[Digit] =
	BEGIN d: CHARACTER;
	GetNCTok[];
	WITH curtok SELECT FROM
		otherchar => d←char;
		ENDCASE => d←0C;
	IF d NOT IN ['0..'9] THEN
		BEGIN
		BackError["Missing digit (0 to 9), 0 inserted"L];
		d←'0;
		END;
	ScanSpacer[];
	RETURN[d-'0];
	END -- of ScanDigit --;

ScanLB: PUBLIC PROCEDURE =
	BEGIN
	ScanNonSpacer;
	IF curcmd#lbrace THEN BackError["Missing { inserted"L];
	END;

ScanString: PUBLIC PROCEDURE[s: STRING] RETURNS[BOOLEAN] =
	BEGIN OPEN TexTokenDefs;
	q: TokenPtr; i,j: CARDINAL;
	temphead: TokenLEntry←nilTokenLEntry;
	FOR i IN [0..s.length)
		DO
		GetNCTok[];
		IF NOT CurCharMatches[s[i]] THEN
			BEGIN
			q←@temphead;
			FOR j IN [0..i) DO StoreTok[@q,MakeCharTok[s[j]]] ENDLOOP;
			StoreTok[@q,curtok];
			InsList[temphead.link];
			RETURN[FALSE];
			END;
		ENDLOOP;
	RETURN[TRUE];
	END -- of ScanString --;

MakeCharTok: PROCEDURE[c: Char] RETURNS[t: TexTokenDefs.Token] =
	BEGIN
	SELECT TexTableDefs.ChType[c] FROM
		macprm => t←[macprm[c]];
		mathbr => t←[mathbr[c]];
		tabmrk => t←[tabmrk[c]];
		supmrk => t←[supmrk[c]];
		submrk => t←[submrk[c]];
		spacer => t←[spacer[c]];
		letter => t←[letter[c]];
		otherchar => t←[otherchar[c]];
		ENDCASE => ERROR Confusion;
	RETURN[t];
	END;

CurCharMatches: PROCEDURE[c: Char] RETURNS[BOOLEAN] =
	BEGIN
	WITH cc:curchar SELECT curcmd FROM
		macprm,lbrace,rbrace,mathbr,tabmrk,
		 supmrk,submrk,spacer,letter,otherchar => RETURN[cc.char=c];
		ENDCASE;
	RETURN[FALSE];
	END;

ScanLongNumber: PROCEDURE RETURNS[LONG CARDINAL] =
	BEGIN
	bign: LONG CARDINAL;
	nbrlength←0; nbrsign←'+;
	ScanNonSpacer;
	SELECT curtok FROM
		[otherchar['-]] => BEGIN nbrsign←'-; ScanNonSpacer END;
		[otherchar['+]] => ScanNonSpacer;
		ENDCASE;
	WITH cc:curchar SELECT curcmd FROM
		count => BEGIN bign←TexTableDefs.Kount[ScanDigit[]]; GetNCTok END;
		letter => BEGIN bign←LOOPHOLE[cc.char,CARDINAL]; GetNCTok END;
		ENDCASE =>
			BEGIN
			digit: BOOLEAN; d: CARDINAL;
			radix: CARDINAL←10;
			IF curtok=[otherchar['']] THEN BEGIN radix←8; GetNCTok END;
			bign←0;
				DO
				[digit,d]←CurDigit[]; IF NOT digit THEN EXIT;
				bign←radix*bign+d; nbrlength←nbrlength+1; GetNCTok;
				ENDLOOP;
			END;
	IF curcmd#spacer THEN BackInput;
	RETURN[bign];
	END;

ScanNumber: PUBLIC PROCEDURE RETURNS[n: CARDINAL] =
	BEGIN
	bign: LONG CARDINAL←ScanLongNumber[];
	IF InlineDefs.HighHalf[bign]>0 THEN
		BEGIN Error["Number bigger than 65535"L]; bign←LAST[CARDINAL] END;
	RETURN[InlineDefs.LowHalf[bign]];
	END;

CurDigit: PROCEDURE RETURNS[isdigit: BOOLEAN, d: [0..9]] =
	BEGIN
	WITH cc:curchar SELECT curcmd FROM
		otherchar => IF cc.char IN['0..'9] THEN RETURN[TRUE,cc.char-'0];
		ENDCASE;
	RETURN[FALSE,0];
	END;

ScanFrac: PROCEDURE RETURNS[n: CARDINAL] =
	BEGIN OPEN InlineDefs;
	pofTen: CARDINAL←1; longn: LongNumber;
	digit: BOOLEAN; d: CARDINAL;
	n←0;
	ScanNonSpacer[];
		DO
		[digit,d]←CurDigit[];
		IF NOT digit THEN EXIT;
		IF pofTen<=1000 THEN
			BEGIN n←10*n+d; pofTen←pofTen*10 END;
		GetNCTok[];
		ENDLOOP;
	IF curcmd#spacer THEN BackInput;
	longn←LongNumber[num[0,n]];
	RETURN[LongDiv[longn.lc,pofTen]];
	END -- of ScanFrac --;

ScanLongLength: PROCEDURE RETURNS[LONG CARDINAL] =
	BEGIN
	magnify: BOOLEAN←TRUE;
	x: LONG CARDINAL; f: CARDINAL←0;
	Scale: PROCEDURE [int,frac: CARDINAL] =
		BEGIN OPEN InlineDefs;
		t2,t3,t4: LongNumber;
		t2.lc←x*frac; t3.lc←LongMult[f,int]; t4.lc←LongMult[f,frac];
		t4.lc←t2.lowbits+t3.lowbits+t4.highbits+LONG[0];
		x←x*int+t2.highbits+t3.highbits+t4.highbits
			+(IF t4.lowbits>77777B THEN 1 ELSE 0);
		END;
	x←ScanNumber[];
	GetNCTok[];
	IF curtok=[otherchar['.]] THEN f←ScanFrac[] ELSE BackInput;
	IF ScanString["true"L] THEN magnify←FALSE;
	SELECT TRUE FROM
		ScanString["mc"L] => Scale[1,0];
		ScanString["pt"L] => Scale[35,9567]; -- 35.14598
		ScanString["in"L] => Scale[2540,0];
		ScanString["pc"L] => Scale[421,49267]; --421.75176
	 	ScanString["cm"L] => Scale[1000,0];
		ScanString["mm"L] => Scale[100,0];
		ScanString["dd"L] => Scale[37,38927]; --37.59398496
		ScanString["vu"L] => Scale[TexTableDefs.DimnParam[varunit],0];
		ScanString["em"L] =>
			BEGIN curfont: Font←TexTableDefs.CurFont[];
			Scale[TexFontDefs.FontPar[curfont,quad],0];
			END;
		ENDCASE => BEGIN
			Scale[35,9567];
			Error["Illegal unit of measure (pt inserted)"L];
			END;
	ScanSpacer[];
	-- *** for debugging, introduce rfudge
	IF magnify THEN Scale[1,14632B]; -- 1.1
	RETURN[x];
	END -- of ScanLongLength --;

ScanLength: PUBLIC PROCEDURE RETURNS[n: Dimn] =
	BEGIN
	x: LONG CARDINAL←ScanLongLength[];
	IF x<=77777B THEN n←InlineDefs.LowHalf[x]
	ELSE BEGIN
		Error["Length greater than 32767 micas"L];
		n←LAST[Dimn];
		END;
	RETURN[IF nbrsign='+ THEN n ELSE -n];
	END -- of ScanLength --;

epsilonDimn: Dimn=1;

ScanPosLength: PUBLIC PROCEDURE RETURNS[Dimn] =
	BEGIN r: Dimn←ScanLength[];
	IF r>0 THEN RETURN[r];
	IF r<0 THEN Error["This dimension shouldn't be negative"L];
	RETURN[epsilonDimn];
	END -- of ScanPosLength --;

ScanFlex: PUBLIC PROCEDURE RETURNS[f: Flex] =
	BEGIN
	x: LONG CARDINAL; n: Dimn;
	ScanSpacer[];
	f.order←(SELECT TRUE FROM
		ScanString["regular"L] => regular,
		ScanString["lowerfill"L] => lowerfill,
		ScanString["fill"L] => fill,
		ENDCASE => regular);
	x←ScanLongLength[];
	IF x>77777B THEN BEGIN x←LongRShift[x,15]; f.order←fill END;
	n←InlineDefs.LowHalf[x];
	f.val←IF nbrsign='+ THEN n ELSE -n;
	RETURN[f];
	END -- of ScanFlex --;

ScanGlue: PUBLIC PROCEDURE[g: GluePtr] =
	BEGIN
	g.space←ScanLength[]; g.flex←[zeroFlex,zeroFlex];
	IF ScanString["plus"L] THEN g.flex[str]←ScanFlex[];
	IF ScanString["minus"L] THEN g.flex[shr]←ScanFlex[];
	END -- of ScanGlue --;

ScanSpec: PUBLIC PROCEDURE[parok: BOOLEAN, size: Dimn]
	RETURNS[len: Dimn, xpand, break: BOOLEAN] =
	BEGIN
	break←FALSE;
	IF ScanString["to"L] OR (break←(parok AND ScanString["par"L])) THEN
		BEGIN
		xpand←FALSE;
		ScanSpacer;
		len←IF ScanString["size"L] THEN size ELSE ScanLength[];
		END
	ELSE
		BEGIN
		xpand←TRUE;
		len←IF ScanString["expand"L] THEN ScanLength[] ELSE 0;
		END;
	ScanLB;
	END -- of ScanSpec --;

ScanHang: PUBLIC PROCEDURE RETURNS[HangSpec] =
	BEGIN
	hang: HangSpec;
	hang.width←ScanLength[];
	SELECT TRUE FROM
		ScanString["for"L] => BEGIN hang.begin←ScanNumber[]; hang.first←TRUE END;
		ScanString["after"L] => BEGIN hang.begin←ScanNumber[]; hang.first←FALSE END;
		ENDCASE => BEGIN hang.begin←1; hang.first←FALSE END;
	RETURN[hang];
	END;

ScanFileName: PUBLIC PROCEDURE [filename: STRING] =
	BEGIN OPEN TexStringDefs;
	c: Char;
	filename.length←0;
	DO
	GetNCTok[];
	WITH cc:curchar SELECT curcmd FROM
		spacer => EXIT;
		macprm,lbrace,rbrace,mathbr,tabmrk,supmrk,submrk,letter,otherchar => c←cc.char;
		ENDCASE => BEGIN BackError["Blank space should follow file name"L]; EXIT END;
	AppendChar[filename,c];
	ENDLOOP;
	END -- of ScanFileName --;

ScanFont: PUBLIC PROCEDURE RETURNS[Font] =
	BEGIN OPEN TexFontDefs;
	f: Font;
		DO
		GetNCTok;
		IF curtok.cmd=ctrlseq THEN BackError["Illegal Font Code"L] ELSE EXIT;
		ENDLOOP;
	f←LOOPHOLE[InlineDefs.BITAND[curchar,37B]];
	GetNCTok;
	IF FontDefined[f] THEN BEGIN IF curcmd#spacer THEN BackInput END
	ELSE
		BEGIN
		c: Char;
			DO
			c←TexTokenDefs.TokChar[curtok];
			IF c='= OR c='← THEN EXIT;
			BackError["First use of font must define it"L];
			GetNCTok;
			ENDLOOP;
		DefineFont[f];
		END;
	RETURN[f];
	END -- of ScanFont --;

DefineFont: PROCEDURE[f: TexDefs.Font] =
	BEGIN
	filename: STRING←[TexFileDefs.filenamesize];
	ScanFileName[filename];
	TexFileDefs.StripExtension[filename];
	TexFontDefs.ReadFontInfo[filename,f];
	END;

NumToMChar: PROCEDURE[n: CARDINAL] RETURNS[TexMathDefs.MChar] =
	BEGIN
	mfontmap: ARRAY [0..3] OF TexMathDefs.MFont = [rm,it,sy,ex];
	Bits: TYPE = MACHINE DEPENDENT RECORD[x:[0..177B], f:[0..3B], c:[0..177B]];
	bits: Bits=LOOPHOLE[n];
	RETURN[[mfont: mfontmap[bits.f], char: LOOPHOLE[bits.c,Char]]];
	END;

ScanMChar: PUBLIC PROCEDURE RETURNS[TexMathDefs.MChar] =
	BEGIN RETURN[NumToMChar[ScanNumber[]]] END;

LongRShift: PROCEDURE[n: LONG CARDINAL, shift: [0..15]]
	RETURNS[LONG CARDINAL] =
	BEGIN OPEN InlineDefs;
	hi,lo: CARDINAL;
	result: LongNumber;
	hi←HighHalf[n]; lo←LowHalf[n];
	result.highbits←BITSHIFT[hi,-shift];
	result.lowbits←BITOR[BITSHIFT[hi,16-shift],BITSHIFT[lo,-shift]];
	RETURN[result.lc];
	END;

ScanDelim: PUBLIC PROCEDURE RETURNS[TexMathDefs.Delimiter] =
	BEGIN OPEN TexMathDefs;
	GetNCTok;
	WITH cc:curchar SELECT curcmd FROM
		otherchar =>
			BEGIN defined: BOOLEAN; delim: Delimiter;
			[defined,delim]←TexTableDefs.DelimLookup[cc.char];
			IF defined THEN RETURN[delim];
			END;
		mathonly =>
			BEGIN mch: MChar←cc.tmchar.mchar;
			IF mch.mfont=sy AND mch.char IN Char[142C..153C] THEN
				BEGIN largechar: MChar←[ex,mch.char-(142B-4B)];
				-- *** Note the built-in dependency on the relationship
				-- between the symbol and mathex fonts!
				RETURN[[small: [TRUE,mch], large: [TRUE,largechar]]];
				END;
			END;
		ascii =>
			BEGIN OPEN InlineDefs;
			DChar: PROCEDURE[n: CARDINAL] RETURNS[DelimChar] =
				BEGIN
				IF n=0 THEN RETURN[nullDelimChar]
				ELSE RETURN[[TRUE,NumToMChar[n]]]
				END;
			nsm,nlg: CARDINAL;
			bign: LONG CARDINAL←ScanLongNumber[];
			nsm←BITAND[LowHalf[LongRShift[bign,9]],777B];
			nlg←BITAND[LowHalf[bign],777B];
			RETURN[[small: DChar[nsm], large: DChar[nlg]]];
			END;
		ENDCASE;
	BackError["Unknown Delimiter"L];
	RETURN[nullDelimiter];
	END -- of ScanDelim --;

defaultRuleDimn: Dimn=14; -- approximately 0.4 points

ScanRuleSpec: PUBLIC PROCEDURE RETURNS[width,height,depth: Dimn] =
	BEGIN
	IF curcmd=hrule THEN
		BEGIN width←nilDimn; height←defaultRuleDimn; depth←0; END
	ELSE
		BEGIN width←defaultRuleDimn; height←nilDimn; depth←nilDimn; END;
	 DO
		SELECT TRUE FROM
		ScanString["width"L] => width←ScanPosLength[];
		ScanString["height"L] => height←ScanPosLength[];
		ScanString["depth"L] => depth←ScanPosLength[];
		ENDCASE => EXIT;
	ENDLOOP;
	RETURN[width,height,depth];
	END -- of ScanRuleSpec --;

PassBlock: PUBLIC PROCEDURE =
	BEGIN unbal: INTEGER←0;
	DO -- ScanToks with no StoreItems
		GetTok[];
		SELECT curcmd FROM
		lbrace => unbal←unbal+1;
		rbrace => IF (unbal←unbal-1)<=0 THEN EXIT;
		ENDCASE;
	ENDLOOP;
	IF unbal<0 THEN Error["Missing { inserted"L];
	ScanSpacer[];
	END -- of PassBlock --;

InsNum: PUBLIC PROCEDURE[n: CARDINAL, style: NumberStyle] =
	BEGIN OPEN TexTokenDefs;
	p,q: TokenPtr; j,k: INTEGER;
	SELECT style FROM
		decimal =>
			BEGIN
			p←NIL;
				DO
				q←MakeTokenLEntry[[otherchar[(n MOD 10)+'0]]];
				q.link←p; p←q;
				n←n/10;
				IF n=0 THEN EXIT;
			ENDLOOP;
			END;
		roman =>
			BEGIN
			romval: ARRAY [1..7] OF CARDINAL ← [1000,500,100,50,10,5,1];
			romtok: ARRAY [1..7] OF CHARACTER ← ['m,'d,'c,'l,'x,'v,'i];
			temphead: TokenLEntry←nilTokenLEntry;
			q←@temphead;
			j←1;
				DO
				WHILE n>=romval[j]
					DO StoreTok[@q,[letter[romtok[j]]]]; n←n-romval[j]; ENDLOOP;
				IF n=0 THEN EXIT;
				k←j+1+(IF (j MOD 2)=0 THEN 0 ELSE 1);
				IF n+romval[k]>=romval[j] THEN
					BEGIN StoreTok[@q,[letter[romtok[j]]]]; n←n+romval[k]; END
				ELSE j←j+1;
				ENDLOOP;
			p←temphead.link;
			END;
		ENDCASE;
	InsList[p];
	END -- of InsNum --;

ScanInteger: PUBLIC PROCEDURE RETURNS[INTEGER] =
	BEGIN
	n: INTEGER←ScanNumber[];
	RETURN[IF nbrsign='- THEN -n ELSE n];  -- *** will do for now
	END;

ScanAscii: PUBLIC PROCEDURE RETURNS[Char] =
	BEGIN
	n: CARDINAL←ScanNumber[];
	RETURN[LOOPHOLE[n MOD 200B]];
	END;

END -- of TexScan --.