<> <> <> <> <> <> <> <> <<>> DIRECTORY CardTab USING [Create, Ref, Update, UpdateAction], Rope USING [Cat, Fetch, FromChar, ROPE, Size], Rosary USING [CatSegments, Fetch, FromItem, MapRuns, ROSARY, RunActionType, Substr], TextLooks USING [Look, Looks, LooksAnd, LooksNot, LooksOr, MaxOffset, noLooks, RunAction, Runs]; TextLooksImpl: CEDAR PROGRAM IMPORTS CardTab, Rope, Rosary, TextLooks EXPORTS TextLooks = BEGIN OPEN TextLooks; ROPE: TYPE ~ Rope.ROPE; ROSARY: TYPE ~ Rosary.ROSARY; <> LooksToRope: PUBLIC PROC [looks: Looks] RETURNS [rope: ROPE] = { FOR lk: TextLooks.Look IN TextLooks.Look DO IF looks[lk] THEN rope _ Rope.Cat[rope, Rope.FromChar[lk]]; ENDLOOP; }; RopeToLooks: PUBLIC PROC [rope: ROPE] RETURNS [looks: Looks] = { looks _ noLooks; FOR i: INT IN [0..Rope.Size[rope]) DO char: CHAR _ Rope.Fetch[rope, i]; IF char IN Look THEN looks[char] _ TRUE; ENDLOOP; }; <> Size: PUBLIC PROC [runs: Runs] RETURNS [INT] ~ { RETURN [IF runs=NIL THEN 0 ELSE runs.size]; }; nilRosary: ROSARY ~ Rosary.FromItem[NIL, INT.LAST]; -- "infinite" rosary of NIL ItemNotNil: Rosary.RunActionType ~ { RETURN[quit: item#NIL] }; Concat: PUBLIC PROC [base, rest: Runs, baseLen, restLen: INT] RETURNS [Runs] ~ { nbase: ROSARY ~ IF base=NIL THEN nilRosary ELSE base; nrest: ROSARY ~ IF rest=NIL THEN nilRosary ELSE rest; r: ROSARY ~ Rosary.CatSegments[[nbase, 0, baseLen], [nrest, 0, restLen]]; RETURN[IF Rosary.MapRuns[[r], ItemNotNil] THEN r ELSE NIL]; }; Substr: PUBLIC PROC [base: Runs, start: INT, len: INT] RETURNS [Runs] ~ { IF base=NIL THEN RETURN[NIL] ELSE { r: ROSARY ~ Rosary.Substr[base, start, len]; RETURN[IF Rosary.MapRuns[[r], ItemNotNil] THEN r ELSE NIL]; }; }; Replace: PUBLIC PROC [base: Runs, start, len: INT, replace: Runs, baseSize, repSize: INT] RETURNS [new: Runs] ~ { d: ROSARY ~ IF base=NIL THEN nilRosary ELSE base; d1: INT ~ MAX[LONG[0], MIN[start, baseSize]]; d2: INT ~ d1+MAX[LONG[0], MIN[len, baseSize-d1]]; s: ROSARY ~ IF replace=NIL THEN nilRosary ELSE replace; r: ROSARY ~ Rosary.CatSegments[[d, 0, d1], [s, 0, repSize], [d, d2, baseSize-d2]]; RETURN[IF Rosary.MapRuns[[r], ItemNotNil] THEN r ELSE NIL]; }; ReplaceByRun: PUBLIC PROC [dest: Runs, start, len, runLen, destSize: INT, inherit: BOOL, looks: Looks] RETURNS [Runs] ~ { xlooks: Looks _ looks; IF inherit THEN { end: INT ~ start+len; SELECT TRUE FROM (start>0) => xlooks _ FetchLooks[dest, start-1]; (end xlooks _ FetchLooks[dest, end]; ENDCASE; }; RETURN[Replace[dest, start, len, CreateRun[runLen, xlooks], destSize, runLen]]; }; <> refFromLooksTab: CardTab.Ref ~ CardTab.Create[]; RefFromLooks: PROC [looks: Looks] RETURNS [ref: REF Looks _ NIL] ~ { refFromLooksAction: CardTab.UpdateAction ~ { <> IF found THEN { ref _ NARROW[val]; RETURN[op: none] } ELSE { ref _ NEW[Looks _ looks]; RETURN[op: store, new: ref] }; }; IF looks=noLooks THEN RETURN[NIL]; CardTab.Update[refFromLooksTab, LOOPHOLE[looks], refFromLooksAction]; IF ref^#looks THEN ERROR; }; CreateRun: PUBLIC PROC [len: INT, looks: Looks _ noLooks] RETURNS [Runs] ~ { refLooks: REF Looks ~ RefFromLooks[looks]; RETURN[Rosary.FromItem[refLooks, len]]; }; FetchLooks: PUBLIC PROC [runs: Runs, index: INT] RETURNS [Looks] = { <> IF runs#NIL THEN WITH Rosary.Fetch[runs, index] SELECT FROM refLooks: REF Looks => RETURN[refLooks^] ENDCASE; RETURN[noLooks]; }; CountRuns: PUBLIC PROC [runs: Runs, start: INT _ 0, len: INT _ MaxOffset] RETURNS [count: INT _ 0] ~ { countAction: Rosary.RunActionType ~ { count _ count+1 }; [] _ Rosary.MapRuns[[runs, start, len], countAction]; }; MapRuns: PUBLIC PROC [runs: Runs, action: RunAction, start: INT _ 0, len: INT _ MaxOffset] RETURNS [quit: BOOL] ~ { mapAction: Rosary.RunActionType ~ { refLooks: REF Looks ~ NARROW[item]; RETURN action[(IF refLooks=NIL THEN noLooks ELSE refLooks^), repeat]; }; RETURN Rosary.MapRuns[[runs, start, len], mapAction]; }; <> ModifyLooks: PUBLIC PROC [old, remove, add: Looks] RETURNS [Looks] ~ { RETURN [LooksOr[LooksAnd[old, LooksNot[remove]], add]]; }; ChangeLooks: PUBLIC PROC [runs: Runs, size: INT, remove, add: Looks, start, len: INT] RETURNS [Runs] ~ { d: ROSARY _ IF runs=NIL THEN nilRosary ELSE runs; changeLooksAction: Rosary.RunActionType ~ { <> oldLooks: Looks ~ IF item=NIL THEN noLooks ELSE NARROW[item, REF Looks]^; newLooks: Looks ~ ModifyLooks[oldLooks, remove, add]; replace: ROSARY ~ Rosary.FromItem[RefFromLooks[newLooks], repeat]; end: INT ~ start+repeat; d _ Rosary.CatSegments[[d, 0, start], [replace], [d, end, size-end]]; start _ end; }; [] _ Rosary.MapRuns[[d, start, len], changeLooksAction]; RETURN[IF Rosary.MapRuns[[d, 0, size], ItemNotNil] THEN d ELSE NIL]; }; END.