{********************************************************
**
** EDIT #5 - 12 July 1980
**
** PROGRAM TITLE: Concordance
**
** WRITTEN BY: Raymond E. Penley
** DATE WRITTEN: 26 January 1980
**
** WRITTEN FOR: Personal pleasure
** Donated to Pascal/Z users Gp
**
** PROGRAM SUMMARY:
**
** Examine a piece of text and produce a list,
** in alphabetical order, of all the distinct
** words which appear in the text.
**
** INPUT AND OUTPUT FILES:
**
** INPUT FILE: DRIVE: BASIC FILE NAME . EXTENSION
** OUT FILE: DRIVE: BASIC FILE NAME . CCD
**
************* }
PROGRAM CONCORDANCE;
label 9;{abort}
const
alfa_len = 16; { length of words }
c4 = MAXINT;{ max line number }
Clearcode = 26; { clear screen }
default = 255;
dflt_str_len = default;
LLmax = default;{ max line length }
LLmin = 72; { Min line length }
space = ' ';
StrMax = 255;
type
alfa = STRING alfa_len;
byte = 0..255;
charname = (lletter, uletter, digit, blank, quote, atab,
EndOfLine, FileMark, otherchar );
charinfo = RECORD
name : charname;
valu : char
END;
dfltstr = STRING default;{ default length for all strings }
ItemRecords = record
item :alfa;
Next :^ItemRecords
end;
ItemPointers = ^ItemRecords;
str0 = string 0;
str255 = string StrMax;
var
Look : char; { Character read in from File }
cline : integer; { current line number }
currchar, { Current operative character }
nextchar : CharInfo; { Look-ahead character }
CON_wanted,
DEBUG,
error_flag: BOOLEAN;
Fbuffer : dfltstr; { Format buffer - before final Print }
flushing : (KNOT, DBL, STD, LIT);
ID : alfa; { Identifier storage }
idlen : byte; { Identifier Length }
ListHead :ItemPointers;
tab : char;
TextFile, { Input file }
Work_File: TEXT; { Output file }
wordcount: integer; { total # of words in file }
xeof, { EOF status AFTER a read }
xeoln : boolean; { EOLN status after a read }
Function length(x: str255): integer; external;
Procedure setlength(var x: str0; y: integer); external;
Function index(x,y: str255): integer; external;
PROCEDURE Error( enumb : byte);
begin
CASE enumb of
0: writeln('Fatal error!');
1: writeln('Exceeded buffer limits on read');
2: {-reserved-};
3: writeln('File not found');
4: {-reserved-}
end{ of case };
error_flag := true
end;
PROCEDURE InsertItem( Newitem :alfa);
{*
** From the book - PASCAL An Introduction
** to Methodical Programming
** Authors:
** W. Findlay and D.A. Watt
****** }
VAR entry,
PriorEntry,
Newentry :ItemPointers;
found :boolean;
Procedure INSERTWORD;
begin{ CREATE the New entry and Insert it in position }
New(Newentry);
Newentry^.item := Newitem;
Newentry^.Next := entry;
If entry = ListHead then
ListHead := Newentry
Else
PriorEntry^.Next := Newentry;
end{-of InsertWord-};
begin
{ FIND the position where the New item will be Inserted }
entry := ListHead;
found := false;
While NOT found AND (entry <> NIL) do
WITH entry^ DO
If (item < Newitem) then
begin
PriorEntry := entry;
entry := Next
end
Else
found := true;
If found then{-Crate a new entry in the list If necessary-}
begin
If (entry^.item <> Newitem) then InsertWord{ at position `entry` }
end
Else
InsertWord{ at end of list }
end{-of InsertItem-};
PROCEDURE WriteItems;
CONST Sail = '*** INDEX ***';
var entry :ItemPointers;
begin
Writeln(Work_File, Sail);
If CON_wanted then writeln(Sail);
entry := ListHead;
While entry <> NIL DO
WITH entry^ DO
begin
Writeln(Work_File, item);
If CON_wanted then writeln(item);
entry := Next
end
end{--of WriteItems-};
Procedure ReadC(var nextchar : charinfo;
var currchar : charinfo );
{ revised 4 Jan 80, rep }
begin{ Terminator status module.
Stores terminator status "AFTER" a read.
NOTE this play on words - after one char is
actually "PRIOR TO" the next character }
xeoln := EOLN(textfile);
xeof := EOF(textfile);
{ read byte module }
If NOT xeof then
READ(Textfile, Look);
{ current operative character module }
currchar := nextchar;
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 }
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{ 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);
error_flag := false;
state := scanning;
REPEAT
ReadC(Nextchar, Currchar);
If (length(fbuffer) >= LLmax) then{ exceeded length of buffer }
begin{ reset EOLN }
state := overflow;
READLN(fbuffer);{ reset EOLN }
error(1)
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" }
const space = ' ';
var Cpos : byte; { Current Position pointer }
begin{ ReadWord }
Cpos := 1; { start at the beginning of a line }
While (Cpos < length(fbuffer)) Do
begin
{ skip spaces }
while (Cpos < length(Fbuffer)) AND (fbuffer[Cpos]=space) Do Cpos:=Cpos+1;
Setlength(ID,0);{ start with a null array }
while (Cpos < length(fbuffer)) AND (fbuffer[Cpos ] <> space) Do
begin{ accept only non-spaces }
If (length(ID)<alfa_len) then append(ID,fbuffer[ Cpos ]);
Cpos := Cpos +1;
end{ while };
while (length(ID)<alfa_len) Do append(ID,space);
{}If DEBUG then writeln(' ',ID);
InsertItem(ID);
WordCount := WordCount + 1;
end;
end{-of ReadWord-};
Procedure SKIP(n : byte);
var i : byte;
begin For i:=1 to N do writeln
end;
Function ConnectFiles: boolean;
const dflt_extension = '.CCD';
fid_len = 14; { Max length CP/M file names }
type FID = string fid_len;
var File_ID,
New_ID : FID;
ix,jx : byte;
Procedure PAD(var ID: fid; required: byte);
const space = ' ';
begin
while (length(ID)<required) Do append(ID,space);
end;
begin{-GETID-}
ConnectFiles := true;
Setlength(File_ID,0);
writeln;
write('Enter <Drive:><File name> ');
readln(File_ID);
If (length(File_ID)>fid_len) then
setlength(File_ID,fid_len)
Else
PAD(File_ID, fid_len);
RESET(File_ID, TextFile);
If EOF(TextFile) then{ ABORT }
begin
error(3);
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);
append(New_ID, File_ID);
append(New_ID, dflt_extension);
PAD(New_ID, fid_len);
REWRITE(New_ID, Work_File);
end;
End{ of ConnectFiles };
Procedure Initialize;
var ch: char;
begin
ListHead := NIL; { MAKE the LIST EMPTY }
cline := 0; { current line counter }
wordcount := 0;
idlen := 0;
tab := chr(9); { ASCII Tab character }
flushing := KNOT{ flushing };
{-INITIALIZE look-ahead char-}
nextchar.name := blank;
nextchar.valu := space;
writeln;
WRITE('DEBUG?');READ(Ch);
DEBUG := ((Ch='Y') or (Ch='y'));
writeln;
WRITE('Output to Console?');READ(Ch);
CON_wanted := ((Ch='Y') or (Ch='y'));
end;
Procedure Sign_On;
begin
Clear(clearcode);
writeln;
writeln(' ':20,'*** C O N C O R D A N C E ***');
SKIP(4);
end;
Begin{ main body of Concordance }
Sign_On;
If NOT ConnectFiles then {ABORT} goto 9;
Initialize;
SKIP(4);
cline:= cline +1;
GetL(Fbuffer) { attempt to read the first line };
while ((currchar.name<>filemark) AND (NOT error_flag)) do
begin
{} If DEBUG then writeln('line',cline:5,' ',fbuffer);
ReadWord{Analyze the Text into single 'words' };
If cline=c4 then cline:=0;
cline := cline +1;
GetL(Fbuffer) { attempt to read another line of text };
end{ while };
Clear(clearcode);
WriteItems; { Write all the Items in order }
writeln;
writeln(' ':18, '*** SUMMARY ***');
writeln('Total # lines =',cline -1);
writeln('Total # words =', wordcount);
writeln;
9:{ABORT};
end{ of ConCordance }.