-- file DIScanner.Mesa
-- last modified by
-- Sandman, May 5, 1978 8:17 AM
-- Barbara, July 10, 1978 2:55 PM
-- Bruce, October 1, 1980 7:50 AM
-- Johnsson, July 16, 1980 8:28 AM
DIRECTORY
DebugOps USING [Foo],
DebugUsefulDefs USING [],
DI USING [],
LiteralOps USING [Find, FindDescriptor, FindString],
P1 USING [ControlZ, CR, hashval, NUL, Token],
ParseTable USING [
endmarker, Handle, tokenCARD, tokenLCARD, tokenCHAR, tokenDOT, tokenDOTS, tokenID,
tokenLNUM, tokenNUM, tokenSTR, TSymbol, VocabHashEntry],
Strings USING [SubStringDescriptor],
SymbolOps USING [EnterString];
Scanner: PROGRAM
IMPORTS LiteralOps, SymbolOps
EXPORTS DebugOps, DebugUsefulDefs, DI, P1 =
BEGIN OPEN P1, ParseTable;
InvalidCharacter: PUBLIC SIGNAL [index: CARDINAL] = CODE;
InvalidNumber: PUBLIC SIGNAL [f: DebugOps.Foo] = CODE;
dHashTab: DESCRIPTOR FOR ARRAY OF VocabHashEntry;
dScanTab: DESCRIPTOR FOR ARRAY CHARACTER [40C..177C] OF TSymbol;
vocab: STRING;
dVocabIndex: DESCRIPTOR FOR ARRAY OF CARDINAL;
text: STRING; -- the input string
desc: Strings.SubStringDescriptor; -- initial buffer segment
charIndex: CARDINAL; -- index of current character
currentChar: CHARACTER; -- most recently scanned character
radix: PUBLIC CARDINAL ← 10;
SetDefaultRadix: PUBLIC PROC [new: CARDINAL] = {radix ← new};
Atom: PUBLIC PROCEDURE RETURNS [symbol: Token] =
BEGIN OPEN symbol;
char, first, last: CHARACTER;
uId: BOOLEAN;
i, j, h: CARDINAL;
s1, s2: CARDINAL;
char ← currentChar;
DO
WHILE char IN [NUL..' ] DO
SELECT char FROM
ControlZ =>
DO
SELECT GetChar[] FROM
NUL => GOTO EndFile;
CR => EXIT;
ENDCASE;
ENDLOOP;
NUL => GOTO EndFile;
ENDCASE;
char ← GetChar[];
ENDLOOP;
index ← charIndex; value ← 0;
SELECT char FROM
'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm,
'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x, 'y, 'z =>
BEGIN
desc.offset ← index;
i ← 0;
DO
char ← GetChar[];
SELECT char FROM
IN ['a..'z], IN ['A..'Z], IN ['0..'9] => i ← i+1;
ENDCASE => EXIT;
ENDLOOP;
desc.length ← i+1;
class ← tokenID;
value ← SymbolOps.EnterString[@desc]; EXIT
END;
'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M,
'N, 'O, 'P, 'Q, 'R, 'S, 'T, 'U, 'V, 'W, 'X, 'Y, 'Z =>
BEGIN
desc.offset ← index;
i ← 0; uId ← TRUE; first ← last ← char;
DO
char ← GetChar[];
SELECT char FROM
IN ['A..'Z] =>
BEGIN last ← char;
i ← i+1;
END;
IN ['a..'z], IN ['0..'9] =>
BEGIN uId ← FALSE;
i ← i+1;
END;
ENDCASE => EXIT;
ENDLOOP;
i ← i+1;
IF uId THEN
BEGIN
h ← (LOOPHOLE[first, CARDINAL]*127 + LOOPHOLE[last, CARDINAL]) MOD hashval + 1;
WHILE (j ← dHashTab[h].symbol) # 0 DO
IF dVocabIndex[j]-(s2←dVocabIndex[j-1]) = i THEN
FOR s1 IN [index .. index+i) DO
IF text[s1] # vocab[s2] THEN EXIT;
s2 ← s2+1;
REPEAT
FINISHED => GO TO reserved;
ENDLOOP;
IF (h ← dHashTab[h].link) = 0 THEN EXIT;
ENDLOOP;
END;
desc.length ← i;
class ← tokenID;
value ← SymbolOps.EnterString[@desc]; EXIT
EXITS
reserved => BEGIN class ← j; EXIT END;
END;
'0, '1, '2, '3, '4, '5, '6, '7, '8, '9 =>
BEGIN
v, v10, v8: LONG INTEGER;
scale: CARDINAL;
valid, valid10, valid8, octal: BOOLEAN;
vRep: ARRAY [0..SIZE[LONG INTEGER]) OF WORD; -- machine dependent
v10 ← v8 ← 0; valid10 ← valid8 ← TRUE;
WHILE char IN ['0..'9]
DO
IF valid10 THEN [v10, valid10] ← AppendDigit10[v10, char];
IF valid8 THEN [v8, valid8] ← AppendDigit8[v8, char];
char ← GetChar[];
ENDLOOP;
SELECT char FROM
'B, 'C, 'b, 'c =>
BEGIN
class ← IF char = 'C OR char = 'c THEN tokenCHAR ELSE tokenCARD;
v ← v8; valid ← valid8; octal ← TRUE;
END;
'D, 'd =>
BEGIN
class ← tokenNUM; v ← v10; valid ← valid10; octal ← FALSE;
END;
ENDCASE =>
SELECT radix FROM
8 =>
BEGIN
class ← tokenCARD; v ← v8; valid ← valid8; octal ← TRUE;
END;
ENDCASE =>
BEGIN
class ← tokenNUM; v ← v10; valid ← valid10; octal ← FALSE;
END;
SELECT char FROM
'B, 'C, 'D, 'b, 'c, 'd =>
BEGIN
char ← GetChar[];
IF class = tokenNUM OR class = tokenCARD
THEN
BEGIN scale ← 0;
WHILE char IN ['0..'9]
DO
scale ← 10*scale + CARDINAL[char-'0];
char ← GetChar[];
ENDLOOP;
THROUGH [1 .. scale] WHILE valid
DO
IF octal
THEN [v, valid] ← AppendDigit8[v, '0]
ELSE [v, valid] ← AppendDigit10[v, '0];
ENDLOOP;
END;
END;
ENDCASE;
vRep ← LOOPHOLE[v];
IF vRep[1] = 0 --v <= MaxLiteral--
THEN value ← LiteralOps.Find[vRep[0]]
ELSE
BEGIN
IF class = tokenCHAR THEN valid ← FALSE;
class ← IF class = tokenCARD THEN tokenLCARD ELSE tokenLNUM;
value ← LiteralOps.FindDescriptor[DESCRIPTOR[vRep]];
END;
IF ~valid THEN SIGNAL InvalidNumber[LOOPHOLE[index]];
EXIT
END;
'' =>
BEGIN
char ← GetChar[];
class ← tokenCHAR;
value ← LiteralOps.Find[LOOPHOLE[char, CARDINAL]];
char ← GetChar[]; EXIT
END;
'" =>
BEGIN
desc.offset ← index+1;
i ← 0;
DO
char ← GetChar[];
IF char = NUL THEN char ← '";
IF char = '" THEN
BEGIN char ← GetChar[]; IF char # '" THEN EXIT; END;
i ← i+1;
ENDLOOP;
desc.length ← i;
class ← tokenSTR;
value ← LiteralOps.FindString[@desc]; EXIT
END;
'. =>
BEGIN
char ← GetChar[];
IF char = '.
THEN
BEGIN class ← tokenDOTS;
char ← GetChar[];
END
ELSE class ← tokenDOT;
EXIT
END;
ENDCASE =>
BEGIN class ← dScanTab[char];
char ← GetChar[];
IF class # 0 THEN EXIT ELSE SIGNAL InvalidCharacter[index];
END;
REPEAT
EndFile => BEGIN class ← endmarker; value ← 0 END;
ENDLOOP;
currentChar ← char; RETURN
END;
-- Character source
GetChar: PROCEDURE RETURNS [c: CHARACTER] =
BEGIN
charIndex ← charIndex + 1;
IF charIndex >= text.length THEN RETURN[NUL] ELSE c ← text[charIndex];
IF c ~IN[40C..177C] THEN SIGNAL InvalidCharacter[charIndex];
END;
-- numerical conversion
Digit: ARRAY CHARACTER ['0..'9] OF CARDINAL = [0,1,2,3,4,5,6,7,8,9];
AppendDigit10: PROCEDURE [v: LONG INTEGER, digit: CHARACTER ['0..'9]]
RETURNS [newV: LONG INTEGER, valid: BOOLEAN] =
BEGIN
MaxV: LONG INTEGER = 429496729; -- (2**32-1)/10
MaxD: CARDINAL = 5; -- (2**32-1) MOD 10
d: [0..9] = Digit[digit];
valid ← v < MaxV OR (v = MaxV AND d <= MaxD);
newV ← 10*v + d;
RETURN
END;
AppendDigit8: PROCEDURE [v: LONG INTEGER, digit: CHARACTER ['0..'9]]
RETURNS [newV: LONG INTEGER, valid: BOOLEAN] =
BEGIN
MaxV: LONG INTEGER = 3777777777B; -- (2**32-1)/8
MaxD: CARDINAL = 7B; -- (2**32-1) MOD 8
d: [0..9] = Digit[digit];
valid ← (d < 8) AND (v < MaxV OR (v = MaxV AND d <= MaxD));
newV ← 8*v + d;
RETURN
END;
-- initialization/finalization
ScanInit: PUBLIC PROCEDURE [
string: STRING, table: ParseTable.Handle] =
BEGIN
desc.base ← text ← string; desc.offset ← 0;
charIndex ← 0; currentChar ← text[0];
IF currentChar ~IN[40C..177C] THEN SIGNAL InvalidCharacter[charIndex];
IF table # NIL THEN
BEGIN OPEN table.scanTable;
dHashTab ← DESCRIPTOR [hashTab];
dScanTab ← DESCRIPTOR [scanTab];
vocab ← LOOPHOLE[@vocabBody, STRING];
dVocabIndex ← DESCRIPTOR [vocabIndex];
END;
RETURN
END;
ScanReset: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
BEGIN
RETURN [charIndex >= text.length]
END;
END.