ImmutableTiogaImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Doug Wyatt, September 16, 1986 5:00:45 pm PDT
DIRECTORY
ImmutableTioga,
Rope,
Rosary,
TextLooks;
ImmutableTiogaImpl: CEDAR PROGRAM
IMPORTS Rope, Rosary, TextLooks
EXPORTS ImmutableTioga
~ BEGIN OPEN ImmutableTioga;
NChildren: PUBLIC PROC [node: Node] RETURNS [INT] ~ {
children: ROSARY ~ node.children;
RETURN [IF children=NIL THEN 0 ELSE children.size];
};
NthChild: PUBLIC PROC [node: Node, i: INT] RETURNS [Node] ~ {
WITH Rosary.Fetch[node.children, i] SELECT FROM
child: Node => RETURN[child];
ENDCASE => RETURN[NIL];
};
Index: TYPE ~ REF IndexRep;
IndexRep: TYPE ~ RECORD [depth: NAT, seq: SEQUENCE size: NAT OF INT];
Path: TYPE ~ REF PathRep;
PathRep: TYPE ~ RECORD [depth: NAT, seq: SEQUENCE size: NAT OF Node];
IndexedNode: PROC [root: Node, index: Index] RETURNS [Node] ~ {
node: Node ← root;
FOR k: NAT IN [0..index.depth) DO
node ← NthChild[node, index[k]];
ENDLOOP;
RETURN[node];
};
SetPath: PROC [root: Node, index: Index, path: Path] ~ {
depth: NAT ~ index.depth;
node: Node ← root;
FOR k: NAT IN [0..depth) DO
path[k] ← node;
node ← NthChild[node, index[k]];
ENDLOOP;
path[depth] ← node;
path.depth ← depth+1;
};
PropListGet: PUBLIC PROC [propList: PropList, name: ATOM] RETURNS [value: REF] ~ {
FOR list: PropList ← propList, list.rest UNTIL list=NIL DO
prop: Prop ~ list.first;
IF prop.name=name THEN RETURN[prop.value];
ENDLOOP;
RETURN[NIL];
};
PropListPut: PUBLIC PROC [propList: PropList, name: ATOM, value: REF] RETURNS [PropList] ~ {
IF PropListGet[propList, name]=value THEN RETURN [propList]
ELSE {
rem: PropList ~ PropListRem[propList, name];
IF value=NIL THEN RETURN [rem]
ELSE RETURN [CONS[NEW[PropRep ← [name, value]], rem]];
};
};
PropListRem: PROC [propList: PropList, name: ATOM] RETURNS [PropList] ~ {
IF propList=NIL THEN RETURN [NIL]
ELSE IF propList.first.name=name THEN RETURN [propList.rest]
ELSE {
remRest: PropList ~ PropListRem[propList.rest, name];
IF propList.rest=remRest THEN RETURN [propList]
ELSE RETURN [CONS[propList.first, remRest]];
};
};
aComment: ATOM ~ $Comment;
aFormat: ATOM ~ $Format;
aStyleDef: ATOM ~ $StyleDef;
aPrefix: ATOM ~ $Prefix;
aPostfix: ATOM ~ $Postfix;
aArtwork: ATOM ~ $Artwork;
refBool: ARRAY BOOL OF REF BOOL ~ [FALSE: NEW[BOOLFALSE], TRUE: NEW[BOOLTRUE]];
PropsGet: PUBLIC PROC [props: Props, name: ATOM] RETURNS [value: REF] ~ {
SELECT name FROM
aComment => value ← refBool[props.comment];
aFormat => value ← props.formatName;
ENDCASE => value ← PropListGet[props.propList, name];
};
PropsPut: PUBLIC PROC [props: Props, name: ATOM, value: REF] RETURNS [Props] ~ {
propList: PropList ← props.propList;
format: ATOM ← props.formatName;
comment: BOOL ← props.comment;
SELECT name FROM
aComment => comment ← WITH value SELECT FROM x: REF BOOL => x^, ENDCASE => FALSE;
aFormat => format ← WITH value SELECT FROM x: ATOM => x, ENDCASE => NIL;
ENDCASE => propList ← PropListPut[props.propList, name, value];
IF propList#props.propList OR format#props.formatName OR comment#props.comment THEN {
new: Props ~ NEW [PropsRep ← [propList: propList, formatName: format, comment: comment, hasStyleDef: props.hasStyleDef, hasPrefix: props.hasPrefix, hasPostfix: props.hasPostfix, hasArtwork: props.hasArtwork]];
IF new.propList#props.propList THEN {
hasValue: BOOL ~ (value#NIL);
SELECT name FROM
aStyleDef => new.hasStyleDef ← hasValue;
aPrefix => new.hasPrefix ← hasValue;
aPostfix => new.hasPostfix ← hasValue;
aArtwork => new.hasArtwork ← hasValue;
ENDCASE;
};
RETURN[new];
}
ELSE RETURN [props]; -- nothing changed
};
NewText: PUBLIC PROC [props: Props ← NIL] RETURNS [Text] ~ {
IF props=NIL THEN props ← NEW [PropsRep ← []];
RETURN [NEW[TextRep ← [rope: NIL, runs: NIL, charSets: NIL, charProps: NIL, props: props]]];
};
TextFromRope: PUBLIC PROC [rope: ROPE, looks: Looks ← noLooks] RETURNS [Text] ~ {
text: Text ~ NewText[];
text.rope ← rope;
text.runs ← TextLooks.CreateRun[Rope.Size[rope], looks];
RETURN [text];
};
Size: PUBLIC PROC [text: Text] RETURNS [INT] ~ {
RETURN[IF text=NIL THEN 0 ELSE Rope.InlineSize[text.rope]];
};
FetchChar: PUBLIC PROC [text: Text, index: INT] RETURNS [char: XChar ← [0, 0]] ~ {
char.code ← ORD[Rope.Fetch[text.rope, index]];
IF text.charSets#NIL THEN WITH Rosary.Fetch[text.charSets, index] SELECT FROM
refCharSet: REF CharSet => char.set ← refCharSet^;
ENDCASE;
};
FetchLooks: PUBLIC PROC [text: Text, index: INT] RETURNS [Looks] = {
RETURN [TextLooks.FetchLooks[text.runs, index]];
};
Fetch: PUBLIC PROC [text: Text, index: INT] RETURNS [char: XChar, looks: Looks] ~ {
char ← FetchChar[text, index];
looks ← FetchLooks[text, index];
};
GetFormat: PUBLIC PROC [text: Text] RETURNS [ATOM] ~ {
RETURN[text.props.formatName];
};
GetComment: PUBLIC PROC [text: Text] RETURNS [BOOL] ~ {
RETURN[text.props.comment];
};
GetProp: PUBLIC PROC [text: Text, name: ATOM] RETURNS [REF] ~ {
RETURN[PropsGet[text.props, name]];
};
GetCharProp: PUBLIC PROC [text: Text, index: INT, name: ATOM] RETURNS [REF] ~ {
RETURN[PropListGet[GetCharPropList[text, index], name]];
};
GetCharPropList: PUBLIC PROC [text: Text, index: INT] RETURNS [PropList] ~ {
RETURN[NARROW[Rosary.Fetch[text.charProps, index]]];
};
rosaryOfNil: ROSARY ~ Rosary.FromItem[NIL, maxLen];
RosaryReplace: PROC [dest: ROSARY, destSize: INT, destStart: INT, destLen: INT, source: ROSARY, sourceStart: INT, sourceLen: INT] RETURNS [rosary: ROSARY] ~ {
notNil: Rosary.RunActionType ~ { quit ← item#NIL };
d: ROSARY ~ IF dest=NIL THEN rosaryOfNil ELSE dest;
d0: INT ~ 0;
d1: INT ~ destStart;
d2: INT ~ d1+destLen;
d3: INT ~ destSize;
s: ROSARY ~ IF source=NIL THEN rosaryOfNil ELSE source;
s0: INT ~ sourceStart;
s1: INT ~ s0+sourceLen;
rosary ← Rosary.CatSegments[[d, d0, d1-d0], [s, s0, s1-s0], [d, d2, d3-d2]];
IF NOT Rosary.MapRuns[[rosary], notNil] THEN rosary ← NIL;
};
RosaryMapRuns: PROC [rosary: ROSARY, start, len: INT, action: Rosary.RunActionType] ~ {
IF rosary=NIL THEN [] ← action[NIL, len]
ELSE [] ← Rosary.MapRuns[[rosary, start, len], action];
};
DeleteText: PUBLIC PROC [dest: Text, destStart: INT ← 0, destLen: INT ← maxLen] RETURNS [Text] ~ {
destSize: INT ~ Size[dest];
destStart ← MIN[MAX[0, destStart], destSize];
destLen ← MIN[MAX[0, destLen], destSize-destStart];
IF destLen=0 THEN RETURN[dest] -- delete nothing
ELSE {
new: Text ~ NEW[TextRep ← [rope: NIL, runs: NIL, charSets: NIL, charProps: NIL, props: NIL]];
newSize: INT ~ destSize-destLen;
IF newSize#0 THEN {
SELECT TRUE FROM
(destStart=0) => { -- delete from start
new.rope ← Rope.Substr[dest.rope, destLen, newSize];
IF dest.runs#NIL THEN new.runs ← TextLooks.Substr[dest.runs, destLen, newSize];
};
(destStart+destLen=destSize) => { -- delete from end
new.rope ← Rope.Substr[dest.rope, 0, newSize];
IF dest.runs#NIL THEN new.runs ← TextLooks.Substr[dest.runs, 0, newSize];
};
ENDCASE => {
new.rope ← Rope.Replace[base: dest.rope, start: destStart, len: destLen, with: NIL];
IF dest.runs#NIL THEN new.runs ← TextLooks.Replace[base: dest.runs, start: destStart, len: destLen, replace: NIL, baseSize: destSize, repSize: 0];
};
IF dest.charSets#NIL THEN new.charSets ← RosaryReplace[dest: dest.charSets, destSize: destSize, destStart: destStart, destLen: destLen, source: NIL, sourceStart: 0, sourceLen: 0];
IF dest.charProps#NIL THEN new.charProps ← RosaryReplace[dest: dest.charProps, destSize: destSize, destStart: destStart, destLen: destLen, source: NIL, sourceStart: 0, sourceLen: 0];
};
new.props ← dest.props;
RETURN[new];
};
};
ReplaceText: PUBLIC PROC [dest: Text, destStart: INT ← 0, destLen: INT ← maxLen, source: Text ← NIL, sourceStart: INT ← 0, sourceLen: INT ← maxLen] RETURNS [Text] ~ {
sourceSize: INT ~ Size[source];
sourceStart ← MIN[MAX[0, sourceStart], sourceSize];
sourceLen ← MIN[MAX[0, sourceLen], sourceSize-sourceStart];
IF sourceLen=0 THEN RETURN DeleteText[dest, destStart, destLen]
ELSE {
new: Text ~ NEW[TextRep ← [rope: NIL, runs: NIL, charSets: NIL, charProps: NIL, props: NIL]];
replaceRope: ROPE ~ Rope.Substr[source.rope, sourceStart, sourceLen];
replaceRuns: Runs ~ TextLooks.Substr[source.runs, sourceStart, sourceLen];
destSize: INT ~ Size[dest];
destStart ← MIN[MAX[0, destStart], destSize];
destLen ← MIN[MAX[0, destLen], destSize-destStart];
SELECT TRUE FROM
(destLen=0 AND destStart=0) => { -- insert at start
new.rope ← Rope.Concat[replaceRope, dest.rope];
new.runs ← TextLooks.Concat[replaceRuns, dest.runs, sourceLen, destSize];
};
(destLen=0 AND destStart=destSize) => { -- insert at end
new.rope ← Rope.Concat[dest.rope, replaceRope];
new.runs ← TextLooks.Concat[dest.runs, replaceRuns, destSize, sourceLen];
};
ENDCASE => {
new.rope ← Rope.Replace[base: dest.rope, start: destStart, len: destLen, with: replaceRope];
new.runs ← TextLooks.Replace[base: dest.runs, start: destStart, len: destLen, replace: replaceRuns, baseSize: destSize, repSize: sourceLen];
};
IF dest.charSets#NIL OR source.charSets#NIL THEN new.charSets ← RosaryReplace[dest: dest.charSets, destSize: destSize, destStart: destStart, destLen: destLen, source: source.charSets, sourceStart: sourceStart, sourceLen: sourceLen];
IF dest.charProps#NIL OR source.charProps#NIL THEN new.charProps ← RosaryReplace[dest: dest.charProps, destSize: destSize, destStart: destStart, destLen: destLen, source: source.charProps, sourceStart: sourceStart, sourceLen: sourceLen];
new.props ← dest.props;
RETURN[new];
};
};
CharSetsReplaceByRun: PROC [destCharSets: ROSARY, destSize: INT, destStart: INT, destLen: INT, charSet: CharSet, runSize: INT] RETURNS [ROSARY] ~ {
sourceCharSets: ROSARYNIL;
IF charSet#0 THEN {
item: REF CharSet ← NIL;
IF destCharSets#NIL THEN {
Try to find a matching item in destCharSets adjacent to the replacement.
Try: TYPE ~ {before, after};
tries: ARRAY Try OF INT ← [before: destStart-1, after: destStart+destLen];
FOR try: Try IN Try WHILE item=NIL DO
i: INT ~ tries[try];
IF i IN [0..destSize) THEN WITH Rosary.Fetch[destCharSets, i] SELECT FROM
destItem: REF CharSet => IF destItem^=charSet THEN item ← destItem;
ENDCASE;
ENDLOOP;
};
IF item=NIL THEN item ← NEW[CharSet ← charSet];
sourceCharSets ← Rosary.FromItem[item, runSize];
};
RETURN[RosaryReplace[dest: destCharSets, destSize: destSize, destStart: destStart, destLen: destLen, source: sourceCharSets, sourceStart: 0, sourceLen: runSize]];
};
ReplaceByRope: PUBLIC PROC [dest: Text, destStart: INT, destLen: INT, rope: ROPE, inherit: BOOL, looks: Looks, charSet: CharSet] RETURNS [Text] ~ {
ropeSize: INT ~ Rope.Size[rope];
IF ropeSize=0 THEN RETURN DeleteText[dest, destStart, destLen]
ELSE {
new: Text ~ NEW[TextRep ← [rope: NIL, runs: NIL, charSets: NIL, charProps: NIL, props: NIL]];
destSize: INT ~ Size[dest];
destStart ← MIN[MAX[0, destStart], destSize];
destLen ← MIN[MAX[0, destLen], destSize-destStart];
new.rope ← Rope.Replace[base: dest.rope, start: destStart, len: destLen, with: rope];
new.runs ← TextLooks.ReplaceByRun[dest: dest.runs, start: destStart, len: destLen, runLen: ropeSize, destSize: destSize, inherit: inherit, looks: looks];
IF dest.charSets#NIL OR charSet#0 THEN new.charSets ← CharSetsReplaceByRun[destCharSets: dest.charSets, destSize: destSize, destStart: destStart, destLen: destLen, charSet: charSet, runSize: ropeSize];
IF dest.charProps#NIL THEN new.charProps ← RosaryReplace[dest: dest.charProps, destSize: destSize, destStart: destStart, destLen: destLen, source: NIL, sourceStart: 0, sourceLen: ropeSize];
new.props ← dest.props;
RETURN[new];
};
};
SetProp: PUBLIC PROC [text: Text, name: ATOM, value: REF] RETURNS [Text] ~ {
props: Props ~ PropsPut[text.props, name, value];
IF text.props=props THEN RETURN [text]
ELSE RETURN [NEW[TextRep ← [rope: text.rope, runs: text.runs, charSets: text.charSets, charProps: text.charProps, props: props]]];
};
SetComment: PUBLIC PROC [text: Text, comment: BOOL] RETURNS [Text] ~ {
IF text.props.comment=comment THEN RETURN [text]
ELSE RETURN SetProp[text, aComment, refBool[comment]];
};
SetFormat: PUBLIC PROC [text: Text, formatName: ATOM] RETURNS [Text] ~ {
IF text.props.formatName=formatName THEN RETURN [text]
ELSE RETURN SetProp[text, aFormat, formatName];
};
SetCharProp: PUBLIC PROC [text: Text, start: INT ← 0, len: INT ← maxLen, name: ATOM, value: REF] RETURNS [Text] ~ {
size: INT ~ Size[text];
start ← MIN[MAX[0, start], size];
len ← MIN[MAX[0, len], size-start];
IF len=0 THEN RETURN [text]
ELSE {
p: PROC[q: PROC[REF, INT]] ~ {
action: Rosary.RunActionType ~ { q[PropListPut[NARROW[item], name, value], repeat] };
RosaryMapRuns[text.charProps, start, len, action];
};
replaceCharProps: ROSARY ~ Rosary.FromProcProc[p];
newCharProps: ROSARY ~ RosaryReplace[dest: text.charProps, destSize: size, destStart: start, destLen: len, source: replaceCharProps, sourceStart: 0, sourceLen: len];
RETURN [NEW[TextRep ← [rope: text.rope, runs: text.runs, charSets: text.charSets, charProps: newCharProps, props: text.props]]];
};
};
SetCharPropList: PUBLIC PROC [text: Text, start: INT ← 0, len: INT ← maxLen, propList: PropList] RETURNS [Text] ~ {
size: INT ~ Size[text];
start ← MIN[MAX[0, start], size];
len ← MIN[MAX[0, len], size-start];
IF len=0 THEN RETURN [text]
ELSE {
replaceCharProps: ROSARY ~ Rosary.FromItem[propList, len];
newCharProps: ROSARY ~ RosaryReplace[dest: text.charProps, destSize: size, destStart: start, destLen: len, source: replaceCharProps, sourceStart: 0, sourceLen: len];
RETURN [NEW[TextRep ← [rope: text.rope, runs: text.runs, charSets: text.charSets, charProps: newCharProps, props: text.props]]];
};
};
ChangeLooks: PUBLIC PROC [text: Text, start: INT ← 0, len: INT ← maxLen, remove, add: Looks] RETURNS [Text] ~ {
size: INT ~ Size[text];
start ← MIN[MAX[0, start], size];
len ← MIN[MAX[0, len], size-start];
IF len=0 THEN RETURN [text]
ELSE {
oldRuns: Runs ~ text.runs;
newRuns: Runs ~ TextLooks.ChangeLooks[oldRuns, size, remove, add, start, len];
IF newRuns=oldRuns THEN RETURN [text] -- no change
ELSE RETURN [NEW[TextRep ← [rope: text.rope, runs: newRuns, charSets: text.charSets, charProps: text.charProps, props: text.props]]];
};
};
END.