{ > 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 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'}