(*====================================================================*) (* PROGRAM TITLE: PASCAL CROSS-REFERENCING PROGRAM *) (* *) (* PROGRAM NAME: XREF *) (* *) (* LAST UPDATE: 14-JUL-81 by Warren A. Smith *) (* *) (* NOTE: THIS PROGRAM WAS ORIGINALLY WRITTEN BY N. WIRTH AND *) (* ADAPTED FOR UCSD PASCAL (I.4 - THE PUBLIC DOMAIN VERSION) *) (* BY SHAWN FANNING (IN 1978) AND SUBSEQUENTLY ADAPTED FOR *) (* PASCAL/MT+ BY MIKE LEHMAN (IN 1981). THIS VERSION WAS THEN *) (* MODIFIED BE WARREN A. SMITH TO TRY TO GET BACK TO ISO STAN- *) (* DARD PASCAL AND TO ADD THE ADDITIONAL FEATURE OF MAPPING *) (* OUT THE COMPOUND STATEMENTS. THIS IS A PUBLIC DOMAIN PROGRAM. *) (* IF YOU MAKE REVISIONS, ETC. PLEASE LEAVE THE AUTHOR *) (* AND ALL MODIFIERS NAMES IN THE SOURCE FILE. THANK YOU. *) (* *) (* PROGRAM SUMMARY: *) (* *) (* THIS PROGRAM PRODUCES A CROSS-REFERENCE LISTING FOR ANY *) (* PASCAL PROGRAM. OCCURENCES ONLY ARE LISTED. NO DISTINCTION IS *) (* MADE BETWEEN DEFINITIONS AND REFERENCES. IT WILL ALSO GIVE A *) (* GRAPHICAL REPRESENTATION OF THE BLOCK STRUCTURE OF THE PROGRAM. *) (* THIS FEATURE WAS ADDED BY WARREN A. SMITH (IN JULY 1981) *) (*====================================================================*) PROGRAM XREF; (*CROSS REFERENCE GENERATOR FOR PASCAL PROGRAMS. N.WIRTH, 7.5.74*) (*'QUADRATIC QUOTIENT' HASH METHOD*) CONST P = 749; (*SIZE OF HASHTABLE*) NK = 45; (*NO. OF KEYWORDS*) PAGESIZE = 60; (*LINES PER PAGE*) ALFALEN = 8; (*SIZE OF IDENTIFIERS*) REFSPERLINE = 15; REFSPERITEM = 5; NESTMAX = 10 ; TYPE ALFA = PACKED ARRAY[1..ALFALEN] OF CHAR; INDEX = 0..P; ITEMPTR = ^ITEM; WORD = RECORD KEY: ALFA; FIRST, LAST: ITEMPTR; FOL: INDEX END ; NUMREFS = 1..REFSPERITEM; REFTYPE = (COUNT, PTR); ITEM = RECORD REF : ARRAY[NUMREFS] OF INTEGER; CASE REFTYPE OF COUNT: (REFNUM: NUMREFS); PTR: (NEXT: ITEMPTR) END ; BUFFER = PACKED ARRAY[0..131] OF CHAR; VAR TOP: INDEX; (*TOP OF CHAIN LINKING ALL ENTRIES IN T*) I,LINECOUNT,BUFCURSOR: INTEGER; (*CURRENT LINE NUMBER*) FF,CH: CHAR; (*CURRENT CHAR SCANNED *) BUF : BUFFER; T: ARRAY [INDEX] OF WORD; (*HASH TABLE*) KEY: ARRAY [1..NK] OF ALFA; (* RESERVED KEYWORD TABLE *) ERROR, (* ERROR FLAG *) LISTING: BOOLEAN; (* LISTING OPTION *) INFILE: TEXT; LST : TEXT; LSTFILENAME : STRING; INPUT_LINE : STRING; PAGE_NUM, NESTLVL, LAST_KEY : INTEGER ; ABORT, LITERAL, ACOMMENT, BCOMMENT, EOL, NESTUP, NESTDN : BOOLEAN ; BAR : CHAR ; FUNCTION UPPER (CH : CHAR) : CHAR ; BEGIN (* UPPER *) IF (CH >= 'a') AND (CH <= 'z') THEN UPPER := CHR(ORD(CH) + (ORD('A') - ORD('a'))) ELSE UPPER := CH END ; (* UPPER *) PROCEDURE INITIALIZE; VAR I : INTEGER; PROCEDURE FIRSTHALF; BEGIN KEY[ 1] := 'AND '; KEY[ 2] := 'ARRAY '; KEY[ 3] := 'BEGIN '; KEY[ 4] := 'BOOLEAN '; KEY[ 5] := 'CASE '; KEY[ 6] := 'CHAR '; KEY[ 7] := 'CONST '; KEY[ 8] := 'DIV '; KEY[ 9] := 'DOWNTO '; KEY[10] := 'DO '; KEY[11] := 'ELSE '; KEY[12] := 'END '; KEY[13] := 'EXIT '; KEY[14] := 'FILE '; KEY[15] := 'FOR '; KEY[16] := 'FUNCTION'; END; PROCEDURE SECONDHALF; BEGIN KEY[17] := 'GOTO '; KEY[18] := 'IF '; KEY[19] := 'IN '; KEY[20] := 'INPUT '; KEY[21] := 'INTEGER '; KEY[22] := 'MOD '; KEY[23] := 'NIL '; KEY[24] := 'NOT '; KEY[25] := 'OF '; KEY[26] := 'OR '; KEY[27] := 'OUTPUT '; KEY[28] := 'PACKED '; KEY[29] := 'PROCEDUR'; KEY[30] := 'PROGRAM '; KEY[31] := 'REAL '; KEY[32] := 'RECORD '; KEY[33] := 'REPEAT '; KEY[34] := 'SET '; KEY[35] := 'STRING '; KEY[36] := 'TEXT '; KEY[37] := 'THEN '; KEY[38] := 'TO '; KEY[39] := 'TYPE '; KEY[40] := 'UNTIL '; KEY[41] := 'VAR '; KEY[42] := 'WHILE '; KEY[43] := 'WITH '; KEY[44] := 'WRITE '; KEY[45] := 'WRITELN '; END; BEGIN (* INITIALIZE *) FOR I := 1 TO 25 DO { clear the screen } WRITELN ; WRITELN('Pascal Program Xref Utility'); WRITELN('This program is public domain'); WRITELN('Contributed by Warren A. Smith -- July 14, 1981'); FOR I := 1 TO 13 DO WRITELN ; FF:=CHR(12); ERROR := FALSE; FOR I := 0 TO P DO T[I].KEY := ' '; FIRSTHALF; SECONDHALF; LINECOUNT:= 1; TOP := P; PAGE_NUM := 1 ; LITERAL := FALSE ; ACOMMENT := FALSE ; BCOMMENT := FALSE ; NESTLVL := 0 ; LAST_KEY := 0 ; BAR := '|' ; CH := ' ' END; (* INITIALIZE *) PROCEDURE OPENFILES; VAR I : INTEGER ; NUMBLOCKS: INTEGER; OPENOK: BOOLEAN; OPENERRNUM : INTEGER; LISTOPTION: CHAR; FILENAME: STRING; BEGIN (* OPEN *) WRITELN ; WRITELN ('An answer of a $ character to any question') ; WRITELN (' will cause the program to abort.') ; ABORT := FALSE ; REPEAT WRITELN; WRITELN('Type in the name of the file you want cross-referenced.' ); WRITELN(' The file will also have the compound statements displayed'); WRITELN(' if you select the list option. '); READLN( FILENAME ); IF LENGTH(FILENAME) > 0 THEN BEGIN FOR I := 1 TO LENGTH(FILENAME) DO FILENAME[I] := UPPER(FILENAME[I]) ; ABORT := FILENAME[1] = '$' ; IF NOT ABORT THEN BEGIN {---------------------------------------------------------------} { This section is implementation dependent. It will work } { for UCSD Pascal or Pascal/MT+ but not for Pascal/Z. } { For Pascal/Z, use } { RESET (FILENAME,INFILE); } {---------------------------------------------------------------} {} ASSIGN(INFILE,FILENAME); {} {} RESET(INFILE); {} {---------------------------------------------------------------} OPENERRNUM := IORESULT; OPENOK := ( OPENERRNUM <> 255 ); ABORT := EOF (INFILE) ; IF NOT OPENOK THEN WRITELN( '*** INPUT OPEN ERROR #', OPENERRNUM ) ELSE IF ABORT THEN WRITELN ('*** FILE ', FILENAME,' IS EMPTY, PROGRAM ABORTING') END END; UNTIL OPENOK OR ABORT; IF NOT ABORT THEN BEGIN WRITELN; WRITELN('Destination file or device name?'); WRITE (' The default is LST: - '); READLN(LSTFILENAME); WRITELN; IF LENGTH (LSTFILENAME) <= 0 THEN LSTFILENAME := 'LST:' ; ABORT := LSTFILENAME [1] = '$' ; IF NOT ABORT THEN BEGIN FOR I := 1 TO LENGTH(LSTFILENAME) DO LSTFILENAME[I] := UPPER(LSTFILENAME[I]) ; {---------------------------------------------------------------} { This section is implementation dependent. It will work } { for UCSD Pascal or Pascal/MT+ but not for Pascal/Z. } { For Pascal/Z, use } { REWRITE (LSTFILENAME, LST); } {---------------------------------------------------------------} {} ASSIGN(LST,LSTFILENAME); {} {} REWRITE(LST) {} {---------------------------------------------------------------} END END ; IF NOT ABORT THEN BEGIN REPEAT WRITE( 'Do you want a listing (y or n)? ' ); READ( LISTOPTION ); WRITELN ; ABORT := LISTOPTION = '$' UNTIL ABORT OR (LISTOPTION IN ['Y','y','N','n']); IF NOT ABORT THEN BEGIN LISTING := NOT(LISTOPTION in ['N','n']) ; WRITELN ; IF LISTING THEN WRITELN ('LIST OPTION ON') ELSE WRITELN END END END; (* OPEN *) FUNCTION TAB (NUM : INTEGER) : CHAR ; VAR I : INTEGER ; BEGIN FOR I := 1 TO NUM DO WRITE (LST, ' ') ; TAB := CHR(0) END ; (* TAB *) PROCEDURE LPWRITELN; VAR I : INTEGER; BEGIN BUF[BUFCURSOR]:=CHR(13); BUFCURSOR:=BUFCURSOR+1; FOR I := 0 TO BUFCURSOR-1 DO WRITE(LST,BUF[I]); BUFCURSOR:=0; LINECOUNT:=LINECOUNT+1; IF (LINECOUNT MOD PAGESIZE) = 0 THEN PAGE(LST); END; PROCEDURE PUTALFA(S:ALFA); BEGIN MOVELEFT(S[1],BUF[BUFCURSOR],8); BUFCURSOR:=BUFCURSOR+8; END; PROCEDURE PUTNUMBER(NUM: INTEGER); VAR I,IPOT:INTEGER; A: ALFA; CH: CHAR; ZAP:BOOLEAN; BEGIN ZAP:=TRUE; IPOT:=10000; A[1]:=' '; FOR I:= 2 TO 6 DO BEGIN CH:=CHR(NUM DIV IPOT + ORD('0')); IF I <> 6 THEN IF ZAP THEN IF CH = '0' THEN CH:=' ' ELSE ZAP:=FALSE; A[I]:=CH; NUM:=NUM MOD IPOT; IPOT:=IPOT DIV 10; END; A[7]:=' '; MOVELEFT(A,BUF[BUFCURSOR],7); BUFCURSOR:=BUFCURSOR+7; END; PROCEDURE SEARCH( ID: ALFA ); (*MODULO P HASH SEARCH*) (*GLOBAL: T, TOP*) VAR I,J,H,D : INTEGER; X : ITEMPTR; F : BOOLEAN; BEGIN J:=0; FOR I:= 1 TO ALFALEN DO J:= J*10+ORD(ID[I]); H := ABS(J) MOD P; F := FALSE; D := 1; REPEAT IF T[H].KEY = ID THEN BEGIN (*FOUND*) F := TRUE; IF T[H].LAST^.REFNUM = REFSPERITEM THEN BEGIN NEW(X); X^.REFNUM := 1; X^.REF[1] := LINECOUNT; T[H].LAST^.NEXT:= X; T[H].LAST := X; END ELSE WITH T[H].LAST^ DO BEGIN REFNUM := REFNUM + 1; REF[REFNUM] := LINECOUNT END END ELSE IF T[H].KEY = ' ' THEN BEGIN (*NEW ENTRY*) F := TRUE; NEW(X); X^.REFNUM := 1; X^.REF[1] := LINECOUNT; T[H].KEY := ID; T[H].FIRST := X; T[H].LAST := X; T[H].FOL := TOP; TOP := H END ELSE BEGIN (*COLLISION*) H := H+D; D := D+2; IF H >= P THEN H := H - P; IF D = P THEN BEGIN WRITELN(OUTPUT,'TBLE OVFLW'); ERROR := TRUE END ; END UNTIL F OR ERROR END (*SEARCH*) ; PROCEDURE PRINTWORD(W: WORD); VAR L: INTEGER; X: ITEMPTR; NEXTREF : INTEGER; THISREF: NUMREFS; BEGIN PUTALFA(W.KEY); X := W.FIRST; L := 0; REPEAT IF L = REFSPERLINE THEN BEGIN L := 0; LPWRITELN; PUTALFA(' '); END ; L := L+1; THISREF := (L-1) MOD REFSPERITEM + 1; NEXTREF := X^.REF[ THISREF ]; IF THISREF = X^.REFNUM THEN X := NIL ELSE IF THISREF = REFSPERITEM THEN X := X^.NEXT; PUTNUMBER(NEXTREF); UNTIL X = NIL; LPWRITELN; END (*PRINTWORD*) ; PROCEDURE PRINTTABLE; VAR I,J,M: INDEX; BEGIN I := TOP; WHILE I <> P DO BEGIN (*FIND MINIMAL WORD*) M := I; J := T[I].FOL; WHILE J <> P DO BEGIN IF T[J].KEY < T[M].KEY THEN M := J; J := T[J].FOL END ; PRINTWORD(T[M]); IF M <> I THEN BEGIN T[M].KEY:=T[I].KEY; T[M].FIRST:=T[I].FIRST; T[M].LAST:=T[I].LAST; END; I := T[I].FOL END END (*PRINTTABLE*) ; PROCEDURE OUTPUT_LINE (BUF : BUFFER) ; VAR I : INTEGER ; PROCEDURE FILL_LINE (VAR LINE : BUFFER) ; VAR I : INTEGER ; BEGIN (* FILL_LINE *) I := 1 ; WHILE (LINE[I] = ' ') DO BEGIN LINE[I] := '-' ; I := I + 1 END END ; (* FILL_LINE *) PROCEDURE PRTNEST (VAR LINE : BUFFER) ; VAR COL : INTEGER ; BEGIN (* PRTNEST *) FOR COL := 1 TO NESTLVL - 1 DO WRITE (LST, BAR, ' ') ; IF NESTLVL > 0 THEN IF NESTUP OR NESTDN THEN BEGIN IF NESTDN THEN BEGIN WRITE (LST, BAR, ' ') ; WRITE (LST, 'E--') ; FOR COL := NESTLVL+2 TO NESTMAX DO WRITE (LST, '---') END ELSE BEGIN WRITE (LST, 'B--') ; FOR COL := NESTLVL+1 TO NESTMAX DO WRITE (LST, '---') END ; FILL_LINE (LINE) END ELSE BEGIN WRITE (LST, BAR, ' ') ; FOR COL := NESTLVL+1 TO NESTMAX DO WRITE (LST, ' ') END ELSE IF NESTDN THEN BEGIN WRITE (LST, 'E--') ; FOR COL := 2 TO NESTMAX DO WRITE (LST, '---') ; FILL_LINE (LINE) END ELSE FOR COL := 1 TO NESTMAX DO WRITE (LST, ' ') END ; (* PRTNEST *) BEGIN (* OUTPUT_LINE *) IF ((LINECOUNT MOD PAGESIZE) = 0) OR (PAGE_NUM = 1) THEN BEGIN IF LISTING THEN BEGIN PAGE (LST) ; WRITELN (LST, TAB(70), 'PAGE ', PAGE_NUM:1) ; WRITELN (LST) ; PAGE_NUM := PAGE_NUM + 1 END ; IF (LSTFILENAME <> 'CON:') AND ((LINECOUNT MOD PAGESIZE) = 0) THEN WRITELN (OUTPUT, '< ', LINECOUNT:4, ',', MEMAVAIL:5, ' >') END ; WRITE (LST, LINECOUNT:4, ' ') ; PRTNEST (BUF) ; FOR I := 1 TO BUFCURSOR DO WRITE (LST, BUF[I]) ; WRITELN (LST) ; IF LSTFILENAME <> 'CON:' THEN WRITE (OUTPUT, '.') END ; (* OUTPUT_LINE *) PROCEDURE GETNEXTCHAR; VAR I : INTEGER; BEGIN (* GETNEXTCHAR *) IF BUFCURSOR >= LENGTH (INPUT_LINE) THEN BEGIN EOL := TRUE ; CH := ' ' ; ERROR := EOF(INFILE) END ELSE BEGIN BUFCURSOR := BUFCURSOR + 1 ; CH := INPUT_LINE [BUFCURSOR] ; BUF [BUFCURSOR] := CH ; CH := UPPER(CH) END END; (* GETNEXTCHAR *) PROCEDURE GETIDENTIFIER; VAR J,K,I: INTEGER; ID: ALFA; BEGIN (* GETIDENTIFIER *) I := 0; ID := ' '; REPEAT IF I < ALFALEN THEN BEGIN I := I+1; ID[I] := CH END; GETNEXTCHAR UNTIL ( NOT(((CH>='A') AND (CH<='Z')) OR (CH='_') OR ((CH>='0') AND (CH<='9')))) OR (ERROR); I := 1; J := NK; REPEAT K := (I+J) DIV 2; (*BINARY SEARCH*) IF KEY[K] <= ID THEN I := K+1; IF KEY[K] >= ID THEN J := K-1; UNTIL I > J; IF KEY[K] <> ID THEN SEARCH(ID) ELSE BEGIN IF (K=3) OR ((K=5) AND (LAST_KEY<>32)) OR { BEGIN or CASE } (K=32) OR (K=33) THEN { RECORD or REPEAT } BEGIN LAST_KEY := K ; IF NESTLVL = NESTMAX THEN WRITE (LST, '----Too many levels') ELSE BEGIN NESTLVL := NESTLVL + 1 ; NESTUP := TRUE END END ; IF (K=12) OR (K=40) THEN { END or UNTIL } IF NESTLVL = 0 THEN WRITE (LST, '----Nesting error ') ELSE BEGIN NESTLVL := NESTLVL - 1 ; NESTDN := TRUE END END END; (* GETIDENTIFIER *) BEGIN (* CROSSREF *) INITIALIZE; OPENFILES; WHILE NOT EOF(INFILE) AND (NOT ABORT) DO BEGIN BUFCURSOR:= 0; NESTUP := FALSE ; NESTDN := FALSE ; READLN (INFILE, INPUT_LINE) ; IF LENGTH (INPUT_LINE) > 0 THEN BEGIN EOL := FALSE ; BUFCURSOR := BUFCURSOR + 1 ; CH := INPUT_LINE [BUFCURSOR] ; BUF [BUFCURSOR] := CH ; CH := UPPER (CH) END ELSE BEGIN EOL := TRUE ; CH := ' ' END ; WHILE NOT EOL DO BEGIN IF ((CH >= 'A') AND (CH <= 'Z')) AND (NOT LITERAL) AND (NOT ACOMMENT) AND (NOT BCOMMENT) THEN GETIDENTIFIER ELSE IF (CH = '''') OR LITERAL THEN BEGIN REPEAT GETNEXTCHAR; UNTIL (CH = '''') OR (ERROR) OR EOL; LITERAL := EOL ; GETNEXTCHAR END ELSE IF (CH = '{') OR ACOMMENT THEN BEGIN WHILE (CH <> '}') AND (NOT ERROR) AND (NOT EOL) DO GETNEXTCHAR ; ACOMMENT := EOL ; GETNEXTCHAR END ELSE IF (CH = '(') OR BCOMMENT THEN BEGIN IF NOT BCOMMENT THEN GETNEXTCHAR; IF (CH = '*') OR BCOMMENT THEN BEGIN IF NOT BCOMMENT THEN GETNEXTCHAR; REPEAT WHILE (CH <> '*') AND (NOT ERROR) AND (NOT EOL) DO GETNEXTCHAR ; BCOMMENT := EOL ; IF NOT EOL THEN GETNEXTCHAR UNTIL (CH = ')') OR ERROR OR EOL ; IF NOT EOL THEN GETNEXTCHAR END END ELSE GETNEXTCHAR; END; (* WHILE *) EOL := FALSE ; OUTPUT_LINE (BUF) ; LINECOUNT := LINECOUNT + 1 END ; IF NOT ABORT THEN BEGIN PAGE(LST); LINECOUNT := 0; BUFCURSOR := 0; PRINTTABLE; PAGE(LST); CLOSE(LST,I); IF I = 255 THEN WRITELN('Error closing output file') END END.