{ > PasScan }

{ Sam corrected this 15 nov 85 cos Tutu code woz a load of crap : D1.04 }
{ Tutu mod in errline - look in $.library on net/adfs for errutil : D1.02 }
{ Also stop exec sequences plowing past errors - use inkey not readln }

PROCEDURE ENTBLK;
{
   This procedure is called when a new block is entered.
   The declaration level is increased by 1, a new DISPLAY entry is created for
   the new level and the entry initialised.
}
BEGIN
   X.LEVEL := SUCC(X.LEVEL);
   NEW (DISPLAY[X.LEVEL]);
   WITH DISPLAY[X.LEVEL]^ DO
   BEGIN
      TYP := BLOC;

{ The following 4 lines are the same as the 2 lines after them, because LOW and
  HIGH overlay LASTSYM, LASTPARAM, VARSIZ and FUNCREF in variant record TYPREC.
      LASTSYM := NIL;
      LASTPARAM := NIL;
      VARSIZ := 0;
      FUNCREF := NIL
}
      LOW := 0;
      HIGH := 0;
      IF X.LEVEL > MAXLEV THEN ERROR (166)
   END
END;  { entblk }


PROCEDURE ENTERID (OBJECT : SYMBOLS; IDTYPE : TYPES);
{
   This procedure is called to add a symbol table entry for a new identifier to
   the start of the linked list of ids pointed to by the entry in DISPLAY at
   the current LEVEL. The pointer into the names tables is set to the end of
   current name.
}
VAR COPY : SYMREC;

BEGIN
   copy.perm := false;
   copy.nrm := true;
{ Tutu - was
   COPY.STRLEN    := 256; { Overlays COPY.PERM := FALSE; COPY.NRM := TRUE;
}
   COPY.REF       := NIL;
   COPY.PAK       := FALSE;
   COPY.FUNCASS   := FALSE;
   COPY.THREAT    := FALSE;
   COPY.USED_LEV  := 0;
   COPY.USED_PROC := 0;
   COPY.ADR       := 0;
   COPY.OBJ       := OBJECT;
   COPY.IDTYP     := IDTYPE;
   NEW (LASTSYMP);
   THISSYMP       := LASTSYMP;
   SYM            := NEWID;
   COPY.NAMLINK   := Y.LASTNAME;
   COPY.LINK      := DISPLAY[X.LEVEL]^.LASTSYM;
   COPY.LEV       := X.LEVEL;
   DISPLAY[X.LEVEL]^.LASTSYM := LASTSYMP;
   Y.LASTNAME     := NAMES.TADR;
   LASTSYMP^      := COPY
END; { enterid }


PROCEDURE LOOKUPID;
{
   This procedure searches the symbol table for an identifier with the same
   name as the current one. The first place to search is a list of activated
   field identifiers (activated by a with statement or by parsing R.FIELD.
   If no match is found then THISSYMP has the pointer value NIL.
   CODE1 (EQID,0,LLIM) is the machine code routine to search a given id list for   a match.
   If no match is found in the activated field list then the main symbol table
   is searched starting at the current level and working backwards. The search
   will continue until a match is found, X.DEPTH becomes negative (see global
   var declarations for warning on X.DEPTH) or the compiler is parsing a
   declaration when LOOKUPID was called (in which case only the current level
   is searched).
}
BEGIN
  { Look through any possible field ids before checking other ids }
   THISSYMP := NIL;
   THISACTIVE := ACTIVELIST;
   WHILE (THISACTIVE <> NIL) AND (THISSYMP = NIL) DO
   BEGIN
      IF THISACTIVE^.FIELDS <> NIL THEN
      BEGIN
         THISSYMP := THISACTIVE^.FIELDS^.LASTSYM;
         LLIM := CODE1 (EQID,0,LLIM)
      END;

      IF THISSYMP = NIL THEN
         THISACTIVE := THISACTIVE^.NEXT
   END;

   IF THISSYMP = NIL THEN
   REPEAT
      NEXTSYMP := NIL;
      THISSYMP := DISPLAY[X.DEPTH]^.LASTSYM;

     { look through each entry at level 'DEPTH' }
      LLIM := CODE1 (EQID,0,LLIM);

      X.DEPTH := PRED(X.DEPTH)
     { stop when match is found, or when last level searched and
      ISDEC is true, or if all the table has been searched }
   UNTIL (X.DEPTH > 100) OR (THISSYMP <> NIL) OR ISDEC
END; { lookupid }


PROCEDURE ERRLINE;
{
   This procedure is called at the end of a line. If one or more errors have
   occurred on the line then a '^' is output on the next line at the character
   position(s) of the error(s).
   For each error that occurred, an error message is printed out.
   If FULL is true then a textual error message is printed by skipping N lines
   in the error message file 'ERRUTIL' (where N is the error number) and then
   printing the text on the next line.
   If FULL is false then just a number is printed.
   When all the errors have been reported and WAIT is true then input is waited
   for from the keyboard (ie wait for <RETURN> after an error - Tutu INKEY !).
}
VAR I,J : UB;
    CHUR : CHAR;

BEGIN
   IF X.ERRNO > 0 THEN
   BEGIN
     { Output the pointers '^' to each error }
      IF LISTING THEN
      BEGIN
         FOR I := -8 TO X.CHRNO DO
            WRITE (CHR(32+62*(ORD(I IN ERRSET))));
{
           The line above is the same as the next line (shorter code).
            IF I IN ERRSET THEN WRITE('^') ELSE WRITE(' ');
}
         WRITELN
      END;

      FOR I := 1 TO X.ERRNO DO
      BEGIN
         WRITELN ('** Error ',ERRARAY[I]:1,' at line ',X.SRCLINE:1,' in ',
                   SOURCEFILE:X.NAMESZ);
         IF FULL THEN
         BEGIN
            if code0 (&FFDA, 0, 0, 0) mod &100 >= 5 {OsArgs}
            then reset (ERRFILE,'$.Library.Errutil')
            else reset (ERRFILE,'Errutil');
       { Sam - right version of if adfs or net get lib
             - look at A instead of X like Tutu did ! }

            FOR J := 1 TO ERRARAY[I] DO READLN (ERRFILE);

            REPEAT
               READ (ERRFILE,CHUR);
               IF CHUR = '_' THEN WRITELN ELSE WRITE (CHUR)
            UNTIL EOLN (ERRFILE)
         END
      END;

      X.ERRNO := 0;
      ERRSET := [];
      IF WAIT THEN
        repeat until (inkey (-74 {CR}) = -1)   { Tutu - used to be READLN }
   END
END; { errline }


PROCEDURE NEXTSYM;
{
   This procedure is called to get the next symbol from the source file in SYM.
   When the procedure is entered, characters are read from the source file
   until all spaces and comments including compiler options have been read,
   leaving the first charcter of the symbol in CH.
   The rest of the characters which make up the symbol are read in and the
   symbol identified and symbol type put in SYM.
}
LABEL 13,131,1313;

VAR I : UB;
    HEX,              { TRUE if hex number found, otherwise FALSE}
    CONV_CH_TO_UPPER, { TRUE if lower case is to be converted to upper case,
                        not TRUE in literals }
    MORE : BOOLEAN;   { used as a loop control when getting a literal }
    NUM : FNAMETYP;   { Local string for number conversion. }
    SAVESYM : SYMBOLS;
    STATUS : CHAR;    { 'C' if in a comment, or '-' if not }


PROCEDURE NEXTCH;
{
   This procedure gets a character from source file and creates the listing.
}
LABEL 13;

BEGIN
   CH := NXTCH;
   IF (CH >= 'a') THEN
      IF CONV_CH_TO_UPPER AND (CH <= 'z') THEN CH := CHR (ORD(CH) - 32);

   IF NEWLINE THEN
   BEGIN
      IF LISTING THEN WRITELN;
     { Zap out any error messages }
      ERRLINE;
      X.LINENO := SUCC(X.LINENO);
      X.SRCLINE := SUCC(X.SRCLINE);
      IF LISTING THEN
         WRITE (X.LINENO:4,PRED(X.LEVEL)+ORD(X.LEVEL<=0):2,STATUS:2);
      X.CHRNO := 0;
      NEWLINE := FALSE
   END
   ELSE X.CHRNO := SUCC(X.CHRNO);
   IF LISTING THEN WRITE (NXTCH);

   IF CH <> CHR(EOFCH) THEN
   BEGIN
      IF EOLN (SOURCE) THEN
      BEGIN
         NEWLINE := TRUE;
         IF (X.LINENO = 1) AND CLO THEN
         BEGIN
            OPEN;
            GOTO 13
         END
      END;
      READ (SOURCE,NXTCH);
  13: IF EOF(SOURCE) THEN
      BEGIN
         NXTCH := CHR(EOFCH);
         NEWLINE := FALSE
      END
   END
END;  { nextch }


PROCEDURE EATCH;
{
   This procedure puts the character in CH into the names table at the next
   free position in the table and follows it with a carriage return. If the
   chunk of memory allocated to the names table has not been claimed, then the
   chunk is claimed first. A check on names table overflow is also made. Before
   exiting, the next char from the source file is read in by calling NEXTCH.
}
BEGIN
   IF NOTAB THEN
   BEGIN
      NOTAB := FALSE;
      NAMES.TADR := CLAIM (X.MAXNAME);
      X.GARECADR := PRED(NAMES.TADR);
      Y.LASTNAME := X.GARECADR;
      X.MAXNAME := X.MAXNAME + NAMES.TADR
   END;
   X.WORDLEN := SUCC(X.WORDLEN);
   NAMES.TPTR^ := CH;
   NAMES.TADR := SUCC(NAMES.TADR);
   IF NAMES.TADR >= X.MAXNAME THEN ERROR(165);
   NAMES.TPTR^ := ' ';
   NEXTCH
END;  { eatch }


PROCEDURE CHKEOF;
{
   This procedure checks that the character held in CH is not an EOF character.
   If it is, then an error is generated and a GOTO 1313 is executed (go to end
   of NEXTSYM procedure.
   Called for check on EOF in a string or comment.
}
BEGIN
   IF CH = CHR(EOFCH) THEN
   BEGIN
      ERROR (130); { Unexpected EOF in string constant or comment }
      SYM := EOFILE;
      GOTO 1313
   END
END; { chkeof }


FUNCTION VAL : UW;
{
   This function reads in a stream of decimal digits from the source file and
   converts them to an integer, which is returned in VAL.
}
VAR NUMBER : FNAMETYP;

BEGIN
   X.INDEX := 1;
   WHILE (NXTCH >= '0') AND (NXTCH <= '9') DO
   BEGIN
      NUMBER[X.INDEX] := NXTCH;
      X.INDEX := SUCC(X.INDEX);
      NEXTCH
   END;
   NUMBER[X.INDEX] := CHR (TERMCH);
   VAL := IVAL (NUMBER)
END; { val }


PROCEDURE DIGITS;
{
   This procedure reads a stream of digits (including hexadecimal ones) from
   the source file into the names table.
}
VAR OLDLEN : UW;

BEGIN
   X.INDEX := X.WORDLEN;
   WHILE (HEX OR (CH <= '9')) AND
   (CH IN ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F']) DO
         EATCH;
   IF X.INDEX = X.WORDLEN THEN
   BEGIN
      FUCKUP := TRUE;
      ERROR (127) {Digit expected}
   END
END;  { digits }


BEGIN { nextsym }
   CONV_CH_TO_UPPER := TRUE;
   STATUS := '-';

   IF NOT FUCKUP THEN
   BEGIN
     { Ignore comments and spaces and get compiler options }
      WHILE (CH=' ') OR ((CH='(') AND (NXTCH='*')) OR (CH='{') DO
      BEGIN
         IF CH <> ' ' THEN
         BEGIN
            STATUS := 'C';
            IF CH = '(' THEN { Get rid of '(' and get '*' }
               NEXTCH;

            IF NXTCH = '$' THEN
              { Possible compiler option present }
               REPEAT
                  NEXTCH; NEXTCH;
                  OPTION := (NXTCH = '+') OR (NXTCH = '*');

                  IF (NXTCH = '-') OR OPTION THEN
                  BEGIN  { On/Off compiler options }
                     CASE CH OF
                       'G' : IF CHANGE THEN GENCODE := OPTION ELSE ERROR (145);
                       'D' : IF CHANGE THEN DEBUG := OPTION ELSE ERROR (145);
                       'F' : FULL := OPTION;
                       'L' : LISTING := OPTION;
                       'R' : RNGCHK := OPTION;
                       'T' : TAIL := OPTION;
                       'U' : BEGIN
                                ASSCHK := OPTION;
                                FLUSHNGEN (80{ASSCHK});
                                IF NXTCH = '*' THEN GENUB (255)
                                ELSE GENUB (ORD(OPTION))
                             END;
                       'W' : WAIT := OPTION;
                       'X' : EXT := OPTION
                     END {CASE}
                     OTHERWISE;
                     NEXTCH
                  END
                  ELSE
                     IF CH = 'S' THEN
                     BEGIN { Switch compilation to new input file }
                        IF NXTCH = '''' THEN
                        BEGIN
                           NEXTCH;
                           IF NXTCH = '''' THEN ERROR (93)
                           ELSE
                           BEGIN
                              CLRSTR (SOURCEFILE);
                              X.NAMESZ := 0;
                              REPEAT
                                 X.NAMESZ := SUCC(X.NAMESZ);
                                 IF X.NAMESZ > MAXFNAME THEN
                                 BEGIN
                                    ERROR (93);
                                    GOTO 13
                                 END;
                                 SOURCEFILE[X.NAMESZ] := NXTCH;
                                 NEXTCH;
                                 CHKEOF
                              UNTIL (NXTCH = '''');
                           13:NEXTCH;NEXTCH;
                              OPEN;
                              GOTO 131
                           END
                        END
                     END
                     ELSE
                        IF (CH = 'I') AND NOTAB THEN X.MAXNAME := VAL
                        else if (ch = 'M') then stack_size := val { Tutu }
               UNTIL (NXTCH <> ',')
            ELSE
               NEXTCH;

            WHILE ((CH <> '*') OR (NXTCH <> ')')) AND (CH <> '}') DO
            BEGIN
               CHKEOF;
               NEXTCH  { Skip all the comment up to comment delimiter }
            END;

            IF (CH <> '}') THEN NEXTCH { Put character after comment in NXTCH }
         END;

     131:STATUS := '-';
         NEXTCH  { Put next character into CH }
      END;  { End of dealing with options, comments and spaces }

     { Get next token from input stream}
      NAMES.TADR := SUCC(Y.LASTNAME);
      X.WORDLEN := 0;

{ Identifiers }

      IF CH IN ['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'] THEN
      { Reads in a valid identifier or reserved word. first the reserved word
        list is looked through for a match. If it is not found then the names
        table is searched. If isdec is true, then only identifiers at the
        current level are searched. If the id name is not present then the id
        is inserted into the symbol table as a new undefined identifier.
        if the name is a reserved word then the symbol representing the
        reserved word is returned in 'SYM' otherwise the OBJ field of the
        symbol table entry is returned in 'SYM'.
      }
      BEGIN
         { Read in all alpha-numeric characters }
         WHILE (CH IN ['0','1','2','3','4','5','6','7','8','9','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','_']) DO
         BEGIN
           { Convert lower case chars to upper before inserting in names table}
            IF CH = '_' THEN ISOERR;
            EATCH
         END;

        { Suss out if symbol is a reserved word }
         I := CODE1 (RESWRD,0,I);

         IF SYM > WITHSY THEN
         BEGIN  { Name is not a reserved word so look it up in symbol table }
            X.DEPTH := X.LEVEL;
            X.USED_AT_LEV := 0;
            LOOKUPID;

            IF THISSYMP = NIL THEN
            BEGIN { The name is a new identifier }

              { Search previous levels for same id }
               X.DEPTH := X.LEVEL;
               MORE := ISDEC;
               ISDEC := FALSE;
               LOOKUPID;
               ISDEC := MORE;

              { If found then set X.USED_AT_LEV to THISSYMP^.USED_LEV }
               IF THISSYMP <> NIL THEN
               BEGIN
                  X.USED_AT_LEV  := THISSYMP^.USED_LEV;
                  X.USED_IN_PROC := THISSYMP^.USED_PROC
               END;

              { Insert identifier into the symbol table }
               ENTERID (UNDEFID,NOTYP)
            END
            ELSE
            BEGIN { Name has been found in the symbol table }
               SYM := THISSYMP^.OBJ;
               IF NOT PHEAD THEN
               BEGIN
                  THISSYMP^.USED_LEV  := X.LEVEL;
                  THISSYMP^.USED_PROC := X.THISPROCNO
               END;
              { The next statement is here to stop a reference of a string
                constant identifier being put in the names table by the
                constants declaration parser. }
               NAMES.TADR := PRED(Y.LASTNAME)
            END
         END
      END { of parsing an identifier }
      ELSE

{ Constants }

         IF CH IN ['0','1','2','3','4','5','6','7','8','9','&',''''] THEN
         BEGIN
            SYM := KONST;
            THISSYMP := BASESYMP;
            THISSYMP^.IDTYP := SUBR;

{ Literals }

            IF CH = '''' THEN
            BEGIN
               CONV_CH_TO_UPPER := FALSE;
               MORE := TRUE;
               NEXTCH;
               WHILE MORE DO { Read in string }
               BEGIN
                  IF CH = '''' THEN
                  BEGIN
                     IF NXTCH <> '''' THEN
                     BEGIN
                        MORE := FALSE;
                        CONV_CH_TO_UPPER := TRUE
                     END;
                     NEXTCH
                  END;
                  IF MORE THEN
                  BEGIN
                     IF (X.CHRNO < 2) AND (X.ERRNO = 0) THEN ERROR (157);
                     CHKEOF;
                     THISSYMP^.VAL := ORD(CH);
                     EATCH
                  END
               END;

               THISSYMP^.REF := CHARDESC;
            { Determine if type is char or string }
               IF X.WORDLEN <> 1 THEN
               BEGIN
                  THISSYMP^.IDTYP := STRNG;
                  THISSYMP^.STRPTR := Y.LASTNAME;
                  THISSYMP^.PAK := TRUE;
                  THISSYMP^.STRLEN := X.WORDLEN;
                  IF X.WORDLEN = 0 THEN ISOERR
               END
            END  { of forming a literal }

{ Numbers }

            ELSE { Reads an integer number in hex or decimal or a decimal real
                  number. The value is put into THISSYMP^.ADR or if real then
                  it is put into THISSYMP^.REELVAL }
            BEGIN
               HEX := FALSE;
               IF CH = '&' THEN
               BEGIN
                  ISOERR;
                  HEX := TRUE;
                  EATCH
               END;

               THISSYMP^.REF := INTDESC;

              { Strip off leading zeros leaving one zero if any found }
               WHILE (CH = '0') AND (NXTCH = '0') DO NEXTCH;

              { Get integer or whole part of real }
               DIGITS;

               IF (CH = '.') AND (NXTCH <> '.') AND (NXTCH <> ')') THEN
               BEGIN  { Decimal point detected, get fractional part }
                  EATCH;
                  DIGITS;
                  THISSYMP^.IDTYP := REEL
               END;

               IF CH = 'E' THEN
               BEGIN { Get exponent part }
                  THISSYMP^.IDTYP := REEL;
                  EATCH;
                  IF (CH = '-') OR (CH = '+') THEN EATCH;
                  DIGITS
               END;

               IF CH IN ['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'] THEN
                  ERROR (152);

              { Convert string to a value }
               X.INDEX := 0;
               IF HEX THEN
               BEGIN
                  IF THISSYMP^.IDTYP = REEL THEN ERROR (105);
                  IF ODD (CODE1 (HEXCON,0,HEX)) THEN ERROR (25);
                  THISSYMP^.VAL := I
               END

               ELSE
                  IF NOT FUCKUP THEN
                  BEGIN
                     IF X.WORDLEN >= 60 THEN ERROR (57);
                     REPEAT
                        X.INDEX := SUCC(X.INDEX);
                        NAMES.TADR := X.INDEX + Y.LASTNAME;
                        NUM[X.INDEX] := NAMES.TPTR^
                     UNTIL NAMES.TPTR^ = ' ';
                     NUM[X.INDEX] := CHR(TERMCH);

                     IF THISSYMP^.IDTYP = SUBR THEN THISSYMP^.VAL := IVAL (NUM)
                     ELSE { Number is a real }
                     BEGIN
                        THISSYMP^.REELVAL := RVAL (NUM);
                        THISSYMP^.REF := NIL
                     END
                  END;
               FUCKUP := FALSE
            END { of forming a number }
         END

{ Punctuation }

         ELSE { Symbol is a punctuation symbol }
         BEGIN { Get punctuation symbol }
            IF CH = CHR(EOFCH) THEN SYM := EOFILE
            ELSE
            BEGIN
               SYM := TWIDLE;
               WHILE (CH <> SINGLEOPS[ORD(SYM)])
                 AND (SYM < DDOT) DO SYM := SUCC(SYM);
               SYM := PRED(SYM);
               IF SYM = NULL THEN SYM := AT
            END;
            NEXTCH;

            IF SYM = ASSIGN THEN
            BEGIN
               ERROR (129);
               NEXTSYM
            END

            ELSE
            BEGIN
               SAVESYM := SYM;
               CASE CH OF
                  '.' : BEGIN
                           IF SYM = DOT THEN SYM := DDOT;
                           IF SYM = LPAREN THEN SYM := LBRAK
                        END;
                  '>' : IF SYM = LT THEN SYM := NE;
                  '=' : BEGIN
                           IF SYM >= LT THEN SYM := PRED(PRED(SYM));
                           IF SYM = COLON THEN SYM := ASSIGN
                        END;
                  ')' : IF SYM = DOT THEN SYM := RBRAK
               END
               OTHERWISE;
               IF SYM <> SAVESYM THEN NEXTCH
            END

         END  { of forming a punctuation symbol }
   END;

   ISSEMI := SYM = SEMICOLON;
   ISCOMA := SYM = COMMA;
   IF SYM IN [DDOT,EQUALS,NE,LT,GT,LE,GE,INSY,PLUS,MINUS,ORSY,MULT,SLASH,DIVSY,
              MODSY,ANDSY,LPAREN,RPAREN] THEN RETKONST := FALSE;
1313:;
END;  { nextsym }


PROCEDURE GETSIGN;
{
   This procedure sets SIGNED to true if the current symbol is a '+' or a '-'.
   NEGSIGN is set to true if the current symbol is a '-'. If a sign is detected
   then the next symbol is read in.
}
BEGIN
   NEGSIGN := (SYM = MINUS);
   SIGNED := (SYM = PLUS) OR (NEGSIGN);
   IF SIGNED THEN
      NEXTSYM
END; { getsign }


PROCEDURE CHK (EXPECTED : SYMBOLS);
{
   This procedure checks that the current symbol is the expected one. If it is
   not then FUCKUP is set to true, an error generated (error No=ord(expected)),
   and symbols are read in until the current symbol is in the ACCEPT set or is
   EOF or is the EXPECTED symbol. FUCKUP is true on exit if the current symbol
   is not the expected one.
   See CHKNEXT for more information on error recovery.
}
BEGIN
   FUCKUP := SYM <> EXPECTED;
   IF FUCKUP THEN
   BEGIN
      ERROR (ORD(EXPECTED));
      WHILE NOT ((SYM IN ACCEPT) OR (SYM = EOFILE) OR (SYM = EXPECTED)) DO
      BEGIN
         FUCKUP := FALSE;
         NEXTSYM;
         FUCKUP := SYM <> EXPECTED
      END
   END
END;  { chk }


PROCEDURE CHKNEXT (EXPECTED : SYMBOLS);
{
   This procedure calls CHK (see above) and then calls NEXTSYM to get the next
   symbol in. Note.. If FUCKUP is true when NEXTSYM is called, no action is
   taken by NEXTSYM.
   Therefore, no new symbol is read in until FUCKUP is set to false by CHK
   (i.e. the current symbol is the expected one) this makes the compiler
   realign on a known symbol.
}
BEGIN
   CHK (EXPECTED);
   NEXTSYM
END;  { chknext }


PROCEDURE NXTSYMOK;
{
   This procedure causes error recovery to stop by calling CHKNEXT with the
   expected symbol being the current one.
}
BEGIN
   CHKNEXT (SYM)
END; { nxtsymok }


PROCEDURE SKIPSPACES;
{
   This procedure skips spaces on the command line. Used when reading filenames
   from the command line.
}
BEGIN
   WHILE (SOURCE^ = ' ') AND (NOT EOLN(SOURCE)) DO GET(SOURCE)
END; { skipspaces }


{$S'PasExpr'}
