{++++++++++++++++++++++++++++++++++++++++++++++++++++++++} {+ +} {+ PROGRAM TITLE: Cross Reference Generator +} {+ +} {+ WRITTEN BY: Peter Grogono +} {+ DATE WRITTEN: ? +} {+ +} {+ SUMMARY: +} {+ +} {+ 1. Output Files: +} {+ default is to disk files: +} {+ a. output file = file name + '.XRF' +} {+ all identifiers and their line # +} {+ b. output file = file name + '.PRN' +} {+ the file with all lines numbered +} {+ 2. LISTING Device: +} {+ Output may be to either the console or +} {+ the printer but NOT both. +} {+ +} {+ MODIFICATION RECORD: +} {+ 12-AUG-80 -modified for Pascal/Z v3.0 +} {+ -by Raymond E. Penley +} {+ 16-AUG-80 -added function ConnectFiles +} {+ 17-AUG-80 -added GetL, ReadC, ReadWord +} {+ 22-AUG-80 -selective use of control-c +} {+ +} {+ +} {++++++++++++++++++++++++++++++++++++++++++++++++++++++++} PROGRAM XREFG2; { Cross Reference Generator } (*$P-,F-,M- [symbolic I/O OFF, floating point checking OFF, integer mult & div checking OFF] *) CONST alfa_length = 8; BLANKS = ' '; dflt_str_len = 255; entrygap = 0; { # of blank lines between line numbers} fid_len = 14; { Max length CP/M file names } heading = 'Cross-Reference Listing'; headingsize = 3; {number of lines for heading} LLmax = dflt_str_len; {} MaxOnLine = 10; Maxlines = MAXINT; {longest document permitted} MaxWordlen = alfa_length;{longest word read without truncation} Maxlinelen = 80; {length of output line} MaxOnPage = 60; {size of output page} numbergap = 2; {number of gaps between line numbers} {} NumKeys = 46; {number of Pascal reseve words} {Read your Pascal manuals on this one!} {} NumKeysP1 = NumKeys + 1; {} NumberWidth = 6; space = ' '; TYPE {} ALFA = PACKED ARRAY[1..alfa_length] OF CHAR; {} BYTE = 0..255; {} CHARNAME = (lletter, uletter, digit, blank, quote, atab, EndOfLine, FileMark, otherchar ); {} CHARINFO = RECORD name : charname; valu : CHAR END; COUNTER = 1..Maxlines; {} dfltstr = string dflt_str_len; FID = string fid_len; lineindex = 1..Maxlinelen; {} pageindex = BYTE; Wordindex = 1..MaxWordlen; Queuepointer = ^Queueitem; Queueitem = RECORD linenumber : counter; NextInQueue: Queuepointer END; EntryType = RECORD Wordvalue : alfa; FirstInQueue, lastinQueue: Queuepointer END; treepointer = ^node; node = RECORD entry : EntryType; left, right : treepointer END; S$0 = string 0; S$255 = string 255; VAR bell : CHAR; blankindex : BYTE; currchar, { Current operative character } nextchar : charinfo; { Look-ahead character } fatal_error : BOOLEAN; FILE_ID, { CP/M file name } PRN_ID, { basic file name + '.PRN' } New_ID : FID; { basic file name + '.XRF' } fbuffer : dfltstr; { Format buffer - before final Print } FIN : TEXT; flushing : (KNOT, DBL, STD, LIT); form_feed : CHAR; Key : ARRAY[1..NumKeysP1] OF alfa; letters : SET OF CHAR; LISTING : BOOLEAN; Look : char; { Character read in from File } {}{OUTPUT : TEXT; } { Listing device -console or printer } tab : CHAR; wordcount : INTEGER; { total # of words in file } WordTree : treepointer; xeof, { EOF status AFTER a read } xeoln : BOOLEAN; { EOLN status after a read } (*$C- [Control-C OFF]***********************************************) FUNCTION length(x: S$255): INTEGER; EXTERNAL; PROCEDURE setlength(VAR x: S$0; y: INTEGER); EXTERNAL; FUNCTION index(x,y: S$255): INTEGER; EXTERNAL; PROCEDURE PAGE(VAR fx: TEXT); BEGIN WRITE(fx, form_feed); END; PROCEDURE CLEAR{output}; VAR ix : 1..24; BEGIN FOR ix:=1 TO 24 DO WRITELN; END; PROCEDURE BuildTree(VAR tree: treepointer); VAR CurrentWord : alfa; Currentline: INTEGER; FOUT: TEXT; { local output file } PROCEDURE Entertree(VAR subtree: treepointer; Word : alfa; line :counter); VAR nextitem : Queuepointer; BEGIN IF subtree=nil THEN BEGIN {create a new entry} NEW(subtree); WITH subtree^ DO BEGIN left := nil; right := nil; WITH entry DO BEGIN Wordvalue := Word; NEW(FirstInQueue); LastinQueue := FirstInQueue; WITH FirstInQueue^ DO BEGIN linenumber := line; NextInQueue := nil; END;{WITH FirstInQueue} END;{WITH entry} END;{WITH subtree} END {create a new entry} ELSE {append a list item} WITH subtree^, entry DO IF Word=Wordvalue THEN BEGIN IF lastinQueue^.linenumber <> line THEN BEGIN NEW(nextitem); WITH Nextitem^ DO BEGIN linenumber := line; NextInQueue := nil; END;{WITH} lastinQueue^.NextInQueue := Nextitem; lastinQueue := nextitem; END; END ELSE IF Word < Wordvalue THEN Entertree(left,Word,line) ELSE Entertree(right,Word,line); END;{Entertree} Procedure ReadC({updating} VAR nextchar : charinfo; {returning}VAR currchar : charinfo ); { revised 4 Jan 80, rep } { Defined the chars "^", "$", and "_" as lowercase letters } BEGIN {+++ File status module. +++ Stores file status "AFTER" a read. NOTE this play on words - after one char is actually "PRIOR TO" the next character } xeoln := EOLN(FIN); xeof := EOF(FIN); {+++ read BYTE module +++} IF NOT xeof THEN READ(FIN, Look); {+++ current operative character module +++} currchar := nextchar; {+++ Classify the character just read +++} WITH nextchar DO BEGIN{ Look-ahead character name module } IF xeof THEN name := FileMark ELSE IF xeoln THEN name := EndOfLine ELSE IF Look IN ['^', '$', '_', 'a'..'z'] THEN {lower case plus} name := lletter ELSE IF Look IN ['A'..'Z'] THEN {upper case} name := uletter ELSE IF Look IN ['0'..'9'] THEN {digit} name := digit ELSE IF Look = '''' THEN name := quote ELSE IF Look = TAB THEN name := atab ELSE IF Look = space THEN name := blank ELSE name := otherchar; CASE name of{ store character value module } EndOfLine, FileMark: Valu := space; ELSE: Valu := Look END{ case name of }; End{ Look-ahead character name module }; END; {of ReadC} PROCEDURE GetL( VAR fbuffer : dfltstr ); {++++++++++++++++++++++++++++++++++++++++++++++++++++++++} {+ Get a line of text into users buffer. +} {+ Flushes comment lines: +} {+ Flushes lines of Literals: 'this is it' +} {+ Ignores special characters & tabs: +} {+ Recognizes End of File and End of Line. +} {+ +} {+GLOBAL +} {+ flushing : (KNOT, DBL, STD, LIT); +} {+ fbuffer = dfltstr +} {+ LLmax = 0..Max Line length; +} {++++++++++++++++++++++++++++++++++++++++++++++++++++++++} VAR state : (scanning, terminal, overflow); BEGIN { GetL } setlength(fbuffer,0); fatal_error := FALSE; state := scanning; REPEAT ReadC(nextchar, currchar); {} WRITE(FOUT, currchar.valu); {} IF listing THEN WRITE( {OUTPUT,} currchar.valu); IF (length(fbuffer) >= LLmax) THEN{ exceeded length of buffer } BEGIN{ reset EOLN } fatal_error := TRUE; state := overflow; setlength(fbuffer,0); WRITE(bell); WRITELN('EXCEEDED LENGTH OF INPUT BUFFER'); END ELSE BEGIN IF (currchar.name IN [FileMark,EndOfLine]) THEN state:=terminal{ END of line or END of file }; CASE flushing of KNOT: CASE currchar.name of lletter, uletter, digit, blank: BEGIN{ store } append(fbuffer,currchar.valu); END; atab, quote, otherchar: BEGIN{ Flush comments -convert tabs & other chars to spaces } IF (currchar.valu='(') and (nextchar.valu='*') THEN flushing := DBL ELSE IF (currchar.valu='{') THEN flushing := STD ELSE IF currchar.name=quote THEN flushing := LIT; { convert to a space } append(fbuffer,space); END; else: { END of line -or- file mark } append(fbuffer,currchar.valu) END{ case currchar name of }; DBL: { scanning for a closing - double comment } IF (currchar.valu ='*') and (nextchar.valu =')') THEN flushing := KNOT; STD: { scanning for a closing curley } IF currchar.valu = '}' THEN flushing := KNOT; LIT: { scanning for a closing quote } IF currchar.name = quote THEN flushing := KNOT END{ flushing case } END{ ELSE } UNTIL (state<>scanning); END; {of GetL} PROCEDURE ReadWord; {++++++++++++++++++++++++++++++++++++++++++++++++} {+ +} {+ Analyze the Line into "words" +} {+ +} {++++++++++++++++++++++++++++++++++++++++++++++++} LABEL 1; CONST TOP = NumKeys + 1; VAR ix, {temp indexer} idlen, {length of the word} Cpos : BYTE; { Current Position pointer } BEGIN{ ReadWord } Cpos := 1; { start at the beginning of a line } WHILE Cpos < length(fbuffer) DO BEGIN {Cpos space) DO BEGIN{ accept only non-spaces } IF idlen < MaxWordlen THEN BEGIN idlen := idlen + 1; CurrentWord[idlen] := fbuffer[Cpos]; END; Cpos := Cpos +1; END{ WHILE }; {} IF idlen=0 THEN {no word was found} GOTO 1; IF idlen >= blankindex THEN blankindex := idlen ELSE REPEAT CurrentWord[blankindex] := space; blankindex := blankindex - 1; UNTIL blankindex=idlen; WordCount := WordCount + 1; {++++++++++++++++++++++++++++++++++} {+ linear search with sentinel +} {++++++++++++++++++++++++++++++++++} Key[TOP] := CurrentWord; ix := 0; REPEAT ix := ix + 1; UNTIL Key[ix] = CurrentWord; {++++++++++++++++++++++++++++++++++} {} IF ix=TOP THEN {CurrentWord is not a reserve word, so} EnterTree(tree,CurrentWord,Currentline); 1:{Here is no word }; END; {WHILE Cposfilemark) AND (NOT fatal_error)) DO BEGIN Currentline := Currentline + 1; WRITE(FOUT, Currentline:6,': '); IF listing THEN WRITE({OUTPUT,} Currentline:6,': '); GetL(fbuffer) { attempt to read the first line }; WRITELN(FOUT); IF listing THEN WRITELN{output}; ReadWord; {Analyze the Text into single 'words' } END; {While} PAGE(FOUT); END; {of BuildTree}{CLOSE(PRN_ID);} PROCEDURE PrintTree(tree: treepointer); { GLOBAL MaxOnLine = max line references per line NumberWidth = field for each number } VAR FOUT: TEXT; { local output file } pageposition: pageindex; PROCEDURE PrintEntry(subtree: treepointer; VAR position: pageindex); VAR ix: Wordindex; itemcount : 0..Maxlinelen; itemptr : Queuepointer; PROCEDURE PrintLine(VAR Currentposition: pageindex; newlines: pageindex); VAR linecounter: pageindex; BEGIN {} IF (Currentposition + newlines) < MaxOnPage THEN BEGIN {} FOR linecounter:=1 TO newlines DO WRITELN(FOUT); {} IF listing THEN FOR linecounter:=1 TO newlines DO WRITELN{OUTPUT}; Currentposition := Currentposition + newlines; END ELSE BEGIN {} PAGE(FOUT); {} WRITELN(FOUT,heading); {} FOR linecounter := 1 TO headingsize - 1 DO WRITELN(FOUT); {} IF listing THEN BEGIN CLEAR{OUTPUT}; {PAGE(OUTPUT);} WRITELN({OUTPUT,} heading); FOR linecounter := 1 TO headingsize - 1 DO WRITELN{OUTPUT}; END; Currentposition := headingsize + 1; END END;{PrintLine} BEGIN{PrintEntry} IF subtree<>nil THEN WITH subtree^ DO BEGIN PrintEntry(left,position); PrintLine(position,entrygap + 1); WITH entry DO BEGIN {} FOR ix:=1 TO MaxWordlen DO WRITE(FOUT, WordValue[ix]); {} IF listing THEN FOR ix:=1 TO MaxWordlen DO WRITE({OUTPUT,} WordValue[ix]); itemcount := 0; itemptr := FirstInQueue; WHILE itemptr <> nil DO BEGIN itemcount := itemcount + 1; IF itemcount > MaxOnLine THEN BEGIN PrintLine(position,1); {} WRITE(FOUT, space:MaxWordlen); {} IF listing THEN WRITE({OUTPUT,} space:MaxWordlen); itemcount := 1; END; {} WRITE(FOUT, itemptr^.linenumber: numberwidth); {} IF listing THEN WRITE({OUTPUT,}itemptr^.linenumber: numberwidth); itemptr := itemptr^.NextInQueue; END;{WHILE} END; {WITH entry} PrintEntry(right,position); END; {WITH subtree^} END; {PrintEntry} BEGIN{PrintTree} {}REWRITE(New_ID, FOUT); PAGE(FOUT); PagePosition := MaxOnPage; PrintEntry(tree,PagePosition); PAGE(FOUT); END; {of PrintTree}{CLOSE(New_ID);} (*$C+ [Control-C ON]*******************************) FUNCTION ConnectFiles: boolean; TYPE Linebuffer = string 80; VAR ix,jx, Cmllen : BYTE; Cmlline : Linebuffer; PROCEDURE GCML( VAR line : linebuffer; VAR len : BYTE ); {++++++++++++++++++++++++++++++++++++++++++++++++} {+ READ the system command line. +} {+ THIS MUST be the very first read in the +} {+ entire program! +} {++++++++++++++++++++++++++++++++++++++++++++++++} CONST input = 0; { !!!! PASCAL/Z !!! } BEGIN setlength(line,0); len := 0; IF NOT EOLN(input) THEN BEGIN READLN(line); len := length(line); END; END; {of GCML} PROCEDURE PAD(VAR this_ID: fid; required: BYTE); BEGIN WHILE (length(this_ID) FILE name - '); READLN(FILE_ID); END ELSE FILE_ID := Cmlline; IF (length(FILE_ID)>fid_len) THEN setlength(FILE_ID,fid_len); PAD(FILE_ID, fid_len); RESET(FILE_ID, FIN); IF EOF(FIN) THEN{ ABORT } BEGIN WRITE(bell); WRITELN('FILE NOT FOUND'); fatal_error := TRUE; ConnectFiles := FALSE; END ELSE BEGIN ix := index(FILE_ID,'.'); { search for an extension } jx := index(FILE_ID,' '); { search for the first space } IF (ix=0) THEN{ no extension was specified } setlength(FILE_ID,jx-1) ELSE setlength(FILE_ID,ix-1); setlength(New_ID,0); { New_ID := CONCAT(FILE_ID, '.XRF'); } append(New_ID, FILE_ID); append(New_ID, '.XRF'); PAD(New_ID, fid_len); setlength(PRN_ID,0); { PRN_ID := CONCAT(FILE_ID, '.PRN'); } append(PRN_ID, FILE_ID); append(PRN_ID, '.PRN'); PAD(PRN_ID, fid_len); END; END{ of ConnectFiles }; (*$C- [control-c OFF]***********************************) PROCEDURE Initialize; VAR Ch: CHAR; con_wanted, tty_wanted : BOOLEAN; BEGIN bell := CHR(7); IF ConnectFiles THEN BEGIN letters := ['A'..'Z','a'..'z']; Key[ 1] := 'AND '; Key[ 2] := 'ARRAY '; Key[ 3] := 'BEGIN '; Key[ 4] := 'BOOLEAN '; {+++ NOT A RESERVE WORD +++} Key[ 5] := 'CASE '; Key[ 6] := 'CHAR '; {+++ NOT A RESERVE WORD +++} Key[ 7] := 'CONST '; Key[ 8] := 'DIV '; Key[ 9] := 'DOWNTO '; Key[10] := 'DO '; Key[11] := 'ELSE '; Key[12] := 'END '; Key[13] := 'EXIT '; {+++ NOT a Pascal reserve word +++} Key[14] := 'FILE '; Key[15] := 'FOR '; Key[16] := 'FUNCTION'; Key[17] := 'GOTO '; Key[18] := 'IF '; Key[19] := 'IN '; Key[20] := 'INPUT '; {+++ NOT A RESERVE WORD +++} Key[21] := 'INTEGER '; {+++ NOT A RESERVE WORD +++} Key[22] := 'LABEL '; Key[23] := 'MOD '; Key[24] := 'NIL '; Key[25] := 'NOT '; Key[26] := 'OF '; Key[27] := 'OR '; Key[28] := 'OUTPUT '; {+++ NOT A RESERVE WORD +++} Key[29] := 'PACKED '; Key[30] := 'PROCEDUR'; Key[31] := 'PROGRAM '; Key[32] := 'REAL '; {+++ NOT A RESERVE WORD +++} Key[33] := 'RECORD '; Key[34] := 'REPEAT '; Key[35] := 'SET '; Key[36] := 'STRING '; {+++ NOT a Pascal reserve word +++} Key[37] := 'TEXT '; {+++ NOT A RESERVE WORD +++} Key[38] := 'THEN '; Key[39] := 'TO '; Key[40] := 'TYPE '; Key[41] := 'UNTIL '; Key[42] := 'VAR '; Key[43] := 'WHILE '; Key[44] := 'WITH '; Key[45] := 'WRITE '; {+++ NOT A RESERVE WORD +++} Key[46] := 'WRITELN '; {+++ NOT A RESERVE WORD +++} blankindex := alfa_length; tab := CHR(9); { ASCII Tab character } form_feed := CHR(12); flushing := KNOT{ flushing }; WRITELN; WRITELN('Output Device:'); WRITE( ' CONSOLE ->'); READLN(Ch); con_wanted := ( (Ch='Y') OR (Ch='y') ); WRITE( ' PRINTER ->'); READLN(Ch); tty_wanted := ( (Ch='Y') OR (Ch='y') ); If tty_wanted THEN con_wanted := FALSE; IF NOT (con_wanted OR tty_wanted) THEN LISTING := FALSE ELSE BEGIN LISTING := TRUE; {++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ IF con_wanted THEN REWRITE('CON:', OUTPUT); IF tty_wanted THEN REWRITE('LST:', OUTPUT); +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++} END; WRITELN; END; {IF ConnectFiles} END; {of Initialize} BEGIN { Cross Reference } CLEAR{output}; WRITELN(' ':22, 'CROSS REFERENCE GENERATOR'); WRITELN;WRITELN;WRITELN;WRITELN; Initialize; IF NOT fatal_error THEN BEGIN WordTree := NIL; {Make the Tree empty} BuildTree(WordTree); PrintTree(WordTree); END; {}WRITELN; END. { Cross Reference } .