TextFindImpl.mesa
Copyright Ó 1991, 1992 by Xerox Corporation. All rights reserved.
Doug Wyatt, March 19, 1992 4:44 pm PST
DIRECTORY
Char,
CharOps,
Rope,
TextFind,
TextFindPrivate;
TextFindImpl: CEDAR PROGRAM
IMPORTS Char, CharOps, Rope
EXPORTS TextFind
~ BEGIN OPEN TextFind;
XCHAR: TYPE ~ Char.XCHAR;
ROPE: TYPE ~ Rope.ROPE;
Error: PUBLIC ERROR [index: INT, reason: ROPE] ~ CODE;
refFalse: REF BOOL ~ NEW[BOOL ¬ FALSE];
Literal searching
LiteralSearch: PUBLIC PROC [direction: Direction,
targetHash: PROC [INT] RETURNS [BYTE], targetStart: INT, targetLen: INT,
objectHash: PROC [INT] RETURNS [BYTE], objectStart: INT, objectLen: INT,
match: PROC [objectStart, targetStart, len: INT] RETURNS [BOOL],
interrupt: REF BOOL ¬ NIL] RETURNS [found: BOOL ¬ FALSE,
matchStart, matchEnd: INT ¬ 0] ~ {
IF interrupt=NIL THEN interrupt ¬ refFalse;
IF targetLen>=0 AND objectLen>=0 AND targetLen<=objectLen THEN {
objectStop: INT ~ objectStart+objectLen-targetLen;
matchStart ¬ IF direction=backward THEN objectStop ELSE objectStart;
IF targetLen=0 THEN GOTO Found
ELSE { -- 0 < targetLen <= objectLen
skip: PACKED ARRAY BYTE OF BYTE ¬ ALL[MIN[targetLen, BYTE.LAST]];
delta: INT ~ targetLen-1;
SELECT direction FROM
forward => {
h1: BYTE ~ targetHash[targetStart+delta];
FOR k: BYTE DECREASING IN[1..MIN[delta, BYTE.LAST]] DO
skip[targetHash[targetStart+delta-k]] ¬ k;
ENDLOOP;
UNTIL matchStart>objectStop OR interrupt­ DO
h2: BYTE ~ objectHash[matchStart+delta];
IF h1=h2 AND match[matchStart, targetStart, targetLen] THEN GOTO Found;
matchStart ¬ matchStart+skip[h2];
ENDLOOP;
};
backward => {
h1: BYTE ~ targetHash[targetStart];
FOR k: BYTE DECREASING IN[1..MIN[delta, BYTE.LAST]] DO
skip[targetHash[targetStart+k]] ¬ k;
ENDLOOP;
UNTIL matchStart<objectStart OR interrupt­ DO
h2: BYTE ~ objectHash[matchStart];
IF h1=h2 AND match[matchStart, targetStart, targetLen] THEN GOTO Found;
matchStart ¬ matchStart-skip[h2];
ENDLOOP;
};
ENDCASE;
};
EXITS Found => { matchEnd ¬ matchStart+targetLen; found ¬ TRUE };
};
IF NOT found THEN RETURN[FALSE]; -- force default return values
};
NewCharMap: PROC [proc: PROC [CHAR] RETURNS [CHAR]] RETURNS [CharMap] ~ {
map: CharMap ~ NEW[CharMapRep];
FOR c: CHAR IN CHAR DO map[c] ¬ proc[c] ENDLOOP;
RETURN[map];
};
MapIdentity: PROC [c: CHAR] RETURNS [CHAR] ~ { RETURN[c] };
MapLower: PROC [c: CHAR] RETURNS [CHAR] ~ { RETURN[CharOps.Lower[c]] };
charMapFromCase: ARRAY --case:--BOOL OF CharMap ~ [
FALSE
: NewCharMap[MapLower], TRUE: NewCharMap[MapIdentity]];
CharMapFromCase: PUBLIC PROC [case: BOOL] RETURNS [CharMap] ~ {
RETURN[charMapFromCase[case]];
};
Pattern parsing
Name: TYPE ~ TextFindPrivate.Name;
NameRep: TYPE ~ TextFindPrivate.NameRep;
Target: TYPE ~ TextFindPrivate.Target;
TargetRep: PUBLIC TYPE ~ TextFindPrivate.TargetRep;
Item: TYPE ~ TextFindPrivate.Item;
ItemRep: TYPE ~ TextFindPrivate.ItemRep;
single character: # any; @ alpha; ~@ nonalpha; % blank; ~% nonblank;
min sequence: * any; & alpha; ~& nonalpha; $ blank; ~$ nonblank;
max sequence: ** any; && alpha; ~&~& nonalpha; $$ blank; ~$~$ nonblank;
miscellaneous: 'x = use x; ~x = not x; { } selection; | boundary; < > named subpattern
xHash: XCHAR ~ Char.Widen['#];
xAt: XCHAR ~ Char.Widen['@];
xPercent: XCHAR ~ Char.Widen['%];
xAsterisk: XCHAR ~ Char.Widen['*];
xAmpersand: XCHAR ~ Char.Widen['&];
xDollar: XCHAR ~ Char.Widen['$];
xTilde: XCHAR ~ Char.Widen['~];
xSingleQuote: XCHAR ~ Char.Widen[''];
xLeftBrace: XCHAR ~ Char.Widen['{];
xRightBrace: XCHAR ~ Char.Widen['}];
xLeftAngle: XCHAR ~ Char.Widen['<];
xRightAngle: XCHAR ~ Char.Widen['>];
xBar: XCHAR ~ Char.Widen['|];
xColon: XCHAR ~ Char.Widen[':];
leftSelItem: Item ~ NEW[ItemRep.leftSel ¬ [leftSel[]]];
rightSelItem: Item ~ NEW[ItemRep.rightSel ¬ [rightSel[]]];
leftBoundaryItem: Item ~ NEW[ItemRep.leftBoundary ¬ [leftBoundary[]]];
rightBoundaryItem: Item ~ NEW[ItemRep.rightBoundary ¬ [rightBoundary[]]];
MakeName: PROC [fetch: FetchProc, start, len: INT] RETURNS [name: Name] ~ {
name ¬ NEW[NameRep[len]];
FOR i: NAT IN[0..name.length) DO name[i] ¬ fetch[start+i] ENDLOOP;
};
CreateTarget: PUBLIC PROC [size: INT, start: INT ¬ 0, len: INT ¬ LAST[INT],
fetch: FetchProc, substr: SubstrProc, pattern: BOOL ¬ FALSE]
RETURNS
[target: Target] ~ {
i0: INT ~ MIN[MAX[0, start], size];
i1: INT ~ i0+MIN[MAX[0, len], size-i0];
list: LIST OF Item ¬ NIL; -- pattern items, in reverse order
count: NAT ¬ 0; -- number of pattern items
Cons: PROC [item: Item] ~ { list ¬ CONS[item, list]; count ¬ count+1 };
IF pattern THEN {
state: {null, quote, tilde, tildeQuote, name} ¬ null; -- state of the lexical scan
braceState: {none, left, right} ¬ none; -- any braces seen?
pending: {none, seq, string} ¬ none; -- any pending item?
seqType: WildType ¬ any; seqIndex: INT ¬ 0; -- for pending seq
stringStart, stringLen: INT ¬ 0; -- for pending string
Flush: PROC ~ {
SELECT pending FROM
none => NULL;
seq => Cons[NEW[ItemRep.min ¬ [min[seqType, seqIndex-i0]]]];
string => Cons[NEW[ItemRep.string ¬ [string[stringStart-i0, stringLen]]]];
ENDCASE => ERROR;
pending ¬ none;
};
Seq: PROC [type: WildType, index: INT] ~ {
IF pending=seq AND seqType=type
THEN { Cons[NEW[ItemRep.max ¬ [max[seqType, seqIndex-i0]]]]; pending ¬ none }
ELSE { Flush[]; seqType ¬ type; seqIndex ¬ index; pending ¬ seq };
};
Put: PROC [item: Item] ~ { Flush[]; Cons[item] };
One: PROC [type: WildType, i: INT] ~ { Put[NEW[ItemRep.one ¬ [one[type, i-i0]]]] };
Chr: PROC [eq: BOOL, i: INT] ~ { Put[NEW[ItemRep.char ¬ [char[eq, i-i0]]]] };
nameStart, nameLen: INT ¬ 0;
name: Name ¬ NIL; -- current subpattern name
inNamedPattern: BOOL ¬ FALSE; -- inside a subpattern?
StartName: PROC [] ~ {
name ¬ MakeName[fetch, nameStart, nameLen];
Put[NEW[ItemRep.startName ¬ [startName[name]]]];
inNamedPattern ¬ TRUE;
};
EndName: PROC [implicit: BOOL] ~ {
IF implicit THEN Put[NEW[ItemRep.min ¬ [min[any, nameStart]]]];
Put[NEW[ItemRep.endName ¬ [endName[name]]]];
inNamedPattern ¬ FALSE;
};
FOR i: INT IN[i0..i1) DO
char: XCHAR ~ fetch[i];
SELECT state FROM
null => SELECT char FROM
xTilde => state ¬ tilde;
xSingleQuote => state ¬ quote;
xHash => One[any, i];
xAt => One[alpha, i];
xPercent => One[blank, i];
xAsterisk => Seq[any, i];
xAmpersand => Seq[alpha, i];
xDollar => Seq[blank, i];
xLeftBrace => SELECT braceState FROM
none => { Put[leftSelItem]; braceState ¬ left };
ENDCASE => ERROR Error[i, "misplaced { in pattern"];
xRightBrace => SELECT braceState FROM
none, left => { Put[rightSelItem]; braceState ¬ right };
ENDCASE => ERROR Error[i, "misplaced } in pattern"];
xLeftAngle => IF inNamedPattern
THEN ERROR Error[i, "misplaced < in pattern"]
ELSE { nameStart ¬ i+1; nameLen ¬ 0; state ¬ name };
xRightAngle => IF inNamedPattern
THEN EndName[implicit: FALSE]
ELSE ERROR Error[i, "misplaced > in pattern"];
xBar => SELECT i FROM
i0 => Put[leftBoundaryItem];
(i1-1) => Put[rightBoundaryItem];
ENDCASE => ERROR Error[i, "misplaced | in pattern"];
ENDCASE => SELECT pending FROM
string => stringLen ¬ stringLen+1;
ENDCASE => { Flush[]; stringStart ¬ i; stringLen ¬ 1; pending ¬ string };
tilde => SELECT char FROM
xSingleQuote => state ¬ tildeQuote;
xAt => { One[nonalpha, i]; state ¬ null };
xPercent => { One[nonblank, i]; state ¬ null };
xAmpersand => { Seq[nonalpha, i]; state ¬ null };
xDollar => { Seq[nonblank, i]; state ¬ null };
ENDCASE => { Chr[FALSE, i]; state ¬ null };
quote => { Chr[TRUE, i]; state ¬ null };
tildeQuote => { Chr[FALSE, i]; state ¬ null };
name => SELECT char FROM
xColon => { StartName[]; state ¬ null };
xRightAngle => { StartName[]; EndName[implicit: TRUE]; state ¬ null };
ENDCASE => nameLen ¬ nameLen+1;
ENDCASE => ERROR;
ENDLOOP;
SELECT state FROM
null => Flush[]; -- ok
tilde => ERROR Error[i1, "trailing ~ in pattern"];
quote => ERROR Error[i1, "trailing ' in pattern"];
tildeQuote => ERROR Error[i1, "trailing ~' in pattern"];
name => ERROR Error[i1, "pattern ends inside subpattern name"];
ENDCASE => ERROR;
IF inNamedPattern THEN ERROR Error[i1, "pattern ends inside subpattern"];
}
ELSE Cons[NEW[ItemRep.string ¬ [string[0, i1-i0]]]]; -- literal
target ¬ NEW[TargetRep[count] ¬ [text: substr[i0, i1-i0], pattern: ]];
FOR i: NAT DECREASING IN [0..count) DO target[i] ¬ list.first; list ¬ list.rest ENDLOOP;
}; -- of CreateTarget
Searching
Subs: TYPE ~ TextFindPrivate.Subs;
SubsRep: PUBLIC TYPE ~ TextFindPrivate.SubsRep;
Sub: TYPE ~ TextFindPrivate.Sub;
SubRep: TYPE ~ TextFindPrivate.SubRep;
Search: PUBLIC PROC [direction: Direction, target: Target,
size: INT, start: INT ¬ 0, len: INT ¬ INT.LAST, substr: SubstrProc,
matchType: MatchTypeProc, matchString: MatchStringProc,
matchProps: MatchStringProc ¬ NIL, matchBound: MatchBoundProc ¬ NIL,
interrupt: REF BOOL ¬ NIL] RETURNS [found: BOOL ¬ FALSE,
matchStart, matchEnd, selStart, selEnd: INT ¬ 0, subs: Subs ¬ NIL] ~ {
list: LIST OF Sub ¬ NIL; count: NAT ¬ 0;
Cons: PROC [name: Name, start, end: INT] ~ {
sub: Sub ~ NEW[SubRep ¬ [name: name, start: start-matchStart, len: end-start]];
list ¬ CONS[sub, list]; count ¬ count+1;
};
i0: INT ~ MIN[MAX[start, 0], size];
i1: INT ~ i0+MIN[MAX[len, 0], size-i0];
MatchType: PROC [i: INT, type: WildType] RETURNS [BOOL] ~ INLINE {
RETURN[type=any OR matchType[i, type]];
};
MatchString: PROC [i, start, len: INT] RETURNS [BOOL] ~ INLINE {
RETURN[matchString[i, target.text, start, len]];
};
MatchProps: PROC [i, start, len: INT] RETURNS [BOOL] ~ INLINE {
RETURN[matchProps=NIL OR matchProps[i, target.text, start, len]];
};
MatchBound: PROC [i: INT, bound: Bound] RETURNS [BOOL] ~ INLINE {
RETURN[matchBound=NIL OR matchBound[i, bound]];
};
nameStart, nameEnd: INT ¬ 0;
IF interrupt=NIL THEN interrupt ¬ refFalse;
SELECT direction FROM
forward => {
MatchForward: PROC [p: NAT, i: INT] RETURNS [ok: BOOL ¬ FALSE] ~ {
IF p<target.size THEN WITH target[p] SELECT FROM
item: REF ItemRep.string => RETURN[(i1-i)>=item.len
AND MatchString[i, item.start, item.len]
AND MatchProps[i, item.start, item.len]
AND MatchForward[p+1, i+item.len]];
item: REF ItemRep.char => RETURN[i<i1
AND MatchString[i, item.index, 1]=item.eq
AND MatchProps[i, item.index, 1]
AND MatchForward[p+1, i+1]];
item: REF ItemRep.one => RETURN[i<i1
AND MatchType[i, item.type]
AND MatchProps[i, item.index, 1]
AND MatchForward[p+1, i+1]];
item: REF ItemRep.min =>
FOR k: INT IN[i..i1] DO
IF MatchForward[p+1, k] THEN RETURN[TRUE];
IF (k<i1
AND MatchType[k, item.type]
AND MatchProps[k, item.index, 1]
) THEN LOOP;
RETURN[FALSE];
ENDLOOP;
item: REF ItemRep.max =>
FOR m: INT IN[i..i1] DO
IF (m<i1
AND MatchType[m, item.type]
AND MatchProps[m, item.index, 1]
) THEN LOOP;
FOR k: INT DECREASING IN[i..m] DO
IF MatchForward[p+1, k] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
ENDLOOP;
item: REF ItemRep.startName => IF (ok ¬ MatchForward[p+1, i])
THEN
Cons[name: item.name, start: (nameStart ¬ i), end: nameEnd];
item: REF ItemRep.endName => IF (ok ¬ MatchForward[p+1, i])
THEN
nameEnd ¬ i;
item: REF ItemRep.leftSel => IF (ok ¬ MatchForward[p+1, i]) THEN selStart ¬ i;
item: REF ItemRep.rightSel => IF (ok ¬ MatchForward[p+1, i]) THEN selEnd ¬ i;
item: REF ItemRep.leftBoundary => RETURN[i=0 AND MatchForward[p+1, i]];
item: REF ItemRep.rightBoundary => RETURN[i=size AND MatchForward[p+1, i]];
ENDCASE => ERROR
ELSE IF MatchBound[i, end] THEN { matchEnd ¬ selEnd ¬ i; RETURN[TRUE] };
};
FOR i: INT IN[i0..i1] UNTIL found OR interrupt­ DO
IF MatchBound[i, start] THEN
found ¬ MatchForward[0, matchStart ¬ selStart ¬ i];
ENDLOOP;
};
backward => {
MatchBackward: PROC [p: NAT, i: INT] RETURNS [ok: BOOL ¬ FALSE] ~ {
IF p>0 THEN WITH target[p-1] SELECT FROM
item: REF ItemRep.string => RETURN[(i-i0)>=item.len
AND MatchString[i-item.len, item.start, item.len]
AND MatchProps[i-item.len, item.start, item.len]
AND MatchBackward[p-1, i-item.len]];
item: REF ItemRep.char => RETURN[i>i0
AND MatchString[i-1, item.index, 1]=item.eq
AND MatchProps[i-1, item.index, 1]
AND MatchBackward[p-1, i-1]];
item: REF ItemRep.one => RETURN[i>i0
AND MatchType[i-1, item.type]
AND MatchProps[i-1, item.index, 1]
AND MatchBackward[p-1, i-1]];
item: REF ItemRep.min =>
FOR k: INT DECREASING IN[i0..i] DO
IF MatchBackward[p-1, k] THEN RETURN[TRUE];
IF (k>i0
AND MatchType[k-1, item.type]
AND MatchProps[k-1, item.index, 1]
) THEN LOOP;
RETURN[FALSE];
ENDLOOP;
item: REF ItemRep.max =>
FOR m: INT DECREASING IN[i0..i] DO
IF (m>i0
AND MatchType[m-1, item.type]
AND MatchProps[m-1, item.index, 1]
) THEN LOOP;
FOR k: INT IN[m..i] DO
IF MatchBackward[p-1, k] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
ENDLOOP;
item: REF ItemRep.startName => IF (ok ¬ MatchBackward[p-1, i])
THEN
nameStart ¬ i;
item: REF ItemRep.endName => IF (ok ¬ MatchBackward[p-1, i])
THEN
Cons[name: item.name, start: nameStart, end: (nameEnd ¬ i)];
item: REF ItemRep.leftSel => IF (ok ¬ MatchBackward[p-1, i]) THEN selStart ¬ i;
item: REF ItemRep.rightSel => IF (ok ¬ MatchBackward[p-1, i]) THEN selEnd ¬ i;
item: REF ItemRep.leftBoundary => RETURN[i=0 AND MatchBackward[p-1, i]];
item: REF ItemRep.rightBoundary => RETURN[i=size AND MatchBackward[p-1, i]];
ENDCASE => ERROR
ELSE IF MatchBound[i, start] THEN { matchStart ¬ selStart ¬ i; RETURN[TRUE] };
};
FOR i: INT DECREASING IN[i0..i1] UNTIL found OR interrupt­ DO
IF MatchBound[i, end] THEN
found ¬ MatchBackward[target.size, matchEnd ¬ selEnd ¬ i];
ENDLOOP;
};
ENDCASE => ERROR;
IF count>0 THEN {
subs ¬ NEW[SubsRep[count] ¬ [text: substr[matchStart, matchEnd-matchStart], subs: ]];
FOR i: NAT IN [0..count) DO subs[i] ¬ list.first; list ¬ list.rest; ENDLOOP
};
};
Replacement
NameEq: PROC [name: Name, fetch: FetchProc, start, len: INT] RETURNS [BOOL] ~ {
IF name.length#len THEN RETURN[FALSE];
FOR i: NAT IN[0..name.length) DO
IF name[i]#fetch[start+i] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
FindSub: PROC [subs: Subs, fetch: FetchProc, start, len: INT] RETURNS [Sub] ~ {
IF subs#NIL THEN FOR k: NAT IN[0..subs.size) DO
sub: Sub ~ subs[k];
IF NameEq[sub.name, fetch, start, len] THEN RETURN[sub];
ENDLOOP;
RETURN[NIL];
};
Replace: PUBLIC PROC [replace: ReplaceProc, substitute: SubstituteProc,
size: INT, start: INT ¬ 0, len: INT ¬ INT.LAST, fetch: FetchProc,
pattern: BOOL ¬ FALSE, subs: Subs ¬ NIL] ~ {
i0: INT ~ MIN[MAX[start, 0], size];
i1: INT ~ i0+MIN[MAX[len, 0], size-i0];
IF pattern THEN {
state: {null, quote, name} ¬ null;
s0: INT ¬ 0; -- start of literal string or name
pending: BOOL ¬ FALSE; -- literal string pending, starting at s0
Flush: PROC [s1: INT] ~ { IF pending THEN { replace[s0, s1-s0]; pending ¬ FALSE } };
FOR i: INT IN[i0..i1) DO
char: XCHAR ~ fetch[i];
SELECT state FROM
null => SELECT char FROM
xSingleQuote => { Flush[i]; state ¬ quote };
xLeftAngle => { Flush[i]; s0 ¬ i+1; state ¬ name };
ENDCASE => IF pending THEN NULL ELSE { s0 ¬ i; pending ¬ TRUE };
quote => { replace[i, 1]; state ¬ null };
name => SELECT char FROM
xRightAngle => {
sub: Sub ~ FindSub[subs, fetch, s0, i-s0];
IF sub#NIL THEN substitute[subs.text, sub.start, sub.len, s0, i-s0];
state ¬ null;
};
ENDCASE => NULL;
ENDCASE => ERROR;
ENDLOOP;
Flush[i1];
}
ELSE IF i1>i0 THEN replace[i0, i1-i0];
};
Rope operations
RopeLiteralSearch: PUBLIC PROC [direction: Direction,
target: ROPE, targetStart: INT ¬ 0, targetLen: INT ¬ INT.LAST,
object: ROPE, objectStart: INT ¬ 0, objectLen: INT ¬ INT.LAST,
case: BOOL ¬ TRUE] RETURNS [found: BOOL, matchStart, matchEnd: INT] ~ {
targetSize: INT ~ Rope.Size[target];
objectSize: INT ~ Rope.Size[object];
map: CharMap ~ CharMapFromCase[case];
targetHash: PROC [i: INT] RETURNS [BYTE] ~ { RETURN[ORD[map[Rope.Fetch[target, i]]]] };
objectHash: PROC [i: INT] RETURNS [BYTE] ~ { RETURN[ORD[map[Rope.Fetch[object, i]]]] };
match: PROC [objectStart, targetStart, len: INT] RETURNS [BOOL] ~ {
RETURN[Rope.Run[s1: target, pos1: targetStart, s2: object, pos2: objectStart,
case: case, len: targetLen]=targetLen];
};
targetStart ¬ MIN[MAX[0, targetStart], targetSize];
targetLen ¬ MIN[MAX[0, targetLen], targetSize-targetStart];
objectStart ¬ MIN[MAX[0, objectStart], objectSize];
objectLen ¬ MIN[MAX[0, objectLen], objectSize-objectStart];
RETURN LiteralSearch[direction: direction,
targetHash: targetHash, targetStart: targetStart, targetLen: targetLen,
objectHash: objectHash, objectStart: objectStart, objectLen: objectLen,
match: match];
};
TargetFromRope: PUBLIC PROC [rope: ROPE, start: INT ¬ 0, len: INT ¬ LAST[INT],
pattern: BOOL ¬ FALSE] RETURNS [Target] ~ {
fetch: FetchProc ~ { RETURN[Char.Widen[Rope.Fetch[rope, index]]] };
substr: SubstrProc ~ { RETURN[Rope.Substr[rope, start, len]] };
RETURN CreateTarget[size: Rope.Size[rope], start: start, len: len,
fetch: fetch, substr: substr, pattern: pattern];
};
RopeSearch: PUBLIC PROC [direction: Direction, target: Target,
rope: ROPE, start: INT ¬ 0, len: INT ¬ INT.LAST,
case: BOOL ¬ TRUE, word, def, all: BOOL ¬ FALSE, interrupt: REF BOOL ¬ NIL]
RETURNS [found: BOOL, matchStart, matchEnd, selStart, selEnd: INT, subs: Subs] ~ {
size: INT ~ Rope.Size[rope];
matchType: MatchTypeProc ~ {
char: CHAR ~ Rope.Fetch[rope, index];
RETURN[SELECT type FROM
any => TRUE,
alpha => CharOps.AlphaNumeric[char],
nonalpha => NOT CharOps.AlphaNumeric[char],
blank => CharOps.Blank[char],
nonblank => NOT CharOps.Blank[char],
ENDCASE => ERROR];
};
matchString: MatchStringProc ~ {
WITH text SELECT FROM
text: ROPE => RETURN[Rope.Run[s1: rope, pos1: index,
s2: text, pos2: start, case: case, len: len]=len];
ENDCASE => RETURN[FALSE];
};
matchBound: MatchBoundProc ~ {
RETURN[NOT (SELECT bound FROM
start => (all AND index#0) OR
((word OR def) AND index>0 AND matchType[index-1, alpha]),
end => (all AND index#size) OR
(word AND index<size AND matchType[index, alpha]) OR
(def AND NOT(index<size AND Rope.Fetch[rope, index]=':)),
ENDCASE => FALSE)];
};
substr: SubstrProc ~ { RETURN[Rope.Substr[rope, start, len]] };
RETURN Search[direction: direction, target: target,
size: size, start: start, len: len, substr: substr,
matchType: matchType, matchString: matchString,
matchBound: IF word OR def OR all THEN matchBound ELSE NIL,
interrupt: interrupt];
};
RopeReplace: PUBLIC PROC [rope: ROPE, start: INT ¬ 0, len: INT ¬ INT.LAST,
with: ROPE, pattern: BOOL ¬ FALSE, subs: Subs ¬ NIL] RETURNS [ROPE] ~ {
fetch: FetchProc ~ { RETURN[Char.Widen[Rope.Fetch[with, index]]] };
dest: ROPE ¬ rope;
destStart: INT ¬ start;
destLen: INT ¬ len;
put: PROC [source: ROPE, start, len: INT] ~ {
dest ¬ Rope.Replace[base: dest,
start: destStart, len: destLen, with: Rope.Substr[source, start, len]];
destStart ¬ destStart+len; destLen ¬ 0;
};
replace: ReplaceProc ~ { put[with, start, len] };
substitute: SubstituteProc ~ { put[NARROW[text], start, len] };
Replace[replace: replace, substitute: substitute,
size: Rope.Size[with], fetch: fetch, pattern: pattern, subs: subs];
IF destLen>0 THEN put[NIL, 0, 0];
RETURN [dest];
};
END.