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[
BOOL ←
FALSE],
TRUE:
NEW[
BOOL ←
TRUE]];
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: ROSARY ← NIL;
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.