-- file PGSScan.Mesa rewritten by PGS, 29-Aug-80  9:27
-- file PGSScan.Mesa
-- syntax last modified by Satterthwaite,  7-Jan-80 11:31 
-- code   last modified by Satterthwaite, July 14, 1980  2:53 PM

DIRECTORY
  PGS1: TYPE USING [ErrorContext, InputLoc, locateindex, Token],
  ParseTable: FROM "PGSParseTable" USING [
    ActionEntry, EndMarker, Handle, HashIndex, Production, ProductionInfo,
    tokenID, tokenNUM, tokenTAB3, tokenTAB4, TSymbol, VocabHashEntry],
  PGScondefs: TYPE;

Scanner: PROGRAM
    IMPORTS PGS1, PGScondefs 
    EXPORTS PGS1, PGScondefs =
  BEGIN OPEN  PGS1, ParseTable, PGScondefs;

  hashTab: POINTER TO ARRAY HashIndex OF VocabHashEntry;
  scanTab: POINTER TO ARRAY CHARACTER [40C..177C] OF TSymbol;
  vocab: STRING;
  vocabIndex: POINTER TO ARRAY TSymbol OF CARDINAL;

  check: CARDINAL = 3;
  warning: CARDINAL = 0;
  specErrorCases: CARDINAL = 5;

  token, numRhsChars: CARDINAL;
  lineWidth: INTEGER;
  insertFlag: CARDINAL;

  hashChain: DESCRIPTOR FOR ARRAY [1..symtabsize/4] OF CARDINAL;

  ProductionArray: TYPE = ARRAY Production OF ProductionInfo;

 -- local data base (supplied by parser)

  v: DESCRIPTOR FOR ARRAY OF UNSPECIFIED;
  l: DESCRIPTOR FOR ARRAY OF CARDINAL;

  q: DESCRIPTOR FOR ARRAY OF ActionEntry;

  prodData: POINTER TO ProductionArray;

 -- initialization/termination

  AssignDescriptors: PUBLIC PROC [
	qd: DESCRIPTOR FOR ARRAY OF ActionEntry,
	vd: DESCRIPTOR FOR ARRAY OF UNSPECIFIED,
	ld: DESCRIPTOR FOR ARRAY OF CARDINAL,
	pp: POINTER TO ProductionArray] =
    {q ← qd;  v ← vd;  l ← ld;  prodData ← pp};

  outtoken: PUBLIC PROC [symbol:CARDINAL] RETURNS [CARDINAL] =
    BEGIN
    IF symbol=0 THEN {outstring["* * *"]; RETURN[5]};
    FOR i:CARDINAL IN [0..syminfo[symbol].length)
      DO outchar[symtab[symbol*tokensize+i],1] ENDLOOP;
    RETURN [syminfo[symbol].length]
    END;


 -- the interpretation rules

  prix, chix: CARDINAL; --indexes into prodinfo and rhschar
  rhsFlag: BOOLEAN; 
  lastSymbol, lhsDef: CARDINAL;

  ProcessQueue: PUBLIC PROC [qI, top: CARDINAL] =
    BEGIN qj,i,j,k: CARDINAL;
    opt: Options;

   -- local procedures
    PrintTableHead: PROC [c: CHARACTER]=
      BEGIN IF flags[echo] THEN
	{outeol[2];outstring["||TABLE"]; outchar[c,1]; outeol[1]}
      END;

    SetRuleChain: PROC [rule: CARDINAL, chain: BOOLEAN] =
      BEGIN
      FixLastProd[];
      IF prix=LENGTH[prodinfo] THEN --production table overflow
	prodinfo ← LOOPHOLE[expand[prodinfo,SIZE[prodrecord],LENGTH[prodinfo]/8]];
      prodinfo[prix].chain ← chain;
      IF rule>maxrule THEN Error[check+10,-5,InputLoc[]] ELSE prodinfo[prix].rule ← rule
      END;

    FixLastProd: PROC =
      BEGIN prodinfo[prix-1].lhs ← lhsDef;
      IF prodinfo[prix-1].count=0 THEN tokeninfo[lhsDef-eofile].empty ← TRUE;
      IF rhsFlag THEN  -- too many rhschars
	{rhsFlag ← FALSE; Error[check+5, (prix-1+specErrorCases), InputLoc[]]};
      END;

    ProdHeader: PROC [new:BOOLEAN] =
      BEGIN
      prodinfo[prix].index ← chix;
      IF lhsDef>eofile THEN
	BEGIN IF tokeninfo[lhsDef-eofile].count=alternatelim THEN
	  BEGIN Error[check+1, lhsDef,InputLoc[]];  --too many alternatives
	  tokeninfo[lhsDef-eofile].count ←1
	  END;
	tokeninfo[lhsDef-eofile].count ← tokeninfo[lhsDef-eofile].count + 1;
	END;
      IF flags[echo] THEN
	BEGIN 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 "   | "];
	END;
      prix ← prix+1
      END;

    LhsSymbol: PROC [symbol:CARDINAL] =
      BEGIN lhsDef ← symbol;
      IF lhsDef<=eofile THEN Error[check+4,lhsDef,InputLoc[]]
	-- undefined or terminal symbol before ::=
      ELSE
      IF tokeninfo[lhsDef-eofile].index = 0 THEN tokeninfo[lhsDef-eofile].index ← prix
      ELSE Error[check+2,lhsDef,InputLoc[]]; --multiple definitions
      ProdHeader[TRUE];
      END;
   -- end of local procedures

    FOR qj IN [0..qI)
      DO top ← top-q[qj].tag.pLength+1;
      SELECT prodData[q[qj].transition].rule FROM

       0  => -- TYPE: ParseTable    MODULE: PGSParseTable.mesa
	     -- BINARY: PGSParseData.bcd    INTERFACE: 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
	  BEGIN 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[]];
	  FreeSegment[BASE[hashChain]];
	  WHILE syminfo[i].used AND i<=totaltokens
	    DO IF i>eofile AND tokeninfo[i-eofile].index = 0 THEN EXIT; i ← i+1;
	    ENDLOOP;
	  IF i <= totaltokens THEN
	    BEGIN defflag:BOOLEAN ← 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 > eofile AND tokeninfo[i-eofile].index = 0 THEN j ← j+2;
	      IF j # 0 THEN
	        BEGIN
		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]
	        END;
	      ENDLOOP;
	    outeol[1]; resetoutstream[];
	    IF defflag THEN
	      Error[check+3,-2,InputLoc[]]; -- nonterminal used but not defined
	    END;
	  FOR i IN [1..numprod]
	    DO IF prodinfo[i].chain THEN
	    IF prodinfo[i].count # 1 OR rhschar[prodinfo[i].index] <= eofile THEN
	      BEGIN Error[warning+2, -(i+specErrorCases), InputLoc[]];
	      prodinfo[i].chain ← FALSE;
	      END;
	    ENDLOOP;
	  END;

       1  => -- head           ::= directives terminals nonterminals "||TABLE4"
	     -- head           ::= directives terminals nonterminals aliases "||TABLE4"
	  BEGIN totaltokens ← lastSymbol; PrintTableHead['4];
	  tokeninfo ← LOOPHOLE[makearray[totaltokens-eofile+1,SIZE[tokenrecord]]];
	  prodinfo ← LOOPHOLE[makearray[maxprod+1,SIZE[prodrecord]]];
	  rhschar ← LOOPHOLE[makearray[maxrhssymbols+1,SIZE[CARDINAL]]];
	  FOR i IN [0..totaltokens-eofile]
	    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
	    BEGIN outeol[1]; outchar[' ,20]; outstring["GOAL ::= "];
	    [] ← outtoken[eofile+1]; outchar[' ,1]; [] ← outtoken[eofile]
	    END;
	  rhschar[0] ← eofile+1; rhschar[1] ← eofile;
	  syminfo[eofile+1].used ← syminfo[eofile].used ← TRUE;
	  prix ← 1; chix ← 2; lhsDef ← 0; rhsFlag ← FALSE;
	  END;

       2  => -- directives     ::=
	  BEGIN FOR opt IN [FIRST[Options]..LAST[Options]]
	    DO flags[opt] ← FALSE ENDLOOP;
	  l[top] ← InputLoc[];
	  END;

       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"
	  BEGIN
	  IF flags[echo] THEN
	    BEGIN outeol[1];
	    FOR opt IN [FIRST[Options]..LAST[Options]]
	      DO IF flags[opt] THEN outstring[
	        SELECT opt FROM
	          echo => "||INPUT "L,
	          chain => "||CHAIN "L,
	          lists => "||LISTS "L,
	          printlr => "||PRINTLR "L,
	          printlalr => "||PRINTLALR "L,
	          first => "||FIRST "L,
	          ENDCASE => "||IDS "L];
	      ENDLOOP;
	    END;
	  PrintTableHead['1];
	  END;

      11  => -- terminals      ::= terminals discard symbol
	     -- nonterminals   ::= nonterminals discard symbol 
	  BEGIN lastSymbol ← v[top+2];
	  IF flags[echo] THEN
	    BEGIN outnum[lastSymbol, 3]; outchar[' , 2];
	    [] ← outtoken[lastSymbol];
	    outeol[1]
	    END;
	  END;

      12  => -- nonterminals   ::= "||TABLE2"
	  BEGIN PrintTableHead['2]; eofile ← lastSymbol;
	  nextalias ← 0; -- assert TABLE3 empty in case it is omitted
	  END;

      13  => -- aliases        ::= "||TABLE3"
	  BEGIN PrintTableHead['3];
	  aliases ← LOOPHOLE[makearray[64,SIZE[aliasrecord]]];
	  END;

      14  => -- aliases        ::= aliases symbol symbol
	  BEGIN IF v[top+1]>eofile THEN Error[check+7,v[top+1],InputLoc[]];
	  IF v[top+2]<=eofile THEN Error[check+8,v[top+2],InputLoc[]];
	  IF nextalias=LENGTH[aliases] THEN
		aliases ← LOOPHOLE[expand[aliases,SIZE[aliasrecord],LENGTH[aliases]/8]];
	  aliases[nextalias] ← [v[top+1],v[top+2]]; nextalias ← nextalias+1;
	  IF flags[echo] THEN
	    BEGIN outchar[' ,tokensize+1-outtoken[v[top+1]]]; outchar[' ,1];
	    j ← v[top+2]*tokensize;
	    FOR i IN [j..j+tokensize) WHILE symtab[i]#0C DO outchar[symtab[i],1] ENDLOOP;
	    outeol[1];
	    END;
	  END;

      15  => -- discard        ::=
	  l[top] ← InputLoc[]; -- keep the parser error recovery happy

      16  => -- rulegroup      ::= symbol "::="
	  {SetRuleChain[prix, FALSE]; LhsSymbol[v[top]]};

      17  => -- rulegroup      ::= prefix symbol "::="
	  LhsSymbol[v[top+1]];

      18  => -- rulegroup      ::= rulegroup symbol "::="
	  {SetRuleChain[prix, FALSE]; LhsSymbol[v[top+1]]};

      19  => -- rulegroup      ::= rulegroup prefix symbol "::="
	  LhsSymbol[v[top+2]];

      20  => -- rulegroup      ::= rulegroup '|
	  {SetRuleChain[prix, FALSE]; ProdHeader[FALSE]};

      21  => -- rulegroup      ::= rulegroup prefix '|
	  ProdHeader[FALSE];

      22  => -- rulegroup      ::= rulegroup symbol
	  BEGIN i ← v[top+1]; syminfo[i].used ← TRUE;
	  IF i=eofile OR i=eofile+1 THEN Error[check+9,0,InputLoc[]]; --goal symbols
	  IF flags[echo] THEN
	    BEGIN IF lineWidth<syminfo[i].length THEN
	      BEGIN outeol[1]; outchar[' ,tokensize+18];
	      lineWidth ← outbuflim - tokensize-18;
	      END;
	    IF (lineWidth ← (lineWidth-outtoken[i]-1)) > 0 THEN outchar[' , 1];
	    END;
	  IF chix=LENGTH[rhschar] THEN
	    rhschar ← LOOPHOLE[expand[rhschar,SIZE[CARDINAL],LENGTH[rhschar]/8]];
	  rhschar[chix]←i; chix ← chix +1;
	  IF prodinfo[prix-1].count = rhslim
	    THEN {prodinfo[prix-1].count ← 1; rhsFlag ← TRUE};
	  prodinfo[prix-1].count ← prodinfo[prix-1].count+1;
	  END;

      23  => -- prefix         ::= num
	  SetRuleChain[v[top], FALSE];

      24  => -- prefix         ::= num num
              -- prefix         ::= '? num
	  SetRuleChain[v[top+1], FALSE];

      25  => -- prefix         ::= discard 'C
	  SetRuleChain[prix, TRUE];

      26  => -- prefix         ::= discard 'C num
	  SetRuleChain[v[top+2], 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;
    END;
 
-- the following procedure is called from the ScanReset if no errors

  FinishInput: PROCEDURE =
    BEGIN emptyflag: BOOLEAN ← TRUE;
    i,j,k,prix,chix: CARDINAL;
    -- compute nonterminals deriving empty
    WHILE emptyflag
      DO emptyflag ← FALSE;
      FOR i IN [1..totaltokens-eofile]  -- each nonterminal
        DO IF tokeninfo[i].empty THEN LOOP;  --which does not derive empty
        j ← tokeninfo[i].index;
        FOR prix IN [j..j+tokeninfo[i].count)
            -- each production of the nonterminal
	DO k ← prodinfo[prix].index;
	FOR chix IN [k..k+prodinfo[prix].count)   -- each rhs character
	  DO
           IF rhschar[chix]<=eofile OR  ~tokeninfo[rhschar[chix]-eofile].empty THEN EXIT;
	  REPEAT FINISHED =>
	    {tokeninfo[i].empty ← emptyflag ← TRUE; EXIT};
	  ENDLOOP
	ENDLOOP
        ENDLOOP
      ENDLOOP;
    checkout[];
    END;

    -- 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 =
    BEGIN i: CARDINAL;
    IF flags[ids] THEN
      BEGIN seterrstream[]; outeol[1];
      outstring["       PRODUCTIONINFO     TOKENINFO    SYMINFO"];
      FOR i IN [0..MAX[numprod,totaltokens]]
        DO outeol[1]; outnum[i,4]; outchar[' ,1];
        IF i>numprod THEN outchar[' ,20] ELSE
	BEGIN 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];
	END;
        IF i<=totaltokens AND i>0 THEN
	BEGIN IF i<=eofile THEN outchar[' ,11] ELSE
	  BEGIN outnum[tokeninfo[i-eofile].count,3];
           outchar[IF tokeninfo[i-eofile].empty THEN 'E ELSE ' ,1];
	  outnum[tokeninfo[i-eofile].index,5]; outchar[' ,2]
	  END;
	outnum[syminfo[i].link,4]; outchar[IF syminfo[i].used THEN 'U ELSE ' ,1];
	outnum[syminfo[i].length,3]; outchar[' ,1]; [] ← outtoken[i];
	END
        ENDLOOP;
      outeol[1]; outstring["RHSCHAR"]; lineWidth ← outbuflim;
      FOR i 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[];
      END;
    END;


 -- error recovery

  TokenValue: PUBLIC PROC [s: TSymbol] RETURNS [UNSPECIFIED] = {RETURN [0]};

-- text input and error routines

  NUL: CHARACTER = 0C;

  tB: POINTER TO PACKED ARRAY OF CHARACTER;
  tI, tMax: [0..TextChars];
  tOrigin, tLimit: CARDINAL;
  tEnded: BOOLEAN;

  FillTextBuffer: PROC =
    BEGIN tOrigin ← tLimit;
    IF tEnded THEN  tMax ← 0 ELSE
	{[tB, tMax, tEnded] ← nextbuffer[]; tLimit ← tOrigin + tMax};
    IF tMax = 0 THEN {tB[0] ← NUL;  tMax ← 1};
    tI ← 0;
    END;


  buffer: STRING = [tokensize];		-- token assembly area

  nTokens: CARDINAL;			-- token count
  nErrors: CARDINAL;			-- lexical errors


  char: CHARACTER;	-- current (most recently scanned) character

  NextChar: PROC =	-- also expanded inline within Atom
    BEGIN IF (tI←tI+1) = tMax THEN FillTextBuffer[];
    char ← tB[tI];
    END;


  Atom: PUBLIC PROC RETURNS [t: Token] =
    BEGIN OPEN t;

    LocateToken: PROC [string: STRING] RETURNS [CARDINAL] =
      BEGIN -- returns token corresponding to string
      i,j,k: CARDINAL;
      j ← hashChain[(string.length*256+LOOPHOLE[string[0],CARDINAL])
		MOD (symtabsize/4) + 1];
      WHILE j # 0
        DO IF syminfo[j].length = string.length THEN
	 BEGIN i ← j*tokensize;
	 FOR k IN [0..string.length)
	   DO IF symtab[i+k]#string[k] THEN EXIT
	   REPEAT FINISHED => RETURN[j]
	   ENDLOOP;
	 END;
        j ← syminfo[j].link;
        ENDLOOP;
      RETURN [0]
      END;

    TokenToSymTab: PROC [string: STRING, token: CARDINAL] =
      BEGIN 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+LOOPHOLE[string[0],CARDINAL])
		MOD (symtabsize/4) + 1;
      syminfo[token].link ← hashChain[j]; hashChain[j] ← token;
      END;

    TokenOrId: PROC [sub:CARDINAL]=
      BEGIN j, s1, s2: CARDINAL;
      h: HashIndex;
      WHILE char ~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,index]}; --overlength
      IF sub = 1 THEN
        BEGIN class ← scanTab[buffer[0]];
        IF class # 0 THEN RETURN
        END;
      j ← buffer[0] - 0C;
      h ← ((j*128-j) + CARDINAL[buffer[sub-1]-0C]) MOD LAST[HashIndex] + 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[s2] THEN EXIT;
	   s2 ← s2+1;
	   REPEAT FINISHED =>
	     BEGIN IF j = tokenTAB3 THEN insertFlag ← 2 ELSE
	       IF j = tokenTAB4 THEN insertFlag ← 3;
	     IF j<=tokenNUM THEN EXIT;
	     class ← j; RETURN 
	     END;
	   ENDLOOP;
        IF (h ← hashTab[h].link) = 0 THEN EXIT;
        ENDLOOP;
      buffer.length←sub; class←tokenID; value←LocateToken[buffer];
      SELECT insertFlag FROM
        1 =>
	-- reading terminals and nonterminals
	IF value # 0 THEN Error[check+2,value,index]  -- multiply defined symbol
	  ELSE
	    BEGIN IF token=LENGTH[symtab] THEN
	      BEGIN symtab ← LOOPHOLE[
		       expand[LOOPHOLE[symtab],wordsfortoken,LENGTH[symtab]/16]];
	      syminfo ← LOOPHOLE[expand[syminfo,SIZE[symtabrecord],LENGTH[syminfo]/16]];
	      END;
	    TokenToSymTab[buffer, token]; value ← token; token ← token+1;
	    END;
        2 =>
	-- processing aliases
	IF value=0 THEN
	  BEGIN s1 ← token*tokensize;
	  IF token=LENGTH[symtab] THEN
	    symtab ← LOOPHOLE[
		       expand[LOOPHOLE[symtab],wordsfortoken,LENGTH[symtab]/16]];
	  FOR j IN [0..buffer.length) DO symtab[s1+j] ← buffer[j] ENDLOOP;
	  value ← token; token ← token+1;
	  END;
        3 =>
	-- processing productions
          IF value = 0 THEN Error[check+3,-3,index];  --symbol not defined
        ENDCASE;
      END;

    DO WHILE char IN [NUL..' ] 
      DO SELECT char FROM
         ControlZ =>
	  UNTIL char = CR
	    DO
	    IF (tI←tI+1) = tMax
	      THEN {IF tEnded THEN GO TO EndFile; FillTextBuffer[]};
	    char ← tB[tI];
	    ENDLOOP;
         ENDCASE;
      IF (tI←tI+1) = tMax
        THEN {IF tEnded THEN GO TO EndFile; FillTextBuffer[]};
      char ← tB[tI];
      ENDLOOP;
    index ← tOrigin + tI;  value ← 0;

    SELECT char FROM
      IN ['0..'9] =>
        BEGIN val:CARDINAL; valid:BOOLEAN;
        maxval:CARDINAL=6553; maxd:CARDINAL = 5;
        valid ← TRUE; val ← 0;
        WHILE char IN ['0..'9]
	  DO IF valid THEN
	    BEGIN d:[0..9];
	    d ← char-'0; val ← 10*val +d;
	    valid ← val<maxval OR (val=maxval AND d<=maxd);
	    END;
	  NextChar[];
	  ENDLOOP;
        IF ~valid THEN val ←maxval*10+maxd;
        class ← tokenNUM; value ← val; GO TO GotNext;
        END;
      '- =>
        BEGIN pChar: CHARACTER;
        NextChar[];
        IF char # '- THEN {buffer[0] ← '-; TokenOrId[1]; GO TO GotNext};
        char ← NUL;
	  DO  pChar ← char;
	  IF (tI←tI+1) = tMax
	    THEN {IF tEnded THEN GO TO EndFile; FillTextBuffer[]};
	  char ← tB[tI];
	  SELECT char FROM
	    '- =>  IF pChar = '- THEN EXIT;
	    CR =>  EXIT;
	    ENDCASE;
	  ENDLOOP;
        NextChar[];
        END;
      ENDCASE => {TokenOrId[0]; GO TO GotNext};
    REPEAT
      GotNext =>  NULL;
      EndFile =>
        BEGIN
        FillTextBuffer[];  char ← tB[tI];
        class ← EndMarker;  index ← tOrigin;  value ← 0;
        END;
    ENDLOOP;
    nTokens ← nTokens + 1;
    RETURN
    END;


 -- initialization/finalization

  ScanInit: PUBLIC PROC [tablePtr: ParseTable.Handle] =
    BEGIN i: CARDINAL;
    hashTab ← @tablePtr.scanTable.hashTab;
    scanTab ← @tablePtr.scanTable.scanTab;
    vocab ← LOOPHOLE[@tablePtr.scanTable.vocabBody, STRING];
    vocabIndex ← @tablePtr.scanTable.vocabIndex;
    tLimit ← 0;  tMax ← 0;  tEnded ← FALSE;
    FillTextBuffer[];  char ← tB[tI];
    nTokens ← nErrors ← 0; buffer.length ← tokensize;

    -- initialise symbol table
    token ← 1; insertFlag ← 1;
    symtab ← LOOPHOLE[makearray[symtabsize+1,wordsfortoken]];
    syminfo ← LOOPHOLE[makearray[symtabsize+1,SIZE[symtabrecord]]];
    FOR i IN [1..symtabsize]
      DO syminfo[i] ← [link:0, length:0, used:FALSE] ENDLOOP;
    hashChain ← DESCRIPTOR[AllocateSegment[symtabsize/4],symtabsize/4];
    FOR i IN [1..symtabsize/4] DO hashChain[i] ← 0 ENDLOOP;
    END;

  ScanReset: PUBLIC PROC [pErrors: CARDINAL] RETURNS [CARDINAL, CARDINAL] =
    BEGIN
    IF (pErrors+nErrors)=0 THEN FinishInput[]; RETURN [nTokens, nErrors]
    END;

 -- error handling

  ResetScanIndex: PUBLIC PROC [index: CARDINAL] =
    BEGIN 
    IF index = tLimit THEN {FillTextBuffer[]; char ← tB[tI]; RETURN};
    IF index < tOrigin THEN
	BEGIN tLimit ← locateindex[index]; tMax ← 0;  tEnded ← FALSE;
	FillTextBuffer[];
	END;
    tI ← index - tOrigin; char ← tB[tI];
    END;

  Error: PROC [code: CARDINAL, control: INTEGER, index: CARDINAL]=
    BEGIN
    ErrorContext[ SELECT code FROM
      1  => "WARNING - Overlength symbol (increase TOKENSIZE?) truncated to - "L,
      2  => "WARNING - Not a chain production - "L,
      3  => "WARNING - Unused(U) or undefined(D)symbols (refer to TABLE1 and 2)"L,
      4  => "ERROR - Nonterminal with too many rules (increase ALTERNATELIM?) - "L,
      5  => "ERROR - Multiple definitions of symbol - "L,
      6  => "ERROR - Symbol not defined - "L,
      7  => "ERROR - Terminal precedes ::= - "L,
      8  => "ERROR - Too many rhs symbols in production (increase RHSLIM?) - "L,
      9  => "ERROR - Internal field will overflow - increase PSSLIM"L,
      10 => "ERROR - Aliased symbol not a terminal symbol - "L,
      11 => "ERROR - Aliases must not be terminal symbols - "L,
      12 => "ERROR - Goal symbols used in rhs"L,
      13 => "ERROR - Number greater than "L,
      ENDCASE => NIL,
      index];
    IF code>check THEN nErrors ← nErrors+1;
    SELECT -control FROM
      <0 => [] ← outtoken[control];
       0 => NULL;
       1 => outstring[buffer];
       2 => outstring["see previous message"L];
       3 => {outstring[buffer]; outstring[" not in TABLE1 or 2"L]};
       4 => NULL;  -- not used
       5 => outstring["MAXRULE"L];
       ENDCASE => outnum[-control-specErrorCases, 5];
    outeol[2];
    IF code<=check THEN warningslogged ← TRUE;
    resetoutstream[];
    END;

  END.