Category : Files from Magazines
Archive   : DDJ1289.ZIP
Filename : FRANZ.LST

 
Output of file : FRANZ.LST contained in archive : DDJ1289.ZIP
WRITING FILTERS IN AN OBJECT-ORIENTED LANGUAGE_
by Marty Franz


[LISTING ONE]

/* header file for RegularExpression class */

#define ANY_CHAR 1 /* ^A */
#define A_CHAR 3 /* ^C */
#define BEGIN_STR 2 /* ^B */
#define END_STR 5 /* ^E */
#define INCLUDE_SET 19 /* ^S */
#define OMIT_SET 14 /* ^N */



[LISTING TWO]

/* *************************************************
* REGULARE.CLS: RegularExpression class file *
************************************************* */

/* Class used to hold regular expressions for string
matching. */!!

inherit(Object, #RegularExpression, #(
pattern /* a pattern to match */
caseMatch /* nil, case doesn't matter */
arrow /* used to scan strings */
cursor /* used to scan patterns */), 2, nil)!!

now(RegularExpressionClass)!!

/* Create a new RegularExpression from String s. */
Def new(self, s | re)
{ re := init(new(self:Behavior));
re.pattern := makePattern(re, s);
^re;
}!!

now(RegularExpression)!!

/* Change every occurrence of string s matching the pattern to string t.
Does not allow recursion: searching occurs after the new string has been
substituted. The target string t is not a pattern, just another string,
inserted into the source string at the point of the match. Returns the
changed source string s */
Def change(self, s, t | from, last, source)
{ source :=
if caseMatch
then asUpperCase(s);
else s
endif;
from := 0;
loop
while from < size(source)
begin last := aMatch(self, source, from);
if last
then s := replace(s, t, 0, size(t), from, last);
from := from + size(t);
else from := from + 1;
endif;
endLoop;
^s;
}!!

/* Set case matching. Converts pattern to upper-case, too. You should
save the old pattern if you plan on toggling case matching a lot.*/
Def setCaseMatch(self, c)
{ caseMatch := c;
if caseMatch
then pattern := asUpperCase(pattern);
endif;
^self;
}!!

/* Match the String s against the pattern. Calls aMatch() for each
possible substring in the String. If caseMatch flag is set, then fold case
to upper before doing the search. */
Def match(self, s)
{ if caseMatch
then s := asUpperCase(s);
endif;
do(size(s),
{using(i)
if aMatch(self, s, i)
then ^i;
endif;
});
^false;
}!!

/* Compare string s against pattern starting at position from. Makes
successive calls to oneMatch. Returns index of matching character, or
nil. Remember that oneMatch() advances the arrow in the source string. */
Def aMatch(self, s, from | found)
{ arrow := from;
cursor := 0;
found := true;
loop
while found cand (cursor < size(pattern))
begin
if oneMatch(self, s)
then cursor := cursor + patternSize(self, cursor);
else ^false;
endif;
endLoop;
^arrow;
}!!

/* Match a single character passed as an argument to the set pointed to by
the pattern cursor. Returns nil if not in the set, otherwise non-nil. */
Def locate(self, c | setSize, fromCurs, toCurs, found)
{ setSize := asInt(pattern[cursor+1]);
fromCurs := cursor + 2;
toCurs := fromCurs + setSize;
found := false;
do(over(fromCurs, toCurs),
{using(i)
if pattern[i] = c
then found := true;
endif;
});
^found;
}!!

/* Match a single character in the target string against a single
character in the pattern. Returns nil or non-nil. Also advances arrow
scanning target string */
Def oneMatch(self, s | next, c)
{ next := -1;
if arrow < size(s)
then c := asInt(pattern[cursor]);
select
case c = ANY_CHAR
is next := 1;
endCase;
case c = BEGIN_STR
is
if arrow := 0
then next := 0;
endif;
endCase;
case c = A_CHAR
is
if s[arrow] = pattern[cursor+1]
then next := 1;
endif;
endCase;
case c = INCLUDE_SET
is
if locate(self, s[arrow])
then next := 1;
endif;
endCase
case c = OMIT_SET
is
if not(locate(self, s[arrow]))
then next := 1;
endif;
endCase
endSelect;
else /* at end of string, check for $ */
if asInt(pattern[cursor]) = END_STR
then next := 0;
endif;
endif;
if next >= 0
then arrow := arrow + next;
endif;
^(next >= 0);
}!!

/* Get the pattern String from a Regular Expression. */
Def pattern(self)
{ ^pattern;
}!!

/* Set the pattern String in a RegularExpression. */
Def setPattern(self, s)
{ pattern := s;
^self;
}!!

/* Initialize a RegularExpression. */
Def init(self)
{ ^self;
}!!

/* Return the size of the Pattern from position p. */
Def patternSize(self, p | c)
{ c := asInt(pattern[p]);
select
case c = A_CHAR
is ^2;
endCase;
case c = ANY_CHAR cor c = BEGIN_STR cor c = END_STR
is ^1;
endCase;
case c = 14 cor c = 19
is ^asInt(pattern[p+1]+2);
endCase;
default ^1;
endSelect;
}!!

/* Return a set of characters for inclusion in a Pattern. The first and
last characters should be [ and ]. Returns INCLUDE_SET for set or OMIT_SET
for exluded set, followed by a count of the characters in the set (just a
single character), followed by the characters themselves. */
Def fillSet(self, s | work, i, count, from, to)
{ i := 1;
count := 0;
if s[i] = '~'
then work := asString(asChar(OMIT_SET));
i := i + 1;
else work := asString(asChar(INCLUDE_SET));
endif;
work := work+asString(asChar(1)); /* holds count later */
loop
while i < size(s) and s[i] <> ']'
begin
select
case s[i] = '\'
is work := work + asString(s[i+1]);
i := i + 2;
count := count + 1;
endCase;
case s[i] = '-'
is from := asInt(s[i-1])+1;
to := asInt(s[i+1])+1;
do(over(from, to),
{using(c) work:=work+asString(asChar(c));
});
i := i + 2;
endCase;
default work := work + asString(s[i]);
i := i + 1;
count := count + 1;
endSelect;
endLoop;
work[1] := asChar(count);
^work;
}!!

/* Convert a normal String into a pattern String. Needs error checking,
and to use more OOP technique. Note that it does not set the Regular-
Expression's instance variable. */
Def makePattern(self, s | work, c, i, j)
{ work := "";
i := 0;
loop
while i < size(s)
begin c := s[i];
select
case c = '?'
is work := work+asString(asChar(ANY_CHAR));
i := i + 1;
endCase;
case c = '%'
is work := work+asString(asChar(BEGIN_STR));
i := i + 1;
endCase;
case c = '$'
is work := work+asString(asChar(END_STR));
i := i + 1;
endCase;
case c = '['
is j := indexOf(s, ']', i+1);
work := work+fillSet(self, subString(s, i, j+1));
i := j + 1;
endCase;
default work := work+asString(asChar(A_CHAR))+asString(c);
i := i + 1;
endSelect;
endLoop;
^work;
}!!




[LISTING THREE]


/* *************************************************
* FILTER.CLS: Filter class file *
************************************************* */

/* This is a class that makes possible filter programs,
like Listers, Greps, etc. in and out are objects that
respond to Stream or File protocols. initBlock, processBlock,
and closeBlock are executed when the filter is started,
running, and done, respectively. */!!

inherit(Object, #Filter, #(inObj
outObj
initBlock
processBlock
closeBlock
), 2, nil)!!

now(FilterClass)!!

/* Create a new Filter. */
Def new(self, input, output, b1, b2, b3 | f)
{ f := init(new(self:Behavior), input, output, b1, b2, b3);
^f;
}!!

now(Filter)!!

/* Once a filter has been set up, run it. Call initBlock to
initialize anything other than opening inObj and outObj.
Read a line from inObj and call processBlock. When done,
close both objects. Note that outObj is optional. Also
not the outObj is the receiver of the blocks. This is because
it's the object that's programmer-defined. */
Def run(self | str)
{ eval(initBlock, outObj, inObj);
open(inObj, 0);
checkError(inObj);
if outObj
then create(outObj);
checkError(outObj);
endif;
loop
while str := readLine(inObj)
begin eval(processBlock, outObj, str);
endLoop;
eval(closeBlock, outObj, inObj);
close(inObj);
if outObj
then close(outObj);
endif;
}!!

/* Initialize a new Filter. Fill-in all its instance
variables. */
Def init(self, input, output, b1, b2, b3)
{ inObj := input;
outObj := output;
initBlock := b1;
processBlock := b2;
closeBlock := b3;
^self;
}!!




[LISTING FOUR]

/* *************************************************
* GREP.CLS: Grep class file *
************************************************* */

/* This is a class that holds a single method that will analyze
a file for regular expressions. This method is an example of a
generic Filter, too. */!!

inherit(Object, #Grep, #(fileName
pattern
matches), 2, nil)!!

now(GrepClass)!!

/* Create and run Grep for a file and a pattern. */
Def run(self, file, expr | g)
{ g := init(new(self:Behavior), file, expr);
^g;
}!!

now(Grep)!!

/* The Filter will try to close us. Make sure something
safe happens. */
Def close(self)
{ ^self;
}!!

/* We need a checkError message because the Filter will try to
call one. For a Grep this doesn't do anything. */
Def checkError(self)
{ ^self;
}!!

/* A dummy method, needed because the Filter will try to
create() a Grep object with a mode value. Ours doesn't do
anything. If it were a real file, it would be created. */
Def create(self)
{ ^self;
}!!

/* When done, print the number of matches that were found. */
Def finish(self, inFile)
{ printLine(asStringRadix(matches, 10)+" lines matched pattern.");
^self;
}!!

/* Process a single line. Compare it against the pattern.
If it matches, print it. */
Def process(self, s)
{ if match(pattern, s)
then printLine(s);
matches := matches + 1;
endif;
^self;
}!!

/* Start grep. Assign the filename to the TextFile */
Def start(self, inFile)
{ setName(inFile, fileName);
printLine("");
printLine("Searching file: "+fileName);
^self;
}!!

/* Open the input text file and search it for the expression.
If found, print it. */
Def init(self, file, expr | f, start, process, finish,
input, output)
{ input := new(TextFile);
setDelimiter(input, CR_LF);
output := self;
pattern := new(RegularExpression, expr);
fileName := file;
matches := 0;
start :=
{using(me, inFile) start(me, inFile);
};
process :=
{using(me, theLine) process(me, theLine);
};
finish :=
{using(me, inFile) finish(me, inFile);
};
f := new(Filter, input, output, start, process, finish);
run(f);
^self;
}!!


[LISTING FIVE]

/* *************************************************
* REVISER.CLS: Reviser class file *
************************************************* */

/* A Reviser is like a Grep, but it takes an additional
instance variable: the new text to be revised when the
pattern is found. Another filename is also required,
to hold the revised text. Finally, there is a handle
for the new file. */!!

inherit(Grep, #Reviser, #(newText
newFileName
newFile), 2, nil)!!

now(ReviserClass)!!

/* Create and run a Reviser. */
Def run(self, file, outFile, expr, text | r)
{ r := init(new(self:Behavior), file, outFile, expr, text);
^r;
}!!

now(Reviser)!!

/* Initialize what's different about the Reviser from
the Grep. */
Def init(self, file, outFile, expr, text)
{ newText := text;
newFileName := outFile;
^init(self:Grep, file, expr);
}!!

/* Process a single line. Compare it against the pattern. If it matches,
change it. For simplicity, this does not handle multiple substitutions in
the same line. */
Def process(self, s)
{ if match(pattern, s)
then write(newFile, change(pattern, s, newText) + CR_LF);
matches := matches + 1;
endif;
^self;
}!!

/* Set the name for the input file, then do whatever else
a Grep does. */
Def start(self, inFile)
{ newFile := new(TextFile);
setName(newFile, newFileName);
setDelimiter(newFile, CR_LF);
^start(self:Grep, inFile);
}!!

/* Do a checkError on the newFile after creation. */
Def checkError(self)
{ ^checkError(newFile);
}!!

/* Create the new file. */
Def create(self)
{ create(newFile);
^self;
}!!

/* Close the new file */
Def close(self)
{ close(newFile);
^self;
}!!




  3 Responses to “Category : Files from Magazines
Archive   : DDJ1289.ZIP
Filename : FRANZ.LST

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/