MODULE; IMPORT SortedTextTextTbl; IMPORT Text; IMPORT TextWr; IMPORT Wr, Thread; IMPORT Fmt; TextSubs
FROM Debug IMPORT S; CONST DebugLevel = 0;
<* FATAL Wr.Failure, Thread.Alerted *> REVEAL T = Public BRANDED OBJECT starts: SET OF CHAR; tbl: SortedTextTextTbl.T; OVERRIDES init := Init; add := Add; int := Int; apply := Apply; END; PROCEDUREInit (self: T): T = BEGIN self.tbl := NEW(SortedTextTextTbl.Default).init(); self.starts := SET OF CHAR{}; RETURN self; END Init; PROCEDUREAdd (self: T; original, replacement: TEXT) = BEGIN <* ASSERT Text.Length(original) > 0 *> self.starts := self.starts + SET OF CHAR{Text.GetChar(original, 0)}; EVAL self.tbl.put(original, replacement); END Add; PROCEDUREInt (self: T; original: TEXT; replacement: INTEGER) = BEGIN self.add(original, Fmt.Int(replacement)); END Int; PROCEDUREApply (self: T; src: TEXT): TEXT = VAR wr := TextWr.New(); c: CHAR; ind, pos, lastFlushed: INTEGER := 0; textLen := Text.Length(src); iter: SortedTextTextTbl.Iterator; original, replacement: TEXT; key, prefix: TEXT; PROCEDURE Flush() = BEGIN Wr.PutText(wr, Text.Sub(src, lastFlushed, pos - lastFlushed)); END Flush; BEGIN WHILE pos < textLen DO c := Text.GetChar(src, pos); IF c IN self.starts THEN (* S("analysing: " & Text.Sub(src, pos), DebugLevel); *) iter := self.tbl.iterateOrdered(); ind := pos; original := ""; REPEAT INC(ind); key := Text.Sub(src, pos, ind-pos); (* S("key = " & key, DebugLevel); *) prefix := Text.Sub(original, 0, ind-pos); CASE Text.Compare(prefix, key) OF | -1 => VAR lastOriginal := original; lastReplacement := replacement; BEGIN iter.seek(key); IF iter.next(original, replacement) THEN (* S("found: " & original, DebugLevel); *) ELSE original := Text.FromChar(LAST(CHAR)); END; prefix := Text.Sub(original, 0, ind-pos); IF NOT Text.Equal(prefix, key) THEN (* S("exiting", DebugLevel); *) original := lastOriginal; replacement := lastReplacement; ind := textLen; END; END; | 1 => ind := textLen; (* S("exiting", DebugLevel); *) | 0 => (* S("holding", DebugLevel); *) END; UNTIL ind = textLen; IF Text.Length(original) > 0 AND Text.Equal(Text.Sub(src, pos, Text.Length(original)), original) THEN Flush(); Wr.PutText(wr, replacement); INC(pos, Text.Length(original)); lastFlushed := pos; DEC(pos); END; END; INC(pos); END; Flush(); RETURN TextWr.ToText(wr); END Apply; BEGIN END TextSubs.