<> <> <> <<>> DIRECTORY Rope, TextFind, TextReplace; TextReplaceImpl: CEDAR PROGRAM IMPORTS Rope, TextFind EXPORTS TextReplace = { OPEN TextReplace; SyntaxError: PUBLIC ERROR = CODE; NoMapping: PUBLIC SIGNAL [rm: RopeMap, from: ROPE] RETURNS [to: ROPE] = CODE; Apply: PUBLIC PROC [s: RopeMap, r: ROPE] RETURNS [mapped: ROPE] = {mapped _ s.Map[s.data, r]}; id: PUBLIC RopeMap _ NEW [RopeMapRep _ [Id, NIL]]; Id: PROC [data: REF ANY, name: ROPE] RETURNS [value: ROPE] = {value _ name}; addBrackets: PUBLIC RopeMap _ NEW [RopeMapRep _ [AddBrackets, NIL]]; AddBrackets: PROC [data: REF ANY, name: ROPE] RETURNS [value: ROPE] = {value _ Rope.Cat["<", name, ">"]}; Constant: PUBLIC PROC [result: ROPE] RETURNS [rm: RopeMap] = { rm _ NEW [RopeMapRep _ [MapToConstant, result]]; }; MapToConstant: PROC [data: REF ANY, arg: ROPE] RETURNS [result: ROPE] = { constant: ROPE = NARROW[data]; result _ constant; }; Catted: TYPE = REF CattedRep; CattedRep: TYPE = RECORD [first, second: RopeMap]; Cat: PUBLIC PROC [first, second: RopeMap] RETURNS [catted: RopeMap] = { catted _ NEW [RopeMapRep _ [ MapCatted, NEW [CattedRep _ [first, second]] ]]; }; MapCatted: PROC [data: REF ANY, name: ROPE] RETURNS [value: ROPE] = { c: Catted _ NARROW[data]; mid: ROPE _ c.first.Map[c.first.data, name]; value _ c.second.Map[c.second.data, mid]; }; Layered: TYPE = REF LayeredRep; LayeredRep: TYPE = RECORD [first, later: RopeMap]; Layer: PUBLIC PROC [first, later: RopeMap] RETURNS [layered: RopeMap] = { layered _ NEW [RopeMapRep _ [ MapLayered, NEW [LayeredRep _ [first, later]] ]]; }; MapLayered: PROC [data: REF ANY, name: ROPE] RETURNS [value: ROPE] = { l: Layered _ NARROW[data]; value _ l.first.Map[l.first.data, name]; IF value = NIL THEN value _ l.later.Map[l.later.data, name]; }; ProcessedPairList: TYPE = LIST OF ProcessedPair; ProcessedPair: TYPE = RECORD [finder: TextFind.Finder, replace: ROPE, literal: BOOL]; RopeMapFromPairs: PUBLIC PROC [pl: PairList] RETURNS [s: RopeMap] = { ppl: ProcessedPairList _ Process[pl]; s _ NEW [RopeMapRep _ [MapPairs, ppl]]; }; Process: PROC [pl: PairList] RETURNS [ppl: ProcessedPairList] = { IF pl = NIL THEN RETURN [NIL]; ppl _ CONS[ [ finder: TextFind.CreateFromRope[ pattern: pl.first.match, literal: pl.first.literal, addBounds: pl.first.addBounds], replace: pl.first.replace, literal: pl.first.literal], Process[pl.rest]]; }; MapPairs: PROC [data: REF ANY, name: ROPE] RETURNS [value: ROPE] = { ppl: ProcessedPairList _ NARROW[data]; pp: ProcessedPair; MapFinder: PROC [data: REF ANY, name: ROPE] RETURNS [value: ROPE] = { start, after: INT; [at: start, atEnd: after] _ TextFind.NameLoc[pp.finder, name]; value _ value.Substr[start: start, len: after - start]; }; s: RopeMap; TRUSTED {s _ NEW [RopeMapRep _ [MapFinder, NIL]]}; s _ Nest[s]; value _ name; FOR ppl _ ppl, ppl.rest WHILE ppl # NIL DO start: INT _ 0; pp _ ppl.first; WHILE start < value.Length[] DO at, atEnd, before, after: INT; found: BOOL; [found, at, atEnd, before, after] _ TextFind.SearchRope[finder: pp.finder, rope: value, start: start]; IF found THEN { newPiece: ROPE _ IF pp.literal THEN pp.replace ELSE Apply[s, pp.replace]; value _ value.Substr[start: 0, len: at].Cat[newPiece, value.Substr[start: atEnd]]; start _ at + newPiece.Length[]; } ELSE EXIT; ENDLOOP; ENDLOOP; }; FinderMapping: TYPE = REF FinderMappingRep; FinderMappingRep: TYPE = RECORD [ finder: TextFind.Finder, lastMatchedRope: ROPE]; MapNamedSubfieldToMatch: PUBLIC PROC [finder: TextFind.Finder, lastMatchedRope: ROPE] RETURNS [rm: RopeMap] = { rm _ NEW [RopeMapRep _ [ MapNamedSubfield, NEW [FinderMappingRep _ [finder, lastMatchedRope]] ]]; }; MapNamedSubfield: PROC [data: REF ANY, name: ROPE] RETURNS [value: ROPE] = { fm: FinderMapping = NARROW[data]; start, after: INT; [at: start, atEnd: after] _ TextFind.NameLoc[fm.finder, name]; IF start = 0 AND after = 0 THEN RETURN [NIL]; value _ fm.lastMatchedRope.Substr[start: start, len: after - start]; }; MatchToTemplate: TYPE = REF MatchToTemplatePrivate; MatchToTemplatePrivate: TYPE = RECORD [ finder: TextFind.Finder, template: ROPE]; MapByMatchToTemplate: PUBLIC PROC [finder: TextFind.Finder, template: ROPE] RETURNS [rm: RopeMap] = { mtt: MatchToTemplate = NEW [MatchToTemplatePrivate _ [finder, template]]; rm _ NEW [RopeMapRep _ [MapMatchToTemplate, mtt]]; }; MapMatchToTemplate: PROC [data: REF ANY, arg: ROPE] RETURNS [result: ROPE] = { mtt: MatchToTemplate = NARROW[data]; found: BOOL = TextFind.SearchRope[mtt.finder, arg].found; IF NOT found THEN RETURN [NIL]; result _ Apply[Nest[MapNamedSubfieldToMatch[mtt.finder, arg]], mtt.template]; }; Nest: PUBLIC PROC [rm: RopeMap] RETURNS [nested: RopeMap] = {nested _ NEW [RopeMapRep _ [MapNested, rm]]}; MapNested: PROC [data: REF ANY, old: ROPE] RETURNS [new: ROPE] = { rm: RopeMap _ NARROW[data]; i: INT _ 0; oldLen: INT _ old.Length[]; new _ NIL; WHILE i < oldLen DO c: CHAR _ old.Fetch[i]; SELECT c FROM '' => { IF i+1 = oldLen THEN SyntaxError[]; new _ new.Concat[Rope.FromChar[old.Fetch[i+1]]]; i _ i + 2}; '< => {j: INT _ i+1; name, value: ROPE; DO IF j >= oldLen THEN SyntaxError[]; SELECT old.Fetch[j] FROM '> => EXIT; '<, '' => SyntaxError[]; --No metachars in field name ENDCASE; j _ j + 1; ENDLOOP; name _ old.Substr[start: i+1, len: j - i - 1]; value _ rm.Map[rm.data, name]; IF value = NIL THEN value _ NoMapping[rm, name]; new _ new.Concat[value]; i _ j + 1}; ENDCASE => { new _ new.Concat[Rope.FromChar[c]]; i _ i + 1}; ENDLOOP; }; MatchAndSubstitute: PUBLIC PROC [pattern, against, template: ROPE] RETURNS [new: ROPE] = { f: TextFind.Finder = TextFind.CreateFromRope[pattern]; found: BOOL = TextFind.SearchRope[f, against].found; IF NOT found THEN RETURN [template]; new _ Apply[Nest[MapNamedSubfieldToMatch[f, against]], template]; }; }.