home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Oakland CPM Archive
/
oakcpm.iso
/
cpm
/
modula2
/
cross2.mod
< prev
next >
Wrap
Text File
|
1987-01-08
|
15KB
|
287 lines
(* Cross reference as above, but using a hash table instead
of a binary tree to store the words encountered. *)
(***************************************************************************************)
(* MODULE crossref--This module is a cross reference generator. A file which is *)
(* specified by the user is read and a cross reference table of all the words is built.*)
(* A word consists of a letter and any combination of letters and digits thereafter *)
(* until a separator, i.e. blanks, ends of lines, special characters, is read. Quotes *)
(* and comments are ignored. The cross reference table is a hash table which stores *)
(* the words and the number of the line on which the word appeared. When the table *)
(* is generated, its contents are printed on the screen. This program is the MODULA-2 *)
(* translation of the PASCAL program 11.2. *)
MODULE crossref;
FROM InOut IMPORT (* get necessary i/o files *)
Read, WriteString, Write, WriteLn, WriteCard, OpenInput, CloseInput, Done;
FROM Storage IMPORT (* get NEW procedure *)
ALLOCATE;
CONST ff = 14c; (* clear the screen constant *)
eol = 36c; (* end of line constant *)
quote = 42c; (* double quote mark *)
wordlen = 10; (* maximum word length *)
numperline = 8; (* no of line numbers per display line *)
digpernum = 6; (* maximum number of digits per number *)
maxline = 9999; (* maximum number of lines in file *)
prime = 997; (* number of hash table entries *)
blank = " "; (* blank character constant *)
filetype = "TEXT"; (* default filename extension *)
TYPE index = [0..prime]; (* range of hash table *)
alfa = ARRAY[1..wordlen] OF CHAR; (* word string *)
relation = (equal,less,greater); (* used for string comparisons *)
itemref = POINTER TO item;
word = RECORD (* hash table entries *)
key : alfa; (* word found in text *)
first, last: itemref; (* pointer to cross reference list *)
follow : index (* hash number of next entry *)
END;
item = RECORD (* cross reference list *)
lineno: [0..maxline]; (* line number word occurred on *)
next : itemref (* pointer to next item in list *)
END;
VAR i : index; (* index to hash table *)
top : index; (* current hash table entry *)
idcntr : INTEGER; (* index to id array *)
id : alfa; (* contains the current word *)
free : alfa; (* blank word *)
table : ARRAY [0..prime] OF word; (* hash table *)
current : CARDINAL; (* current line number *)
ch : CHAR; (* current character *)
tablefull: BOOLEAN; (* flags if table gets full *)
PROCEDURE compare(j,k:alfa):relation;
(**************************************************************************************)
(* This function compares the two strings j and k to see how they compare. If j = k *)
(* then the value of equal is returned. If j < k then the value is less is returned *)
(* and if j > k tthen the value greater is returned. *)
(**************************************************************************************)
VAR compvalue : relation; (* function value *)
through : BOOLEAN; (* flags when through with loop *)
i : INTEGER; (* array index *)
BEGIN
compvalue := equal; (* initializations *)
i := 1;
through := FALSE;
WHILE (NOT through) & (i <= 10) DO (* compare the two strings *)
IF CAP(j[i]) = CAP(k[i]) THEN
INC(i)
ELSE
through := TRUE;
IF CAP(j[i]) < CAP(k[i]) THEN
compvalue := less
ELSE
compvalue := greater
END
END
END;
RETURN compvalue
END compare;
PROCEDURE search():BOOLEAN;
(**************************************************************************************)
(* This function searches the hash table to see if an entry for the current word *)
(* already exists. This is done by calculating the hash value of the current word. *)
(* If no entry exists at the hash value slot in the table, then an entry is created *)
(* for that word and an item list created. If the entry already exists, then only a *)
(* new item node is created and added to the item list. If the hash slot is already *)
(* occupied by a different word, then the hash table is searched for an empty slot. *)
(* If one is found, then it is filled in with the current word, etc. If no empty *)
(* slot can be found, then a message is printed indicating table overflow and the *)
(* procedure quits, returning the value of FALSE. *)
(**************************************************************************************)
VAR hash : CARDINAL; (* contains hash value *)
addvalue : index; (* contains search increment value *)
done : BOOLEAN; (* flags when finished *)
full : BOOLEAN; (* flags if table is full *)
x : itemref; (* pointer to current item list *)
compvalue : relation; (* contains result of compare *)
BEGIN
full := FALSE; (* initialize *)
hash := 0;
done := FALSE;
addvalue := 1;
NEW(x); (* get a new item list node *)
x^.lineno := current; (* fill in current line number *)
x^.next := NIL; (* set next link to nil *)
FOR i := 1 TO wordlen (* calculate hash value *)
DO
hash := (hash + ORD(id[i])) MOD prime
END;
REPEAT (* continue searching until done *)
compvalue := compare(id, table[hash].key); (* compare id to key to see if equal *)
IF compvalue = equal THEN (* if word entry already exists *)
done := TRUE; (* flag to end loop *)
table[hash].last^.next := x; (* link last item node to new node *)
table[hash].last := x (* link table pointer to new last node*)
ELSE
compvalue := compare(free,table[hash].key);
IF compvalue = equal THEN (* if no entry exists *)
WITH table[hash] DO
key := id; (* fill in current word *)
first := x; (* link to item node *)
last := x;
follow := top (* fill in last hash table entry *)
END;
top := hash; (* set to current hash table entry *)
done := TRUE
ELSE (* collision occurred *)
hash := hash + addvalue; (* incrmt hash to check next entry *)
addvalue := addvalue + 2; (* increment displacement *)
IF hash >= prime THEN (* if hash value greater than length *)
hash := hash - prime (* reset hash value *)
END;
IF addvalue = prime THEN (* if table is full *)
done := TRUE; (* flag that search is through *)
full := TRUE; (* flag that table is full *)
WriteString("Table Overflow");
WriteLn
END
END
END
UNTIL done;
RETURN full
END search;
PROCEDURE printtable;
(**************************************************************************************)
(* This procedure prints out the cross reference table. It lists each word and the *)
(* line numbers on which that word occurred. Printtable has an internal procedure *)
(* printword that handles printing the word and its line references. The cross *)
(* reference table is printed out in alphabetical order. *)
(**************************************************************************************)
VAR hold : index; (* contains the current entry index *)
least : index; (* contains index to least word *)
move : index; (* used to search for least word *)
compvalue : relation; (* contains compare result *)
PROCEDURE printword(w: word);
VAR numcnt: INTEGER; (* keeps track line nos on screen *)
x : itemref; (* pointer to current item node *)
BEGIN
Write(blank);
WriteString(w.key);
x := w.first;
numcnt := 0;
REPEAT (* do until all line numbers printed *)
IF numcnt = numperline THEN (* if need a new line for line nos *)
numcnt := 0; (* reset counter *)
WriteLn;
Write(blank);
WriteString(free)
END;
INC(numcnt);
WriteCard(x^.lineno,digpernum); (* write the line number *)
Write(blank); (* move to next item node *)
x := x^.next
UNTIL x = NIL;
WriteLn;
END printword;
BEGIN
hold := top; (* start at last entry to be added *)
WHILE hold <> prime (* do for all of the table *)
DO
least := hold; (* initialize for alphabetic search *)
move := table[hold].follow;
WHILE move <> prime (* search table for least entry *)
DO
compvalue := compare(table[move].key,table[least].key);
IF compvalue = less THEN
least := move
END;
move := table[move].follow
END;
printword(table[least]); (* print the word and its line nos *)
IF least <> hold THEN (* make sure entry won't get printed*)
table[least].key := table[hold].key;
table[least].first := table[hold].first;
table[least].last := table[hold].last
END;
hold := table[hold].follow (* move to the next entry *)
END
END printtable;
BEGIN (* ***MAIN PROCEDURE*** *)
current := 0; (* initialize *)
top := prime;
tablefull := FALSE;
FOR i := 1 TO wordlen DO
free[i] := blank
END;
OpenInput(filetype); (* request filename and open file *)
IF NOT Done THEN (* if file does not exist quit *)
WriteString("Error--file DK.file.TEXT does not exist")
ELSE (* otherwise continue *)
FOR i := 1 TO prime DO (* more initialization *)
table[i].key := free
END;
Read(ch); (* get the first character *)
WHILE NOT tablefull DO (* do while table is not full *)
WHILE Done DO (* do while end of file not reached*)
IF current = maxline THEN (* counter exceeds allowed line no *)
current := 0 (* reset counter *)
END;
INC(current);
WriteCard(current,digpernum); (* write current line no to screen *)
Write(blank);
WHILE (ch <> eol) & (Done) DO (* while not at end of file line *) id := free;
IF (CAP(ch)>= "A") & (CAP(ch)<="Z") THEN (* see if alphabetic *)
idcntr := 0;
REPEAT (* get the word and put in id *)
IF idcntr < wordlen THEN
INC(idcntr);
id[idcntr] := ch;
END;
Write(ch);
Read(ch);
UNTIL ((CAP(ch)<"A") OR (CAP(ch)>"Z")) & ((ch<"0") OR (ch>"9"));
tablefull := search() (* call search to add to table *)
ELSE (* if not a word *)
IF ch = quote THEN (* if a quote ignore between quotes*)
REPEAT
Write(ch);
Read(ch)
UNTIL ch = quote
ELSIF ch = "{" THEN (* if a brace ignore between braces*)
REPEAT
Write(ch);
Read(ch)
UNTIL ch = "}"
END;
Write(ch);
Read(ch);
END; (* end if alphabetic statement *)
END; (* end while not eol loop *)
WriteLn;
Read(ch);
END; (* end while not eof loop *)
tablefull := TRUE; (* exit outer loop so can print tab*)
END;
CloseInput; (* close the input file *)
(* Write(ff) *)
printtable; (* print the table *)
END
END crossref.