"AL HARTMANN
 INFORMATION SCIENCE
 CALIFORNIA INSTITUTE OF TECHNOLOGY
 PASADENA, CALIFORNIA 91125

 PDP 11/45 SEQUENTIAL PASCAL
 COMPILER PASS 1: LEXICAL ANALYSIS

 MARCH 1975"
(CHECK, NUMBER)

"###########
#  PREFIX  #
###########"

CONST              EOL = '(:10:)';     FF = '(:12:)';      EOM = '(:25:)';
PRINTLIMIT = 18;   MAXDIGIT = 6;
WORDLENGTH = 2 "BYTES";
REALLENGTH = 8 "BYTES";
SETLENGTH = 16 "BYTES";
SPLITLENGTH = 4 "WORDS PER SPLIT REAL";
MAX_STRING_LENGTH = 80 "CHARS";
WORDS_PER_STRING = 40 "MAX_STRING_LENGTH DIV WORDLENGTH";
LISTOPTION = 0;    SUMMARYOPTION = 1;  TESTOPTION = 2;     CHECKOPTION = 3;
CODEOPTION = 4;    NUMBEROPTION = 5;

"*****************************   CAUTION   ************************************"
"THE 'LARGEST_REAL' PROCEDURE IS MACHINE DEPENDANT. IT MAY HAVE TO BE CHANG&D  "
"IF THE COMPILER IS MOVED TO ANOTHER MACHINE .                                 "

TYPE FILE = 1..2;

CONST IDLENGTH = 16;
TYPE IDENTIFIER = ARRAY (.1..IDLENGTH.) OF CHAR;

TYPE   POINTER = @ INTEGER;
OPTION = LISTOPTION..NUMBEROPTION;
PASSPTR = @PASSLINK;
PASSLINK =
  RECORD
    OPTIONS: SET OF OPTION;
    LABELS, BLOCKS, CONSTANTS, RESETPOINT: INTEGER;
    TABLES: POINTER
  END;

TYPE ARGTAG =
  (NILTYPE, BOOLTYPE, INTTYPE, IDTYPE, PTRTYPE);

TYPE ARGTYPE = RECORD
                 CASE TAG: ARGTAG OF
                   NILTYPE, BOOLTYPE: (BOOL: BOOLEAN);
                   INTTYPE: (INT: INTEGER);
                   IDTYPE: (ID: IDENTIFIER);
                   PTRTYPE: (PTR: PASSPTR)
               END;

CONST MAXARG = 10;
TYPE ARGLIST = ARRAY (.1..MAXARG.) OF ARGTYPE;

CONST PAGELENGTH = 256;
TYPE PAGE = ARRAY (.1..PAGELENGTH.) OF INTEGER;

PROCEDURE READ(VAR C: CHAR);
PROCEDURE WRITE(C: CHAR);
PROCEDURE NOTUSED1;
PROCEDURE NOTUSED2;
PROCEDURE GET(F: FILE; P: INTEGER; VAR BLOCK: UNIV PAGE);
PROCEDURE PUT(F: FILE; P: INTEGER; BLOCK: UNIV PAGE);
FUNCTION FILE_LENGTH(F:FILE): INTEGER;
PROCEDURE MARK(VAR TOP: INTEGER);
PROCEDURE RELEASE(TOP: INTEGER);

PROGRAM MAIN(VAR PARAM: ARGLIST);

"#############################################
#  PASS(VAR OK: BOOLEAN; VAR LINK: POINTER)  #
#############################################"

CONST

"OUTPUT OPERATORS"

EOM2=0;            BEGIN2=1;           IF2=2;              CASE2=3;
WHILE2=4;          REPEAT2=5;          FOR2=6;             WITH2=7;
ID2=8;             REAL2=9;            STRING2=10;         INTEGER2=11;
CHAR2=12;          OPEN2=13;           NOT2=14;            SUB2=15;
SET2=16;           ARRAY2=17;          RECORD2=18;         ARROW2=19;
PERIOD2=20;        STAR2=21;           SLASH2=22;          DIV2=23;
MOD2=24;           AND2=25;            PLUS2=26;           MINUS2=27;
OR2=28;            EQ2=29;             NE2=30;             LE2=31;
GE2=32;            LT2=33;             GT2=34;             IN2=35;
CONST2=36;         TYPE2=37;           VAR2=38;            PROCEDURE2=39;
FUNCTION2=40;      PROGRAM2=41;        SEMICOLON2=42;      CLOSE2=43;
UP_TO2=44;         OF2=45;             COMMA2=46;          BUS2=47;
COLON2=48;         END2=49;            FORWARD2=50;        UNIV2=51;
BECOMES2=52;       THEN2=53;           ELSE2=54;           DO2=55;
UNTIL2=56;         TO2=57;             DOWNTO2=58;         LCONST2=59;
MESSAGE2=60;       NEW_LINE2=61;

"OTHER CONSTANTS"

"ERRORS"

COMMENT_ERROR=1;   NUMBER_ERROR=2;     INSERT_ERROR=3;     STRING_ERROR=4;
CHAR_ERROR=5;

"STANDARD SPELLING/NOUN INDICES"

XUNDEF=0;          XFALSE=1;           XTRUE=2;            XINTEGER=3;
XBOOLEAN=4;        XCHAR=5;            XNIL=6;             XABS=7;
XATTRIBUTE=8;      XCHR=9;             XCONV=10;           XORD=11;
XPRED=12;          XSUCC=13;           XTRUNC=14;          XNEW=15;
XREAL=16;

ID_PIECE_LENGTH=9; "TEN CHARS PER PIECE"
MAX_PIECES = 13;  "FOURTEEN PIECES => 140 CHARS"
TEST_MAX = 50;
NULL=32767; "SYMBOL"
SPAN=26; "NUMBER OF DISTINCT ID CHARS"
THIS_PASS=1;
TEXT_LENGTH = 18;
INFILE = 1;        OUTFILE = 2;
HASH_MAX=750; "HASH TABLE UPPER BOUND"
HASH_MAX1=751; "PRIME LENGTH OF HASH TABLE"
MAX_INDEX=700; "MAX_LOADING=0.98 * HASH_MAX1-NO. OF RES.WDS."

MIN_ORD=0;         MAX_ORD=127;        MAX_INTEGER=32767;
INTEGER_LIMIT="(MAX_INTEGER-9) DIV 10" 3275;
MAX_EXPONENT=38;

TYPE

  SPLITREAL = ARRAY (.1..SPLITLENGTH.) OF INTEGER;

  TEXT_TYPE = ARRAY (.1..TEXT_LENGTH.) OF CHAR;

  PACKED_STRING = ARRAY (.1..WORDS_PER_STRING.) OF INTEGER;

  ALFA=ARRAY (.1..10.) OF CHAR;

  SPELLING_INDEX=INTEGER;

  PIECE=ARRAY(.0..ID_PIECE_LENGTH.) OF CHAR;

  PIECE_PTR=@ID_PIECE;

  ID_PIECE=
    RECORD
      PART:PIECE;
      NEXT:PIECE_PTR
    END;

VAR

  REAL0, REAL1, REAL10, MAX_REAL, REAL_LIMIT: REAL;

  INTER_PASS_PTR:PASSPTR;

  CH:CHAR;

  LETTERS, DIGITS, ALFAMERICS, NON_ALFAS,
  STRING_SPECIAL: SET OF CHAR;

  TEST, UPTO_SW, BUS_SW, END_SCAN: BOOLEAN;

  CL1,CL2,CL3,CL4 "LINE NUMBER": CHAR;

  LINE_NO:INTEGER;

  PIECES: INTEGER;      "ID LENGTH IN PIECES"

  TEST_BUF: ARRAY (.1..TEST_MAX.) OF INTEGER;

  TEST_INDEX: INTEGER;

  ID_TEXT: ARRAY(.0..MAX_PIECES.) OF PIECE;

  BLANK: PIECE "BLANK PADDING";

  CHAR_INDEX:0..ID_PIECE_LENGTH "CURRENT CHAR INDEX";

  SYMB: INTEGER "ID SYMBOL";

  STRING_LENGTH:INTEGER;

  HASH_KEY: 0..HASH_MAX; "INDEX TO HASH_TABLE"

  CURRENT_INDEX  "LAST ASSIGNED INDEX",
  INDEX  "LAST SCANNED INDEX"  : SPELLING_INDEX;

  STRING_TEXT: ARRAY (.1..MAX_STRING_LENGTH.) OF CHAR;

  HASH_TABLE:
    ARRAY (.0..HASH_MAX.) OF
      RECORD
        SPIX:SPELLING_INDEX;
        NAME:ID_PIECE
      END;

"############################"
"COMMON TEST OUTPUT MECHANISM"
"############################"

PRINTED: INTEGER;

OK: BOOLEAN;
  "PASS1 TO 6:  OK = NOT DISK OVERFLOW
   PASS7:       OK = NOT DISK OVERFLOW & PROGRAM CORRECT"

PAGE_IN: PAGE;  PAGES_IN, WORDS_IN: INTEGER;
PAGE_OUT: PAGE;  PAGES_OUT, WORDS_OUT: INTEGER;

PROCEDURE PRINT_TEXT (TEXT: TEXT_TYPE);
VAR I: INTEGER;
BEGIN
  WRITE(EOL);
  FOR I:= 1 TO TEXT_LENGTH DO WRITE(TEXT(.I.));
  WRITE(EOL)
END;

PROCEDURE FILE_LIMIT;
BEGIN
  PRINT_TEXT('PASS 1: FILE_LIMIT');
  OK:= FALSE
END;

PROCEDURE INIT_PASS (VAR LINK: PASSPTR);
BEGIN
  LINK:= PARAM(.2.).PTR;
  OK:= TRUE;
  PAGES_IN:= 1; WORDS_IN:= PAGELENGTH;
  PAGES_OUT:= 1; WORDS_OUT:= 0
END;

PROCEDURE NEXT_PASS (LINK: PASSPTR);
BEGIN
  IF WORDS_OUT > 0 THEN
    IF PAGES_OUT > FILE_LENGTH(OUTFILE) THEN FILE_LIMIT
      ELSE PUT(OUTFILE, PAGES_OUT, PAGE_OUT);
  WITH PARAM(.1.) DO BEGIN
    TAG:= BOOLTYPE; BOOL:=OK END;
  WITH PARAM(.2.) DO BEGIN
    TAG:= PTRTYPE; PTR:= LINK END;
  WITH PARAM(.4.) DO BEGIN
    TAG:= INTTYPE;  INT:= PAGES_OUT  END;
  WITH PARAM(.5.) DO BEGIN
    TAG:= BOOLTYPE; BOOL:= CH = EOM END;
END;

PROCEDURE READ_IFL (VAR I: INTEGER);
BEGIN
  IF WORDS_IN = PAGELENGTH THEN BEGIN
    IF PAGES_IN > FILE_LENGTH(INFILE) THEN FILE_LIMIT
    ELSE BEGIN
      GET(INFILE, PAGES_IN, PAGE_IN);
      PAGES_IN:= SUCC(PAGES_IN)
    END;
    WORDS_IN:= 0
  END;
  WORDS_IN:= SUCC(WORDS_IN);
  I:= PAGE_IN(.WORDS_IN.)
END;

PROCEDURE WRITE_IFL (I: INTEGER);
BEGIN
  WORDS_OUT:= SUCC(WORDS_OUT);
  PAGE_OUT(.WORDS_OUT.):= I;
  IF WORDS_OUT = PAGELENGTH THEN BEGIN
    IF PAGES_OUT > FILE_LENGTH(OUTFILE) THEN FILE_LIMIT
    ELSE BEGIN
      PUT(OUTFILE, PAGES_OUT, PAGE_OUT);
      PAGES_OUT:= SUCC(PAGES_OUT)
    END;
    WORDS_OUT:= 0
  END
END;

PROCEDURE SPLIT (INPUT: UNIV SPLITREAL; VAR OUTPUT: SPLITREAL);
BEGIN
  OUTPUT:= INPUT
END;

PROCEDURE LARGEST_REAL (VAR MAX: UNIV SPLITREAL);
BEGIN
  MAX(.1.):= 32767;
  MAX(.2.):= -1;
  MAX(.3.):= MAX(.2.);
  MAX(.4.):= MAX(.3.)
END;

PROCEDURE PRINTABS(ARG: INTEGER);
VAR T: ARRAY (.1..MAXDIGIT.) OF CHAR; REM, DIGIT, I: INTEGER;
BEGIN
  REM:= ARG; DIGIT:= 0;
  REPEAT
    DIGIT:= DIGIT + 1;
    T(.DIGIT.):= CHR(ABS(REM MOD 10) + ORD('0'));
    REM:= REM DIV 10;
  UNTIL REM = 0;
  FOR I:= DIGIT DOWNTO 1 DO WRITE(T(.I.));
  FOR I:= DIGIT + 1 TO MAXDIGIT DO WRITE(' ');
END;

PROCEDURE PRINTFF;
BEGIN WRITE(FF); PRINTED:= 0 END;

PROCEDURE PRINTEOL;
BEGIN WRITE(EOL); PRINTED:= 0 END;

PROCEDURE PRINTOP(OP: INTEGER);
BEGIN
  IF PRINTED = PRINTLIMIT THEN PRINTEOL;
  WRITE('C'); PRINTABS(OP);
  PRINTED:= PRINTED + 1;
END;

PROCEDURE PRINTARG(ARG: INTEGER);
BEGIN
  IF PRINTED = PRINTLIMIT THEN PRINTEOL;
  IF ARG < 0 THEN WRITE('-') ELSE WRITE(' ');
  PRINTABS(ARG);
  PRINTED:= PRINTED + 1;
END;

PROCEDURE STORE_TEST (ARG: INTEGER);
BEGIN
  IF TEST_INDEX < TEST_MAX THEN BEGIN
    TEST_INDEX:= TEST_INDEX + 1;
    TEST_BUF(.TEST_INDEX.):= ARG
  END
END;

PROCEDURE PRINT_TEST;
VAR I: INTEGER;
BEGIN
    PRINTED:= PRINTLIMIT;
  FOR I:= 1 TO TEST_INDEX DO PRINTARG(TEST_BUF(.I.));
  TEST_INDEX:= 0
END;

"NOTE: A PASS RUNNING WITH TEST OUTPUT SHOULD START
 BY CALLING PROCEDURE PRINTFF"

  PROCEDURE PUT_ARG(ARG:INTEGER);
  BEGIN
    WRITE_IFL(ARG);
    IF TEST THEN STORE_TEST(ARG)
  END;

  PROCEDURE PUT0NC(OP:INTEGER);
  BEGIN
    WRITE_IFL(OP);
    IF TEST THEN STORE_TEST(OP);
    WRITE(CH); READ(CH)
  END;

  PROCEDURE PUT0(OP:INTEGER);
  BEGIN
    WRITE_IFL(OP);
    IF TEST THEN STORE_TEST(OP)
  END;

  PROCEDURE PUT1(OP,ARG:INTEGER);
  BEGIN
    WRITE_IFL(OP); WRITE_IFL(ARG);
    IF TEST THEN BEGIN
      STORE_TEST(OP);  STORE_TEST(ARG)
    END
  END;

  PROCEDURE PUT2(OP,ARG1,ARG2:INTEGER);
  BEGIN
    WRITE_IFL(OP); WRITE_IFL(ARG1); WRITE_IFL(ARG2);
    IF TEST THEN BEGIN
      STORE_TEST(OP);  STORE_TEST(ARG1);  STORE_TEST(ARG2)
    END
  END;

  PROCEDURE PUT_STRING (STRING: UNIV PACKED_STRING; STRING_LENGTH: INTEGER);
  VAR I: INTEGER;
  BEGIN
    PUT1(STRING2, STRING_LENGTH);  PUT1(LCONST2, STRING_LENGTH);
    FOR I:= 1 TO STRING_LENGTH DIV WORDLENGTH DO
      PUT_ARG(STRING(.I.))
  END;

  PROCEDURE ERROR(ERROR_NUM:INTEGER);
  BEGIN
    PUT2(MESSAGE2,THIS_PASS,ERROR_NUM)
  END;

"##########"
"INITIALIZE"
"##########"

  PROCEDURE STD_ID(ID:PIECE; INDEX:SPELLING_INDEX);
  VAR S:SPELLING_INDEX; CHAR_INDEX:INTEGER;
  BEGIN
    HASH_KEY:=1;
    FOR CHAR_INDEX:=0 TO ID_PIECE_LENGTH DO
      IF ID(.CHAR_INDEX.)<>' ' THEN
        HASH_KEY:=HASH_KEY*(ORD(ID(.CHAR_INDEX.)) MOD SPAN +1) MOD HASH_MAX1;
    WHILE HASH_TABLE(.HASH_KEY.).SPIX<>NULL DO
      HASH_KEY:=(HASH_KEY+1) MOD HASH_MAX1;
    "NOW WE HAVE ENTRY SLOT"
    WITH HASH_TABLE(.HASH_KEY.) DO BEGIN
      SPIX:=INDEX;
      WITH NAME DO BEGIN PART:=ID; NEXT:=NIL END
    END
  END;

  PROCEDURE LONG_STD_ID(ID1,ID2:PIECE; INDEX:SPELLING_INDEX);
  VAR CHAR_INDEX:INTEGER;
  BEGIN
    HASH_KEY:=1;
    FOR CHAR_INDEX:=0 TO ID_PIECE_LENGTH DO
      HASH_KEY:=HASH_KEY*(ORD(ID1(.CHAR_INDEX.)) MOD SPAN +1) MOD HASH_MAX1;
    FOR CHAR_INDEX:=0 TO ID_PIECE_LENGTH DO
      IF ID2(.CHAR_INDEX.)<>' ' THEN
        HASH_KEY:=HASH_KEY*(ORD(ID2(.CHAR_INDEX.)) MOD SPAN+1) MOD HASH_MAX1;
    WHILE HASH_TABLE(.HASH_KEY.).SPIX<>NULL DO
      HASH_KEY:=(HASH_KEY+1) MOD HASH_MAX1;
    WITH HASH_TABLE(.HASH_KEY.) DO BEGIN
      SPIX:=INDEX;
      WITH NAME DO BEGIN
        PART:=ID1; NEW(NEXT);
        WITH NEXT@ DO BEGIN PART:=ID2; NEXT:=NIL END
      END
    END
  END;

  PROCEDURE STD_NAMES;
  BEGIN
    STD_ID('END       ',-END2);
    STD_ID('IF        ',-IF2);
    STD_ID('THEN      ',-THEN2);
    STD_ID('BEGIN     ',-BEGIN2);
    STD_ID('ELSE      ',-ELSE2);
    STD_ID('DO        ',-DO2);
    STD_ID('WITH      ',-WITH2);
    STD_ID('IN        ',-IN2);
    STD_ID('OF        ',-OF2);
    STD_ID('WHILE     ',-WHILE2);
    STD_ID('CASE      ',-CASE2);
    STD_ID('REPEAT    ',-REPEAT2);
    STD_ID('UNTIL     ',-UNTIL2);
    STD_ID('PROCEDURE ',-PROCEDURE2);
    STD_ID('VAR       ',-VAR2);
    STD_ID('FOR       ',-FOR2);
    STD_ID('ARRAY     ',-ARRAY2);
    STD_ID('RECORD    ',-RECORD2);
    STD_ID('SET       ',-SET2);
    STD_ID('TO        ',-TO2);
    STD_ID('DOWNTO    ',-DOWNTO2);
    STD_ID('MOD       ',-MOD2);
    STD_ID('OR        ',-OR2);
    STD_ID('AND       ',-AND2);
    STD_ID('NOT       ',-NOT2);
    STD_ID('DIV       ',-DIV2);
    STD_ID('CONST     ',-CONST2);
    STD_ID('TYPE      ',-TYPE2);
    STD_ID('FUNCTION  ',-FUNCTION2);
    STD_ID('FORWARD   ',-FORWARD2);
    STD_ID('UNIV      ',-UNIV2);
    STD_ID('PROGRAM   ',-PROGRAM2);
    STD_ID('FALSE     ',XFALSE);
    STD_ID('TRUE      ',XTRUE);
    STD_ID('INTEGER   ',XINTEGER);
    STD_ID('BOOLEAN   ',XBOOLEAN);
    STD_ID('CHAR      ',XCHAR);
    STD_ID('NIL       ',XNIL);
    STD_ID('NEW       ',XNEW);
    STD_ID('ABS       ',XABS);
    STD_ID('ATTRIBUTE ',XATTRIBUTE);
    STD_ID('CHR       ',XCHR);
    STD_ID('CONV      ',XCONV);
    STD_ID('ORD       ',XORD);
    STD_ID('PRED      ',XPRED);
    STD_ID('SUCC      ',XSUCC);
    STD_ID('TRUNC     ',XTRUNC);
    STD_ID('REAL      ',XREAL);
  END;

  PROCEDURE END_LINE;
  VAR I: INTEGER;
  BEGIN
    IF TEST THEN PRINT_TEST;
    WRITE(CH); READ(CH);
    LINE_NO:=LINE_NO+1;
    PUT1(NEW_LINE2,LINE_NO);
      IF CL4<'9' THEN CL4:=CHR(ORD(CL4)+1) ELSE BEGIN
        CL4:='0';
        IF CL3<'9' THEN CL3:=CHR(ORD(CL3)+1) ELSE BEGIN
          CL3:='0';
          IF CL2<'9' THEN CL2:=CHR(ORD(CL2)+1) ELSE BEGIN
            CL2:='0';
            IF CL1<'9' THEN CL1:=CHR(ORD(CL1)+1) ELSE CL1:='0'
          END
        END
      END;
      WRITE(CL1); WRITE(CL2); WRITE(CL3); WRITE(CL4); WRITE(' ');
    IF CH = ' ' THEN REPEAT WRITE(CH); READ(CH) UNTIL CH <> ' '
  END;

  PROCEDURE GET_CHAR(SKIP_FIRST: BOOLEAN);
  BEGIN
    IF SKIP_FIRST THEN BEGIN WRITE(CH); READ(CH) END;
    REPEAT
      IF CH='"' THEN BEGIN
        REPEAT
          REPEAT WRITE(CH); READ(CH) UNTIL (CH=EOL) OR (CH='"');
          WHILE CH = EOL DO END_LINE
        UNTIL (CH=EOM) OR (CH='"');
        IF CH = '"' THEN BEGIN WRITE(CH); READ(CH) END ELSE ERROR(COMMENT_ERROR)
      END;
      WHILE CH = ' ' DO BEGIN WRITE(CH); READ(CH) END;
      WHILE CH=EOL DO END_LINE
    UNTIL (CH<>' ') AND (CH<>'"')
  END;

  PROCEDURE INIT_OPTIONS;
  VAR STOP:SET OF CHAR;
  BEGIN
    END_LINE;
    NEW(INTER_PASS_PTR);
    WITH INTER_PASS_PTR@ DO BEGIN
      OPTIONS:=(.LISTOPTION,CHECKOPTION,NUMBEROPTION.);
      MARK(RESETPOINT);
      TABLES:=NIL;
      GET_CHAR(FALSE);
      IF CH='(' THEN BEGIN
        STOP:=(.',' , ')' , EOM.);
        REPEAT
          GET_CHAR(TRUE);
          IF CH='L' THEN OPTIONS:=OPTIONS-(.LISTOPTION.) ELSE
          IF CH='S' THEN OPTIONS:=OPTIONS OR (.SUMMARYOPTION.) ELSE
          IF CH='T' THEN OPTIONS:=OPTIONS OR (.TESTOPTION.) ELSE
          IF CH='C' THEN OPTIONS:=OPTIONS-(.CHECKOPTION.) ELSE
          IF CH='N' THEN OPTIONS:=OPTIONS-(.NUMBEROPTION.);
          WHILE NOT(CH IN STOP) DO GET_CHAR(TRUE)
        UNTIL (CH=EOM) OR (CH=')');
      IF CH=')' THEN BEGIN WRITE(CH); READ(CH) END
      END;
      IF TESTOPTION IN OPTIONS THEN BEGIN
        TEST:=TRUE;
        TEST_INDEX:= 0
      END
    END
  END;

  PROCEDURE INITIALIZE;
  VAR S:SPELLING_INDEX; C:MIN_ORD..MAX_ORD;
  BEGIN
    TEST:= FALSE;
    "EMPTY SET" PUT1(LCONST2,SETLENGTH);
    FOR S:=1 TO SETLENGTH DIV WORDLENGTH DO PUT_ARG(0);
    REAL0:= CONV(0);  REAL1:= CONV(1);  REAL10:= CONV(10);
    LARGEST_REAL(MAX_REAL);  REAL_LIMIT:= MAX_REAL / REAL10;
    CH:= EOL;
    END_SCAN:=FALSE;
    UPTO_SW:=FALSE; BUS_SW:=FALSE;
    LINE_NO:=0;
    CL1:='0'; CL2:='0'; CL3:='0'; CL4:='0';
    DIGITS:=(.'0','1','2','3','4','5','6','7','8','9'.);
    LETTERS:=(.'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','_'.);
    ALFAMERICS:=LETTERS OR DIGITS;
    NON_ALFAS:= (..);
    FOR C:= MIN_ORD TO MAX_ORD DO NON_ALFAS:= NON_ALFAS OR (.CHR(C).);
    NON_ALFAS:= NON_ALFAS - ALFAMERICS;
    STRING_SPECIAL:= (.'''', EOL, EOM, '('.);
    BLANK:='          ';
    FOR S:=0 TO HASH_MAX DO HASH_TABLE(.S.).SPIX:=NULL;
    CURRENT_INDEX:=XREAL;
    STD_NAMES;
    INIT_OPTIONS;
  END;

"######"
"NUMBER"
"######"

  PROCEDURE NUMBER;
  VAR MANTISSA,POWER_OF_TEN, RESULT: REAL;
    ERROR_SW,EXPONENT_SIGN:BOOLEAN;
    REAL_VAL:SPLITREAL; OP:INTEGER;
    EXPONENT,EXPONENT_PART,I:INTEGER;
  BEGIN
    OP:= INTEGER2;  MANTISSA:= REAL0;  ERROR_SW:= FALSE;  EXPONENT:= 0;
    "COLLECT INTEGER PART"
    REPEAT
      IF MANTISSA<=REAL_LIMIT THEN
        MANTISSA:=MANTISSA*REAL10 + CONV(ORD(CH)-ORD('0'))
      ELSE ERROR_SW:=TRUE;
      WRITE(CH);  READ(CH)
    UNTIL NOT(CH IN DIGITS);
    "COLLECT FRACTIONAL PART"
    IF CH='.' THEN BEGIN
      WRITE(CH); READ(CH);
      IF CH=')' THEN BUS_SW:=TRUE ELSE
      IF CH='.' THEN UPTO_SW:=TRUE ELSE
      BEGIN
        OP:=REAL2;
        IF NOT(CH IN DIGITS) THEN ERROR(NUMBER_ERROR) ELSE
        REPEAT
          IF MANTISSA <= REAL_LIMIT THEN BEGIN
            MANTISSA:=MANTISSA*REAL10 + CONV(ORD(CH)-ORD('0'));
            EXPONENT:=EXPONENT-1
          END;
          WRITE(CH); READ(CH)
        UNTIL NOT(CH IN DIGITS);
      END
    END;
    "COLLECT EXPONENT PART"
    IF CH='E' THEN BEGIN
      OP:=REAL2;
      WRITE(CH); READ(CH);
      EXPONENT_PART:=0; EXPONENT_SIGN:=FALSE;
      IF CH='+' THEN BEGIN WRITE(CH); READ(CH) END ELSE
      IF CH='-' THEN BEGIN
        EXPONENT_SIGN:= TRUE; WRITE(CH); READ(CH)
      END;
      IF NOT(CH IN DIGITS) THEN ERROR(NUMBER_ERROR) ELSE
      REPEAT
        IF EXPONENT_PART<=INTEGER_LIMIT THEN
          EXPONENT_PART:=EXPONENT_PART*10-ORD('0') +ORD(CH)
        ELSE ERROR_SW:=TRUE;
        WRITE(CH); READ(CH)
      UNTIL NOT(CH IN DIGITS);
      "ASSERT EXPONENT <= 0;"
      IF EXPONENT_SIGN THEN
        IF MAX_EXPONENT + EXPONENT >= EXPONENT_PART
        THEN EXPONENT:= EXPONENT - EXPONENT_PART
        ELSE ERROR_SW:= TRUE
        ELSE EXPONENT:=EXPONENT+EXPONENT_PART
    END;
    "NOW CONSTRUCT THE NUMBER"
    IF OP=INTEGER2 THEN BEGIN
      IF MANTISSA>CONV(MAX_INTEGER) THEN BEGIN
        ERROR(NUMBER_ERROR); MANTISSA:= REAL0
      END;
      PUT1(INTEGER2,TRUNC(MANTISSA))
    END ELSE "OP=REAL2" BEGIN
      IF ERROR_SW THEN BEGIN
        ERROR(NUMBER_ERROR);
        SPLIT(REAL0, REAL_VAL)
      END ELSE BEGIN
        "COMPUTE THE APPROPRIATE POWER OF TEN"
        POWER_OF_TEN:=REAL1;
        IF EXPONENT<0 THEN BEGIN
          EXPONENT_SIGN:=TRUE;
          EXPONENT:=ABS(EXPONENT)
        END ELSE EXPONENT_SIGN:=FALSE;
        IF EXPONENT>MAX_EXPONENT THEN BEGIN
          ERROR(NUMBER_ERROR);
          EXPONENT:=0
        END;
        FOR I:=1 TO EXPONENT DO POWER_OF_TEN:=POWER_OF_TEN*REAL10;
        "NOW EITHER MANTISSA=0.0 OR MANTISSA>=1.0"
        IF MANTISSA = REAL0 THEN RESULT:= REAL0 ELSE
        IF EXPONENT_SIGN THEN RESULT:= MANTISSA / POWER_OF_TEN ELSE
        "IF MANTISSA>=1.0 THEN WE MUST HAVE:
          MANTISSA*POWER_OF_TEN<=MAX_REAL
          => POWER_OF_TEN<=MAX_REAL/MANTISSA<=MAX_REAL"
        IF POWER_OF_TEN<=MAX_REAL/MANTISSA THEN
          RESULT:= MANTISSA * POWER_OF_TEN
        ELSE BEGIN
          ERROR(NUMBER_ERROR); RESULT:= REAL0
        END;
        SPLIT(RESULT, REAL_VAL)
      END;
      PUT0(REAL2);
      PUT1(LCONST2,REALLENGTH);
      FOR I:= 1 TO SPLITLENGTH DO PUT_ARG(REAL_VAL(.I.))
    END
  END;

"#######"
"HASHING"
"#######"

  FUNCTION SAME_ID:BOOLEAN;
  VAR SAME:BOOLEAN; THIS_PIECE:PIECE_PTR; I:INTEGER;
  BEGIN
    WITH HASH_TABLE(.HASH_KEY.) DO BEGIN
      SAME:=NAME.PART=ID_TEXT(.0.);
      IF PIECES>0 THEN
        IF SAME THEN BEGIN
          THIS_PIECE:=NAME.NEXT;
          I:=1;
          REPEAT
            IF THIS_PIECE=NIL THEN BEGIN
              SAME:=FALSE "CANDIDATE IS TOO SHORT";
              I:=PIECES+1 "QUIT"
            END ELSE BEGIN "COMPARE AND INCREMENT"
              SAME:=SAME AND (THIS_PIECE@.PART=ID_TEXT(.I.));
              THIS_PIECE:=THIS_PIECE@.NEXT;
              I:=I+1;
            END
          UNTIL I>PIECES;
          SAME:=SAME AND (THIS_PIECE=NIL)
        END;
      SAME_ID:=SAME
    END
  END;

  PROCEDURE INSERT_ID;
  VAR I:INTEGER; P,P1:PIECE_PTR;
  BEGIN
    WITH HASH_TABLE(.HASH_KEY.) DO BEGIN
      CURRENT_INDEX:=CURRENT_INDEX+1;
      IF CURRENT_INDEX>=MAX_INDEX THEN BEGIN
        ERROR(INSERT_ERROR); CH:=EOM; WRITE(EOL)
      END;
      SPIX:=CURRENT_INDEX;
      WITH NAME DO BEGIN PART:=ID_TEXT(.0.); NEXT:=NIL END;
      IF PIECES>0 THEN BEGIN
        NEW(P); NAME.NEXT:=P; P@.PART:=ID_TEXT(.1.);
        FOR I:=2 TO PIECES DO BEGIN
          NEW(P1); P@.NEXT:=P1;
          P1@.PART:=ID_TEXT(.I.); P:=P1
        END;
        P@.NEXT:=NIL
      END
    END
  END;

  PROCEDURE SEARCH_ID;
  VAR FINISHED:BOOLEAN;
  BEGIN
    FINISHED:=FALSE;
    REPEAT
      WITH HASH_TABLE(.HASH_KEY.) DO
        IF SPIX<>NULL THEN
            IF SAME_ID THEN "FOUND IT" BEGIN
              FINISHED:=TRUE;
              IF SPIX>=0 THEN BEGIN
                SYMB:=ID2; INDEX:=SPIX
              END ELSE SYMB:=ABS(SPIX)
            END ELSE HASH_KEY:=(HASH_KEY+1) MOD HASH_MAX1
        ELSE "SYM=NULL" BEGIN
          INSERT_ID;
          SYMB:=ID2;
          INDEX:=CURRENT_INDEX;
          FINISHED:=TRUE
        END
    UNTIL FINISHED "WITH SEARCH"
  END;

"######"
"STRING"
"######"

  PROCEDURE STRING_CHAR;
  BEGIN
   IF STRING_LENGTH = MAX_STRING_LENGTH THEN ERROR(STRING_ERROR)
   ELSE BEGIN
    STRING_LENGTH:=STRING_LENGTH+1;
    STRING_TEXT(.STRING_LENGTH.):= CH;
      WRITE(CH); READ(CH)
   END
  END;

  PROCEDURE STRING;
  VAR ORD_VALUE, I: INTEGER;  DONE: BOOLEAN;
  BEGIN
    STRING_LENGTH:=0;
    WRITE(CH); READ(CH); DONE:= FALSE;
    REPEAT
      WHILE NOT (CH IN STRING_SPECIAL) DO STRING_CHAR;
      CASE CH OF
        '''':
          BEGIN
            STRING_CHAR;
            IF CH = '''' THEN BEGIN WRITE(CH); READ(CH) END ELSE DONE:= TRUE
          END;
        EOL, EOM:
          BEGIN
            ERROR (STRING_ERROR);
            DONE:= TRUE
          END;
        '(':
          BEGIN
            STRING_CHAR;
            IF CH = ':' THEN BEGIN
            REPEAT WRITE(CH); READ(CH) UNTIL CH <> ' ';
              ORD_VALUE:= 0;
              IF CH IN DIGITS THEN
                REPEAT
                  IF ORD_VALUE <= MAX_ORD THEN
                    ORD_VALUE:= ORD_VALUE * 10 + (ORD(CH) - ORD('0'));
                WRITE(CH); READ(CH)
                UNTIL NOT (CH IN DIGITS)
              ELSE ERROR (STRING_ERROR);
          WHILE CH=' ' DO BEGIN WRITE(CH); READ(CH) END;
          IF CH=':' THEN BEGIN WRITE(CH); READ(CH) END ELSE ERROR(STRING_ERROR);
          IF CH=')' THEN BEGIN WRITE(CH); READ(CH) END ELSE ERROR(STRING_ERROR);
              IF ORD_VALUE > MAX_ORD THEN BEGIN
                ERROR(STRING_ERROR);
                ORD_VALUE:= ORD('?')
              END;
              STRING_TEXT(.STRING_LENGTH.):= CHR(ORD_VALUE)
            END
          END
      END
    UNTIL DONE;
    IF STRING_LENGTH <= 1 THEN BEGIN
      ERROR(STRING_ERROR);
      STRING_LENGTH:= 1;  STRING_TEXT(.1.):= '?'
    END ELSE STRING_LENGTH:= STRING_LENGTH - 1;
    IF STRING_LENGTH > 1 THEN IF STRING_LENGTH MOD WORDLENGTH <> 0 THEN
      BEGIN ERROR(STRING_ERROR); STRING_LENGTH:= 1 END;
    IF STRING_LENGTH = 1 THEN PUT1(CHAR2, ORD(STRING_TEXT(.1.)))
    ELSE PUT_STRING(STRING_TEXT, STRING_LENGTH)
  END;

"##########"
"IDENTIFIER"
"##########"

  PROCEDURE IDENTIFIER;
  BEGIN
    PIECES:=-1; CHAR_INDEX:=ID_PIECE_LENGTH;
    HASH_KEY:= 1;
    REPEAT
      IF CHAR_INDEX=ID_PIECE_LENGTH THEN BEGIN
        CHAR_INDEX:= 0;  PIECES:= SUCC(PIECES);
        ID_TEXT(.PIECES.):=BLANK;
      END ELSE CHAR_INDEX:= SUCC(CHAR_INDEX);
      ID_TEXT(.PIECES,CHAR_INDEX.):=CH;
      HASH_KEY:=HASH_KEY*(ORD(CH) MOD SPAN +1) MOD HASH_MAX1;
      WRITE(CH); READ(CH)
    UNTIL CH IN NON_ALFAS;
    SEARCH_ID;
    IF SYMB=ID2 THEN PUT1(ID2,INDEX)
    ELSE BEGIN
      PUT0(SYMB);
      IF SYMB=END2 THEN BEGIN
        GET_CHAR(FALSE);
        IF CH='.' THEN BEGIN
          PUT0(PERIOD2);
          REPEAT WRITE(CH); READ(CH) UNTIL CH = EOL;
          WRITE(CH);
          END_SCAN:=TRUE
        END
      END
    END
  END;

"#######"
"SCANNER"
"#######"

  PROCEDURE SCAN;
  BEGIN
    REPEAT
      CASE CH OF
        ' ':
          BEGIN WRITE(CH); READ(CH) END;

        EOL:
          END_LINE;

        EOM:
          END_SCAN:=TRUE;

        '"': BEGIN
          REPEAT
        REPEAT WRITE(CH); READ(CH) UNTIL (CH = '"') OR (CH = EOL);
            WHILE CH = EOL DO END_LINE
          UNTIL (CH='"') OR (CH=EOM);
        IF CH=EOM THEN ERROR(COMMENT_ERROR) ELSE BEGIN WRITE(CH); READ(CH) END
        END;

        '.': BEGIN
        WRITE(CH); READ(CH);
          IF UPTO_SW THEN BEGIN
            PUT0(UP_TO2);
            UPTO_SW:=FALSE
          END ELSE IF CH='.' THEN PUT0NC(UP_TO2)
          ELSE IF CH=')' THEN PUT0NC(BUS2)
          ELSE PUT0(PERIOD2)
        END;

        ':' : BEGIN
        WRITE(CH); READ(CH);
          IF CH='=' THEN PUT0NC(BECOMES2) ELSE PUT0(COLON2)
        END;

        '<': BEGIN
        WRITE(CH); READ(CH);
          IF CH='=' THEN PUT0NC(LE2) ELSE
          IF CH='>' THEN PUT0NC(NE2) ELSE
          PUT0(LT2)
        END;

        '=':
          PUT0NC(EQ2);

        '>': BEGIN
        WRITE(CH); READ(CH);
          IF CH='=' THEN PUT0NC(GE2) ELSE PUT0(GT2)
        END;

        '''':
          STRING;

        '0','1','2','3','4','5','6','7','8','9':
          NUMBER;

        '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','_':
          IDENTIFIER;

        '(': BEGIN
        WRITE(CH); READ(CH);
          IF CH='.' THEN PUT0NC(SUB2) ELSE PUT0(OPEN2)
        END;

        ')':
          IF BUS_SW THEN BEGIN
            PUT0NC(BUS2);
            BUS_SW:=FALSE
          END ELSE PUT0NC(CLOSE2);

        ',':
          PUT0NC(COMMA2);

        ';':
          PUT0NC(SEMICOLON2);

        '*':
          PUT0NC(STAR2);

        '/':
          PUT0NC(SLASH2);

        '+':
          PUT0NC(PLUS2);

        '-':
          PUT0NC(MINUS2);

        '&':
          PUT0NC(AND2);

        '@':
          PUT0NC(ARROW2);

      '(:0:)', '(:1:)', '(:2:)', '(:3:)', '(:4:)', '(:5:)', '(:6:)', '(:7:)',
        '(:8:)', '(:9:)', '(:11:)', '(:12:)', '(:13:)', '(:14:)', '(:15:)',
        '(:16:)', '(:17:)', '(:18:)', '(:19:)', '(:20:)', '(:21:)', '(:22:)',
        '(:23:)', '(:24:)', '(:26:)', '(:27:)', '(:28:)', '(:29:)', '(:30:)',
        '(:31:)', '(:33:)', '(:35:)', '(:36:)', '(:37:)', '(:63:)', '(:91:)',
        '(:92:)', '(:93:)', '(:94:)',           '(:96:)', '(:97:)', '(:98:)',
        '(:99:)', '(:100:)', '(:101:)', '(:102:)', '(:103:)', '(:104:)',
        '(:105:)', '(:106:)', '(:107:)', '(:108:)', '(:109:)', '(:110:)',
        '(:111:)', '(:112:)', '(:113:)', '(:114:)', '(:115:)', '(:116:)',
        '(:117:)', '(:118:)', '(:119:)', '(:120:)', '(:121:)', '(:122:)',
        '(:123:)', '(:124:)', '(:125:)', '(:126:)', '(:127:)':
          BEGIN
        WRITE('?'); READ(CH);
          ERROR(CHAR_ERROR)
          END
      END
    UNTIL END_SCAN;
    PUT0(EOM2)
  END;

"####"
"MAIN"
"####"

BEGIN
  INIT_PASS(INTER_PASS_PTR);
  INITIALIZE;
  SCAN;
  RELEASE(INTER_PASS_PTR@.RESETPOINT);
  NEXT_PASS(INTER_PASS_PTR)
END.
