-- NPGSScan.pgs
-- Copyright Ó 1985, 1986, 1988, 1990 by Xerox Corporation. All rights reserved.
-- DO NOT CONVERT TO TIOGA FORMAT! (NPGS requires the mesa-style comments)
-- Russ Atkinson (RRA) March 17, 1988 10:14:22 am PST
-- Doug Wyatt, May 15, 1986 5:36:01 pm PDT
-- JKF May 24, 1990 10:02:00 am PDT
-- NPGS [defs: NPGSParseTable, grammar: NPGS] ¬ NPGSScan.pgs
DIRECTORY
IO USING [STREAM],
NPGS1 USING [
ActionStack, Index, LinkStack, Token, Value, ValueStack, nullValue, InputLoc],
NPGSParseTable USING [
endMarker, HashIndex, HashTableRef, IndexTableRef,
InitHashTable, InitIndexTable, InitScanTable, InitVocabulary,
ProdDataRef, ScanTableRef, TSymbol, VocabularyRef, tokenID, tokenNUM,
tokenTAB3, tokenTAB4],
NPGSConDefs USING [
aliases, alternateLim,
CuspPreSetPriorityTokenArray, -- new for CUSP
eofMark, ExpandAliases, ExpandProdInfo, ExpandRhsChar,
ExpandSymTab, flags, getindex, inchar, MakeAliases, MakeProdInfo,
MakeRhsChar, MakeSymInfo, MakeSymTab, MakeTokenInfo, maxProd,
maxRhsSymbols, maxRule, nextAlias, numProd, numRules, outbufLim,
outchar, outeol, outnum, outstring, prodInfo, pssLim, resetoutstream,
rhsChar, rhsLim, seterrstream, setindex, setoutstream, symInfo, symTab,
symTabSize, tokenInfo, tokenSize, totalTokens, warningsLogged],
NPGSTypes USING [Options],
Rope USING [FromRefText, Length, ROPE];
NPGSScan: CEDAR PROGRAM
IMPORTS NPGS1, NPGSConDefs, NPGSParseTable, Rope
EXPORTS NPGS1, NPGSConDefs
= { OPEN NPGS1, NPGSParseTable, NPGSConDefs;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
-- TreeBuild: interpreter section
-- interpreter state
check: NAT = 3;
warning: NAT = 0;
specErrorCases: NAT = 5;
token, numRhsChars: CARDINAL ¬ 0;
lineWidth: INTEGER;
insertFlag: CARDINAL;
HashChainArray: TYPE = ARRAY [1..symTabSize/4] OF CARDINAL;
hashChain: REF HashChainArray;
-- local data base (supplied by parser)
v: NPGS1.ValueStack;
l: NPGS1.LinkStack;
q: NPGS1.ActionStack;
prodData: ProdDataRef;
-- initialization/termination
AssignDescriptors: PUBLIC PROC [
qd: NPGS1.ActionStack, vd: NPGS1.ValueStack, ld: NPGS1.LinkStack, pp: ProdDataRef] = {
q ¬ qd;
v ¬ vd;
l ¬ ld;
prodData ¬ pp;
};
OutToken: PUBLIC PROC [symbol: CARDINAL] RETURNS [CARDINAL] = {
r: ROPE = "* * *";
IF symbol = 0 THEN {outstring[r]; RETURN[Rope.Length[r]]};
FOR i: CARDINAL IN [0..symInfo[symbol].length) DO
outchar[symTab[symbol*tokenSize+i],1];
ENDLOOP;
RETURN [symInfo[symbol].length];
};
-- the interpretation rules
prix, chix: CARDINAL ¬ 0; --indexes into prodInfo and rhsChar
rhsFlag: BOOL;
lastSymbol, lhsDef: CARDINAL ¬ 0;
ProcessQueue: PUBLIC PROC [qI, top: CARDINAL, cusp: BOOL] = {
i, j, k: CARDINAL;
-- local procedures
PrintTableHead: PROC [c: CHAR] = {
IF flags[echo] THEN {outeol[2]; outstring["||TABLE"]; outchar[c,1]; outeol[1]};
};
SetRuleChain: PROC [rule: CARDINAL, chain: BOOL] = {
FixLastProd[];
IF prix=prodInfo.length THEN --production table overflow
prodInfo ¬ ExpandProdInfo[prodInfo, prodInfo.length/8];
prodInfo[prix].chain ¬ chain;
numRules ¬ MAX[numRules, rule];
IF rule>maxRule THEN Error[check+10,-5,InputLoc[]] ELSE prodInfo[prix].rule ¬ rule;
};
FixLastProd: PROC = {
eofMark: CARDINAL ¬ NPGSConDefs.eofMark;
prodInfo[prix-1].lhs ¬ lhsDef;
IF prodInfo[prix-1].count=0 THEN tokenInfo[lhsDef-eofMark].empty ¬ TRUE;
IF rhsFlag THEN {
-- too many rhsChars
rhsFlag ¬ FALSE;
Error[check+5, (prix-1+specErrorCases), InputLoc[]];
};
};
ProdHeader: PROC [new: BOOL] = {
prodInfo[prix].index ¬ chix;
IF lhsDef>eofMark THEN {
IF tokenInfo[lhsDef-eofMark].count=alternateLim THEN {
Error[check+1, lhsDef,InputLoc[]]; --too many alternatives
tokenInfo[lhsDef-eofMark].count ¬1;
};
tokenInfo[lhsDef-eofMark].count ¬ tokenInfo[lhsDef-eofMark].count + 1;
};
IF flags[echo] THEN {
lineWidth ¬ outbufLim-tokenSize-14;
outeol[IF new THEN 2 ELSE 1];
outnum[prix,3];
outstring[IF prodInfo[prix].chain THEN " C " ELSE " "];
outnum[prodInfo[prix].rule,3]; outchar[' ,2];
outchar[' ,tokenSize-(IF new THEN OutToken[lhsDef] ELSE 0)];
outstring[IF new THEN " ::= " ELSE " | "];
};
prix ¬ prix+1;
};
LhsSymbol: PROC [symbol: CARDINAL] = {
lhsDef ¬ symbol;
IF lhsDef<=eofMark
THEN Error[check+4,lhsDef,InputLoc[]]
-- undefined or terminal symbol before ::=
ELSE
IF tokenInfo[lhsDef-eofMark].index = 0
THEN tokenInfo[lhsDef-eofMark].index ¬ prix
ELSE Error[check+2,lhsDef,InputLoc[]]; --multiple definitions
ProdHeader[TRUE];
};
-- end of local procedures
FOR qj: CARDINAL IN [0..qI) DO
top ¬ top-q[qj].tag.pLength+1;
SELECT prodData[q[qj].transition].rule FROM
0 => -- TYPE: NPGSParseTable EXPORTS: SELF
-- GOAL: grammar
-- TERMINALS:
-- symbol num '? '| "::=" 'C "||TABLE1" "||TABLE2" "||TABLE3"
-- "||TABLE4" "||INPUT" "||CHAIN" "||LISTS" "||PRINTLR"
-- "||PRINTLALR" "||FIRST" "||IDS" "GOAL"
-- ALIASES: symbol tokenID num tokenNUM '? tokenQUERY
-- "||TABLE3" tokenTAB3 "||TABLE4" tokenTAB4 '? initialSymbol
-- PRODUCTIONS:
-- grammar ::= '? head ruleset
{
FixLastProd[];
numProd ¬ prix-1;
numRhsChars ¬ chix-1;
i ¬ 1;
IF flags[echo] THEN outeol[1];
IF numProd > pssLim OR totalTokens > pssLim THEN Error[check+6,0,InputLoc[]];
IF cusp THEN CuspPreSetPriorityTokenArray[hashChain]; -- new for CUSP
hashChain ¬ NIL;
WHILE symInfo[i].used AND i <= totalTokens DO
IF i > eofMark AND tokenInfo[i-eofMark].index = 0 THEN EXIT;
i ¬ i+1;
ENDLOOP;
IF i <= totalTokens THEN {
defflag: BOOL ¬ FALSE;
lineWidth ¬ 0; Error[warning+3,0,InputLoc[]]; seterrstream[];
FOR i IN [i..totalTokens] DO
j ¬ 0; IF ~symInfo[i].used THEN j ¬ j+1;
IF i > eofMark AND tokenInfo[i-eofMark].index = 0 THEN j ¬ j+2;
IF j # 0 THEN {
IF (lineWidth ¬ lineWidth+(k¬(11+j)/2)) > outbufLim THEN {
outeol[1]; lineWidth ¬ k};
outnum[i,5];
IF j#2 THEN outchar['U,1] ELSE defflag ¬ TRUE;
IF j>1 THEN outchar['D,1]};
ENDLOOP;
outeol[1]; resetoutstream[];
IF defflag THEN
-- nonterminal used but not defined
Error[check+3,-2,InputLoc[]];
};
FOR i IN [1..numProd] DO
IF prodInfo[i].chain THEN
IF prodInfo[i].count # 1 OR rhsChar[prodInfo[i].index] <= eofMark THEN {
Error[warning+2, -(i+specErrorCases), InputLoc[]];
prodInfo[i].chain ¬ FALSE;
};
ENDLOOP;
FinishInput[];
};
1 => -- head ::= directives terminals nonterminals "||TABLE4"
-- head ::= directives terminals nonterminals aliases "||TABLE4"
{
totalTokens ¬ lastSymbol; PrintTableHead['4];
tokenInfo ¬ MakeTokenInfo[totalTokens-eofMark+1];
numRules ¬ 0;
prodInfo ¬ MakeProdInfo[maxProd+1];
rhsChar ¬ MakeRhsChar[maxRhsSymbols+1];
FOR i IN [0..totalTokens-eofMark] DO
tokenInfo[i] ¬ [count:0, empty:FALSE, index:0];
ENDLOOP;
prodInfo[0] ¬ [count:2, rule:0, chain:FALSE, lhs:0, index:0];
FOR i IN [1..maxProd] DO
prodInfo[i] ¬ [count:0, rule:0, chain:FALSE, lhs:0, index:0];
ENDLOOP;
IF flags[echo] THEN {
outeol[1];
outchar[' ,20];
outstring["GOAL ::= "];
[] ¬ OutToken[eofMark+1]; outchar[' ,1];
[] ¬ OutToken[eofMark];
};
rhsChar[0] ¬ eofMark+1;
rhsChar[1] ¬ eofMark;
symInfo[eofMark+1].used ¬ symInfo[eofMark].used ¬ TRUE;
prix ¬ 1; chix ¬ 2; lhsDef ¬ 0; rhsFlag ¬ FALSE;
};
2 => -- directives ::=
{flags ¬ ALL[FALSE]; l[top] ¬ InputLoc[]};
3 => -- directive ::= "||INPUT"
{flags[echo] ¬ TRUE; setoutstream[".echo"]};
4 => -- directive ::= "||CHAIN"
flags[chain] ¬ TRUE;
5 => -- directive ::= "||LISTS"
flags[lists] ¬ TRUE;
6 => -- directive ::= "||PRINTLR"
flags[printLR] ¬ TRUE;
7 => -- directive ::= "||PRINTLALR"
flags[printLALR] ¬ TRUE;
8 => -- directive ::= "||FIRST"
flags[first] ¬ TRUE;
9 => -- directive ::= "||IDS"
flags[ids] ¬ TRUE;
10 => -- terminals ::= "||TABLE1"
{
IF flags[echo] THEN {
outeol[1];
FOR opt: NPGSTypes.Options IN NPGSTypes.Options DO
IF flags[opt] THEN outstring[
SELECT opt FROM
echo => "||INPUT ",
chain => "||CHAIN ",
lists => "||LISTS ",
printLR => "||PRINTLR ",
printLALR => "||PRINTLALR ",
first => "||FIRST ",
ENDCASE => "||IDS "];
ENDLOOP;
};
PrintTableHead['1];
};
11 => -- terminals ::= terminals discard symbol
-- nonterminals ::= nonterminals discard symbol
{
lastSymbol ¬ v[top+2].s;
IF flags[echo] THEN {
outnum[lastSymbol, 3]; outchar[' , 2];
[] ¬ OutToken[lastSymbol];
outeol[1]};
};
12 => -- nonterminals ::= "||TABLE2"
{
PrintTableHead['2]; eofMark ¬ lastSymbol;
nextAlias ¬ 0; -- assert TABLE3 empty in case it is omitted
aliases ¬ NIL;
};
13 => -- aliases ::= "||TABLE3"
{
PrintTableHead['3];
aliases ¬ MakeAliases[64];
};
14 => -- aliases ::= aliases symbol symbol
{temp: UNSPECIFIED ¬ NPGSConDefs.eofMark;
IF v[top+1].s>temp THEN Error[check+7,v[top+1].s,InputLoc[]];
IF v[top+2].s<=temp THEN Error[check+8,v[top+2].s,InputLoc[]];
IF nextAlias=aliases.length THEN
aliases ¬ ExpandAliases[aliases, aliases.length/8];
aliases[nextAlias] ¬ [v[top+1].s,v[top+2].s]; nextAlias ¬ nextAlias+1;
IF flags[echo] THEN {
outchar[' ,tokenSize+1-OutToken[v[top+1].s]]; outchar[' ,1];
j ¬ CARDINAL[v[top+2].s]*tokenSize;
FOR i IN [j..j+tokenSize) WHILE symTab[i]#0C DO outchar[symTab[i],1] ENDLOOP;
outeol[1]};
};
15 => -- discard ::=
l[top] ¬ InputLoc[]; -- keep the parser error recovery happy
16 => -- rulegroup ::= symbol "::="
{SetRuleChain[prix, FALSE]; LhsSymbol[v[top].s]};
17 => -- rulegroup ::= prefix symbol "::="
LhsSymbol[v[top+1].s];
18 => -- rulegroup ::= rulegroup symbol "::="
{SetRuleChain[prix, FALSE]; LhsSymbol[v[top+1].s]};
19 => -- rulegroup ::= rulegroup prefix symbol "::="
LhsSymbol[v[top+2].s];
20 => -- rulegroup ::= rulegroup '|
{SetRuleChain[prix, FALSE]; ProdHeader[FALSE]};
21 => -- rulegroup ::= rulegroup prefix '|
ProdHeader[FALSE];
22 => -- rulegroup ::= rulegroup symbol
{temp: CARDINAL ¬ 0;
i ¬ v[top+1].s; symInfo[i].used ¬ TRUE;
IF i=eofMark OR i=eofMark+1 THEN Error[check+9,0,InputLoc[]]; --goal symbols
IF flags[echo] THEN {
IF lineWidth<symInfo[i].length THEN {
outeol[1]; outchar[' ,tokenSize+18]; lineWidth ¬ outbufLim - tokenSize-18};
IF (lineWidth ¬ (lineWidth-OutToken[i]-1)) > 0 THEN outchar[' , 1]};
IF chix=rhsChar.length THEN
rhsChar ¬ ExpandRhsChar[rhsChar, rhsChar.length/8];
rhsChar[chix]¬i; chix ¬ chix +1;
IF prodInfo[prix-1].count = rhsLim THEN {
prodInfo[prix-1].count ¬ 1; rhsFlag ¬ TRUE};
temp ¬ prodInfo[prix-1].count;
prodInfo[prix-1].count ¬ temp+1;
};
23 => -- prefix ::= num
SetRuleChain[v[top].s, FALSE];
24 => -- prefix ::= num num
-- prefix ::= '? num
SetRuleChain[v[top+1].s, FALSE];
25 => -- prefix ::= discard 'C
SetRuleChain[prix, TRUE];
26 => -- prefix ::= discard 'C num
SetRuleChain[v[top+2].s, TRUE];
27 => -- prefix ::= '?
SetRuleChain[prix, FALSE];
28 => -- directives ::= directives directive
-- discard ::= num
-- discard ::= '?
-- ruleset ::=C rulegroup
-- ruleset ::= goalrule rulegroup
-- goalrule ::= "GOAL" "::=" symbol symbol
NULL;
ENDCASE => ERROR;
ENDLOOP;
};
-- the following procedure is called from the ScanReset if no errors
FinishInput: PROC = {
emptyFlag: BOOL ¬ TRUE;
j, k: CARDINAL;
-- compute nonterminals deriving empty
WHILE emptyFlag DO
emptyFlag ¬ FALSE;
FOR i: CARDINAL IN [1..totalTokens-eofMark] DO -- each nonterminal
IF tokenInfo[i].empty THEN LOOP; --which does not derive empty
j ¬ tokenInfo[i].index;
FOR prix: CARDINAL IN [j..j+tokenInfo[i].count) DO
-- each production of the nonterminal
k ¬ prodInfo[prix].index;
FOR chix: CARDINAL IN [k..k+prodInfo[prix].count) DO -- each rhs character
IF rhsChar[chix]<=eofMark OR ~tokenInfo[rhsChar[chix]-eofMark].empty THEN EXIT;
REPEAT FINISHED => {tokenInfo[i].empty ¬ emptyFlag ¬ TRUE; EXIT};
ENDLOOP
ENDLOOP
ENDLOOP
ENDLOOP;
};
-- the following procedure outputs the data structure contents in the tables:
-- PRODUCTIONINFO TOKENINFO SYMINFO
-- num count rule chain lhs index count empty index link used length symbol
-- 4 1 3 5 1 4 5 2 3 1 5 2 4 1 3 1 ...
CheckOut: PUBLIC PROC = {
IF flags[ids] THEN {
seterrstream[]; outeol[1];
outstring[" PRODUCTIONINFO TOKENINFO SYMINFO"];
FOR i: CARDINAL IN [0..MAX[numProd,totalTokens]] DO
outeol[1]; outnum[i,4]; outchar[' ,1];
IF i>numProd THEN outchar[' ,20]
ELSE {
outnum[prodInfo[i].count,3]; outnum[prodInfo[i].rule,5];
outchar[IF prodInfo[i].chain THEN 'C ELSE ' , 1];
outnum[prodInfo[i].lhs,4]; outnum[prodInfo[i].index,5]; outchar[' ,2]};
IF i IN (0..totalTokens] THEN {
IF i<=eofMark THEN outchar[' ,11] ELSE {
outnum[tokenInfo[i-eofMark].count,3];
outchar[IF tokenInfo[i-eofMark].empty THEN 'E ELSE ' ,1];
outnum[tokenInfo[i-eofMark].index,5]; outchar[' ,2]};
outnum[symInfo[i].link,4]; outchar[IF symInfo[i].used THEN 'U ELSE ' ,1];
outnum[symInfo[i].length,3]; outchar[' ,1]; [] ¬ OutToken[i]}
ENDLOOP;
outeol[1]; outstring["RHSCHAR"]; lineWidth ¬ outbufLim;
FOR i: CARDINAL IN [0..numRhsChars] DO
lineWidth ¬ lineWidth+tokenSize+1;
IF lineWidth > outbufLim THEN {outeol[1]; outnum[i,4]; lineWidth ¬ 4};
outchar[' ,1]; [] ¬ OutToken[rhsChar[i]];
ENDLOOP;
outeol[1]; resetoutstream[];
};
};
-- error recovery
TokenValue: PUBLIC PROC [s: TSymbol] RETURNS [NPGS1.Value] = {
RETURN [NPGS1.nullValue];
};
-- Scanner: text input and error routines
-- table installation
hashTab: HashTableRef;
scanTab: ScanTableRef;
vocab: VocabularyRef;
vocabIndex: IndexTableRef;
InstallScanTable: PUBLIC PROC = {
hashTab ¬ NPGSParseTable.InitHashTable[];
scanTab ¬ NPGSParseTable.InitScanTable[];
vocab ¬ NPGSParseTable.InitVocabulary[];
vocabIndex ¬ NPGSParseTable.InitIndexTable[];
};
-- scanner state
stream: STREAM ¬ NIL; -- the input stream (a dummy for now)
Logger: PROC [PROC [log: STREAM]] ¬ NIL;
NUL: CHAR = '\000;
buffer: REF TEXT ¬ NIL; -- token assembly area
nTokens: CARDINAL ¬ 0; -- token count
nErrors: CARDINAL; -- lexical errors
char: CHAR; -- current (most recently scanned) character
tI: CARDINAL ¬ 0; -- its (stream) index
eof: BOOL;
NextChar: PROC = { -- also expanded inline within Atom
tI ¬ tI + 1; [char, eof] ¬ inchar[]};
NextToken: PUBLIC PROC RETURNS[t: Token] = {
--OPEN t;
LocateToken: PROC[string: REF TEXT] RETURNS[CARDINAL] = {
-- returns token corresponding to string
i, j: CARDINAL;
j ¬ hashChain[(string.length*256+(string[0].ORD)) MOD (symTabSize/4) + 1];
WHILE j # 0 DO
IF symInfo[j].length = string.length THEN {
i ¬ j*tokenSize;
FOR k: CARDINAL IN [0..string.length) DO
IF symTab[i+k]#string[k] THEN EXIT
REPEAT FINISHED => RETURN [j]
ENDLOOP;
};
j ¬ symInfo[j].link;
ENDLOOP;
RETURN [0];
};
TokenToSymTab: PROC[string: REF TEXT, token: CARDINAL] = {
i, j: CARDINAL;
i ¬ token*tokenSize;
FOR j IN [0..string.length) DO symTab[i+j] ¬ string[j] ENDLOOP;
symInfo[token].length ¬ string.length; symInfo[token].used ¬ FALSE;
j ¬ (string.length*256+(string[0].ORD)) MOD (symTabSize/4) + 1;
symInfo[token].link ¬ hashChain[j];
hashChain[j] ¬ token;
};
TokenOrId: PROC[sub: CARDINAL] = {
j, s1, s2: CARDINAL;
h: HashIndex;
WHILE char NOT IN [NUL..' ] DO
IF sub<tokenSize THEN buffer[sub] ¬ char;
sub ¬ sub + 1; NextChar[];
ENDLOOP;
IF sub>tokenSize THEN {
buffer.length ¬ sub ¬ tokenSize; Error[1,-1,t.index]}; --overlength
IF sub = 1 THEN {
t.class ¬ scanTab[buffer[0]];
IF t.class # 0 THEN RETURN};
j ¬ buffer[0] - 0C;
h ¬ ((j*128-j) + CARDINAL[buffer[sub-1]-0C]) MOD HashIndex.LAST + 1;
WHILE (j ¬ hashTab[h].symbol) # 0 DO
IF vocabIndex[j]-(s2¬vocabIndex[j-1]) = sub THEN
FOR s1 IN [0 .. sub) DO
IF buffer[s1] # vocab.text[s2] THEN EXIT;
s2 ¬ s2+1;
REPEAT FINISHED => {
IF j = tokenTAB3 THEN insertFlag ¬ 2 ELSE
IF j = tokenTAB4 THEN insertFlag ¬ 3;
IF j<=tokenNUM THEN EXIT;
t.class ¬ j; RETURN};
ENDLOOP;
IF (h ¬ hashTab[h].link) = 0 THEN EXIT;
ENDLOOP;
buffer.length ¬ sub; t.class ¬ tokenID; t.value.s ¬ LocateToken[buffer];
SELECT insertFlag FROM
1 =>
-- reading terminals and nonterminals
IF t.value # NPGS1.nullValue
THEN Error[check+2,t.value.s,t.index] -- multiply defined symbol
ELSE {
IF token=symTab.length THEN {
symTab ¬ ExpandSymTab[symTab, symTab.length/16];
symTab ¬ ExpandSymTab[symTab, symInfo.length/16];
};
TokenToSymTab[buffer, token];
t.value.s ¬ token; --[scalar[token]];
token ¬ token+1;
};
2 =>
-- processing aliases
IF t.value=NPGS1.nullValue THEN {
s1 ¬ token*tokenSize;
IF token=symTab.length THEN
symTab ¬ ExpandSymTab[symTab, symTab.length/16];
FOR j IN [0..buffer.length) DO symTab[s1+j] ¬ buffer[j] ENDLOOP;
t.value.s ¬ token; --[scalar[token]];
token ¬ token+1;
};
3 =>
-- processing productions
IF t.value = NPGS1.nullValue THEN Error[check+3,-3,t.index]; --symbol not defined
ENDCASE;
};
DO
WHILE char IN [NUL..' ] DO
SELECT char FROM
'\032 --^Z-- =>
UNTIL char = '\n DO
IF eof THEN GO TO EndFile;
NextChar[];
ENDLOOP;
ENDCASE;
IF eof THEN GO TO EndFile;
NextChar[];
ENDLOOP;
t.index ¬ tI;
t.value.s ¬ NPGS1.nullValue;
SELECT char FROM
IN ['0..'9] => {
val: CARDINAL ¬ 0;
valid: BOOL ¬ TRUE;
maxVal: CARDINAL = CARDINAL.LAST;
WHILE char IN ['0..'9] DO
IF valid THEN {
d: [0..9] = char.ORD-'0.ORD;
valid ¬ val<maxVal/10 OR (val=maxVal/10 AND d<=(maxVal MOD 10));
IF valid THEN val ¬ 10*val+d};
NextChar[];
ENDLOOP;
IF ~valid THEN val ¬ maxVal;
t.class ¬ tokenNUM; t.value.s ¬ val; GO TO GotNext}; --[scalar[val]]; GO TO GotNext};
'- => {
NextChar[];
IF char # '- THEN {buffer[0] ¬ '-; TokenOrId[1]; GO TO GotNext};
char ¬ NUL;
DO
pChar: CHAR = char;
IF eof THEN GO TO EndFile;
NextChar[];
SELECT char FROM
'- => IF pChar = '- THEN EXIT;
'\n => EXIT;
ENDCASE;
ENDLOOP;
NextChar[];
};
ENDCASE => {TokenOrId[0]; GO TO GotNext};
REPEAT
GotNext => NULL;
EndFile => {t.class ¬ endMarker; t.index ¬ tI; t.value.s ¬ NPGS1.nullValue};
ENDLOOP;
nTokens ¬ nTokens + 1;
};
-- initialization/finalization
ScanInit: PUBLIC PROC [source: STREAM, logger: PROC [PROC [log: STREAM]]] = {
stream ¬ source; Logger ¬ logger;
buffer ¬ NEW[TEXT[tokenSize]];
tI ¬ 0;
[char, eof] ¬ inchar[];
nTokens ¬ nErrors ¬ 0;
buffer.length ¬ tokenSize;
-- initialise symbol table
token ¬ 1; insertFlag ¬ 1;
symTab ¬ MakeSymTab[(symTabSize+1)*tokenSize];
symInfo ¬ MakeSymInfo[symTabSize+1];
FOR i: CARDINAL IN [1..symTabSize] DO
symInfo[i] ¬ [link:0, length:0, used:FALSE];
ENDLOOP;
hashChain ¬ NEW[HashChainArray ¬ ALL[0]];
};
ScanReset: PUBLIC PROC = {
stream ¬ NIL;
Logger ¬ NIL;
CheckOut[];
buffer ¬ NIL;
};
ScanStats: PUBLIC PROC RETURNS[CARDINAL, CARDINAL] = {
RETURN [nTokens, nErrors];
};
-- error handling
ResetScanIndex: PUBLIC PROC[index: CARDINAL] RETURNS[success: BOOL¬TRUE] = {
IF index = tI THEN RETURN;
setindex[index];
[char, eof] ¬ inchar[];
tI ¬ index;
};
PrintTextLine: PROC[origin: CARDINAL] RETURNS[start: CARDINAL] = {
lineIndex: CARDINAL;
char: CHAR;
n: [1..100];
start ¬ lineIndex ¬ origin;
FOR n IN [1..100] UNTIL lineIndex = 0 DO
lineIndex ¬ lineIndex - 1;
setindex[lineIndex];
IF inchar[].c = '\n THEN EXIT;
start ¬ lineIndex;
ENDLOOP;
setindex[start];
FOR n IN [1..100] UNTIL ([c: char] ¬ inchar[]).end DO
SELECT char FROM
'\n, '\032 --^Z-- => EXIT;
ENDCASE => outchar[char,1];
ENDLOOP;
outeol[1];
};
ErrorContext: PUBLIC PROC [to: STREAM, message: ROPE, tokenIndex: CARDINAL] = {
saveIndex: CARDINAL = getindex[];
origin: CARDINAL = tokenIndex;
char: CHAR;
setindex[PrintTextLine[origin]];
UNTIL getindex[] = origin OR ([c: char] ¬ inchar[]).end DO
outchar[IF char = '\n THEN '\n ELSE ' ,1];
ENDLOOP;
outstring["^ ["]; outnum[tokenIndex,1];
outchar['],1]; outeol[1]; outstring[message];
setindex[saveIndex];
};
Error: PROC [code: CARDINAL, control: INTEGER, index: CARDINAL] = {
Inner: PROC [log: STREAM -- a dummy for now --] ~ {
ErrorContext[log,
SELECT code FROM
1 => "WARNING - Overlength symbol (increase TOKENSIZE?) truncated to - ",
2 => "WARNING - Not a chain production - ",
3 => "WARNING - Unused(U) or undefined(D)symbols (refer to TABLE1 and 2)",
4 => "ERROR - Nonterminal with too many rules (increase ALTERNATELIM?) - ",
5 => "ERROR - Multiple definitions of symbol - ",
6 => "ERROR - Symbol not defined - ",
7 => "ERROR - Terminal precedes ::= - ",
8 => "ERROR - Too many rhs symbols in production (increase RHSLIM?) - ",
9 => "ERROR - Internal field will overflow - increase PSSLIM",
10 => "ERROR - Aliased symbol not a terminal symbol - ",
11 => "ERROR - Aliases must not be terminal symbols - ",
12 => "ERROR - Goal symbols used in rhs",
13 => "ERROR - Number greater than ",
ENDCASE => NIL,
index];
SELECT -control FROM
<0 => [] ¬ OutToken[control];
0 => NULL;
1 => outstring[Rope.FromRefText[buffer]];
2 => outstring["see previous message"];
3 => {outstring[Rope.FromRefText[buffer]]; outstring[" not in TABLE1 or 2"]};
4 => NULL; -- not used
5 => outstring["MAXRULE"];
ENDCASE => outnum[-control-specErrorCases, 5];
outeol[2];
};
Logger[Inner];
IF code>check THEN nErrors ¬ nErrors+1 ELSE warningsLogged ¬ TRUE;
};
}.