TranslateToOpenProperties:
PROC [reader: TiogaAccess.Reader, writer: TiogaAccess.Writer] ~ {
t: TiogaAccess.TiogaChar ← [charSet: 0, char: 0C, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL];
node: TiogaAccess.TiogaChar ← [charSet: 0, char: 15C, looks: ALL[FALSE], format: $code, comment: FALSE, endOfNode: TRUE, deltaLevel: 0, propList: NIL];
nest: TiogaAccess.TiogaChar ← [charSet: 0, char: 15C, looks: ALL[FALSE], format: $code, comment: FALSE, endOfNode: TRUE, deltaLevel: 1, propList: NIL];
outc: TiogaAccess.TiogaChar ← [charSet: 0, char: 0C, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL];
PutRope:
PROC [rope:
ROPE] ~ {
FOR i:
INT
IN [0..Rope.Size[rope])
DO
outc.char ← Rope.Fetch[rope, i];
TiogaAccess.Put[writer, outc];
ENDLOOP;
};
PutOctal:
PROC [b: [0..256)] ~ {
outc.char ← '0+b/64;
TiogaAccess.Put[writer, outc];
outc.char ← '0+(b/8) MOD 8;
TiogaAccess.Put[writer, outc];
outc.char ← '0+b MOD 8;
TiogaAccess.Put[writer, outc];
};
PutPropName:
PROC [atom:
ATOM] ~ {
save: TiogaAccess.Looks ← outc.looks;
outc.looks['n] ← TRUE;
PutRope[Atom.GetPName[atom]];
outc.looks ← save;
PutRope[": "];
};
PutKey:
PROC [rope:
ROPE] ~ {
save: TiogaAccess.Looks ← outc.looks;
outc.looks['k] ← TRUE;
outc.looks['b] ← TRUE;
PutRope[rope];
outc.looks ← save;
};
PutProperties:
PROC [propList: Atom.PropList] ~ {
FOR each: Atom.PropList ← propList, each.rest
UNTIL each =
NIL
DO
key: ATOM ~ NARROW[each.first.key];
PutProperty[key, each.first.val];
ENDLOOP;
};
PutProperty:
PROC [key:
ATOM, value:
REF] ~ {
IF value #
NIL
THEN {
rope: ROPE ~ TiogaAccess.GetExternalProp[key, value];
PutPropName[key];
IF rope =
NIL
OR NodePropsExtras.Is[key, $ClientOnly]
THEN {
save: TiogaAccess.Looks ← outc.looks;
outc.looks['c] ← TRUE;
PutRope["(opaque)"];
outc.looks ← save;
node.propList ← CONS[NEW[Atom.DottedPairNode ← [key: $OpaqueValue, val: value]], node.propList];
TiogaAccess.Put[writer, node];
node.propList ← node.propList.rest;
}
ELSE {
PutRope[rope];
TiogaAccess.Put[writer, node];
};
};
};
TranslateNodeContents:
PROC ~ {
UNTIL t.endOfNode
DO
looks: TiogaAccess.Looks ~ t.looks;
propList: Atom.PropList ~ t.propList;
IF t.charSet = 0
AND t.char
IN [' ..'~]
THEN {
Emit a run of printable ascii characters, all with the same looks and properties
PutPropName[$Text];
WHILE
NOT t.endOfNode
AND t.charSet = 0
AND t.char
IN [' ..'~]
AND t.looks=looks
AND t.propList = propList
DO
save: TiogaAccess.Looks ← outc.looks;
outc.char ← t.char;
outc.looks['o] ← TRUE;
TiogaAccess.Put[writer, outc];
outc.looks ← save;
t ← TiogaAccess.Get[reader];
ENDLOOP;
}
ELSE {
Emit a single XC character
PutPropName[$Char];
PutRope["("];
PutOctal[t.charSet];
PutRope["|"];
PutOctal[ORD[t.char]];
PutRope[")"];
t ← TiogaAccess.Get[reader];
};
TiogaAccess.Put[writer, node];
IF looks #
ALL[
FALSE]
THEN {
Emit the looks that apply to the previous text or character
TiogaAccess.Nest[writer, 1];
PutKey["LOOKS"];
PutRope["("];
FOR c:
CHAR
IN TiogaAccess.Look
DO
IF looks[c] THEN {outc.char ← c; TiogaAccess.Put[writer, outc]};
ENDLOOP;
PutRope[")"];
TiogaAccess.Put[writer, node];
TiogaAccess.Nest[writer, -1];
};
IF propList #
NIL
THEN {
Emit the properties that apply to the previous text or character
TiogaAccess.Nest[writer, 1];
PutProperties[propList];
TiogaAccess.Nest[writer, -1];
};
ENDLOOP;
};
UNTIL TiogaAccess.EndOf[reader]
DO
t ← TiogaAccess.Get[reader];
PutKey["BEGIN NODE"]; TiogaAccess.Put[writer, nest];
IF
NOT t.endOfNode
THEN {
PutKey["CONTENTS"]; TiogaAccess.Put[writer, nest];
TranslateNodeContents[];
TiogaAccess.Nest[writer, -1];
};
PutKey["PROPERTIES"]; TiogaAccess.Put[writer, nest];
PutProperty[$Comment, IF t.comment THEN NodeProps.true ELSE NodeProps.false];
PutProperty[$Format, t.format];
PutProperties[t.propList];
TiogaAccess.Nest[writer, -1];
PutKey["END NODE"];
TiogaAccess.Put[writer, node];
TiogaAccess.Nest[writer, t.deltaLevel-1];
ENDLOOP;
};
TranslateFromOpenProperties:
PROC [reader: TiogaAccess.Reader, writer: TiogaAccess.Writer] ~ {
Must: PROC [assertion: BOOL] ~ {IF NOT assertion THEN Cant[]};
break: TiogaAccess.TiogaChar ← [charSet: 0, char: 0C, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL];
Match:
PROC [rope:
ROPE]
RETURNS [
BOOL] ~ {
targetLen: INT ~ Rope.Size[rope];
input: ROPE ~ TiogaAccess.PeekRope[reader];
matchLen: INT ~ Rope.Run[s1: rope, s2: input, case: TRUE];
IF matchLen=targetLen
THEN {
FOR i: INT IN [0..targetLen) DO [] ← TiogaAccess.Get[reader] ENDLOOP;
RETURN [TRUE];
};
RETURN [FALSE];
};
level: INT ← 0;
MatchBreak:
PROC
RETURNS [
BOOL] ~ {
IF TiogaAccess.Peek[reader].endOfNode
THEN {
break ← TiogaAccess.Get[reader];
level ← level + break.deltaLevel;
RETURN [TRUE];
};
RETURN [FALSE];
};
MustNest: PROC ~ {Must[MatchBreak[] AND break.deltaLevel = 1]};
GetOctal3:
PROC
RETURNS [[0..256)] ~ {
v: CARDINAL ← 0;
FOR i:
NAT
IN [0..3)
DO
t: TiogaAccess.TiogaChar ← TiogaAccess.Get[reader];
Must[t.char IN ['0..'8) AND t.charSet = 0];
v ← v*8 + (t.char-'0);
ENDLOOP;
Must[v IN [0..256)];
RETURN [v];
};
RopeToEnd:
PROC
RETURNS [
ROPE] ~ {
rope: ROPE ← TiogaAccess.PeekRope[reader];
FOR i: INT IN [0..Rope.Size[rope]) DO [] ← TiogaAccess.Get[reader] ENDLOOP;
RETURN [rope];
};
ParseProp:
PROC
RETURNS [Atom.DottedPairNode] ~ {
keyText: REF TEXT ~ RefText.ObtainScratch[100];
t: TiogaAccess.TiogaChar ← TiogaAccess.Get[reader];
key: ATOM ← NIL;
rope: ROPE ← NIL;
value: REF;
WHILE t.charSet = 0
AND RopeEdit.AlphaNumericChar[t.char]
DO
Must[keyText.length < keyText.maxLength];
keyText[keyText.length] ← t.char;
keyText.length ← keyText.length + 1;
t ← TiogaAccess.Get[reader];
ENDLOOP;
Must[keyText.length > 0];
Must[t.charSet = 0 AND t.char = ':];
Must[Match[" "]];
rope ← RopeToEnd[];
Must[MatchBreak[]];
key ← Atom.MakeAtomFromRefText[keyText];
value ← Atom.GetPropFromList[break.propList, $OpaqueValue];
IF value = NIL THEN value ← TiogaAccess.GetInternalProp[key, rope];
RefText.ReleaseScratch[keyText];
RETURN [[key: key, val: value]];
};
UNTIL TiogaAccess.EndOf[reader]
DO
IF Match["BEGIN NODE"]
THEN {
node: TiogaAccess.TiogaChar ← [charSet: 0, char: 15C, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: TRUE, deltaLevel: 0, propList: NIL];
level ← 0;
MustNest[];
IF Match["CONTENTS"]
THEN {
MustNest[];
WHILE level >= 2
DO
outc: TiogaAccess.TiogaChar ← [charSet: 0, char: 0C, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL];
rope: ROPE ← NIL;
Must[level = 2];
IF Match["Text: "]
THEN rope ← RopeToEnd[]
ELSE {
Must[Match["Char: ("]];
outc.charSet ← GetOctal3[];
Must[Match["|"]];
rope ← Rope.FromChar[VAL[GetOctal3[]]];
Must[Match[")"]];
};
Must[MatchBreak[]];
WHILE level >= 3
DO
Must[level = 3];
IF Match["LOOKS("]
THEN {
t: TiogaAccess.TiogaChar ← TiogaAccess.Peek[reader];
WHILE t.charSet = 0
AND t.char
IN TiogaAccess.Look
DO
t ← TiogaAccess.Get[reader];
outc.looks[t.char] ← TRUE;
t ← TiogaAccess.Peek[reader];
ENDLOOP;
Must[Match[")"]];
Must[MatchBreak[]];
}
ELSE {
pair: Atom.DottedPairNode ~ ParseProp[];
outc.propList ← CONS[NEW[Atom.DottedPairNode ← pair], outc.propList];
};
ENDLOOP;
IF rope #
NIL
THEN {
action: Rope.ActionType = { outc.char ← c; TiogaAccess.Put[writer, outc] };
[] ← Rope.Map[base: rope, action: action];
};
ENDLOOP;
};
IF Match["PROPERTIES"]
THEN {
MustNest[];
WHILE level >= 2
DO
pair: Atom.DottedPairNode;
Must[level = 2];
pair ← ParseProp[];
SELECT pair.key
FROM
$Comment => WITH pair.val SELECT FROM b: REF BOOL => node.comment ← b^ ENDCASE;
$Format => WITH pair.val SELECT FROM a: ATOM => node.format ← a ENDCASE;
ENDCASE => node.propList ← CONS[NEW[Atom.DottedPairNode ← pair], node.propList];
ENDLOOP;
};
Must[level = 1];
Must[Match["END NODE"]];
Must[MatchBreak[]];
node.deltaLevel ← level;
TiogaAccess.Put[writer, node];
}
ELSE {
Must[TiogaAccess.CopyNode[writer, reader].nodeEnd];
};
ENDLOOP;
};
MakeNodeSelection:
PROC ~ {
tSel: TEditDocument.Selection;
IF TEditSelection.pSel=NIL OR TEditSelection.pSel.viewer=NIL THEN RETURN;
tSel ← TEditSelection.Alloc[];
TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel];
SELECT tSel.granularity
FROM
point, char, word => {
tSel.start.pos.where ← 0;
tSel.end.pos.where ← TextNode.EndPos[tSel.end.pos.node];
tSel.granularity ← node;
};
ENDCASE => NULL;
TEditSelection.MakeSelection[new: tSel];
TEditSelection.Free[tSel]
};
OpenPropertiesOp: TEditInput.CommandProc ~ {
OpenProperties:
PROC [root: TiogaOps.Ref] ~ {
reader: TiogaAccess.Reader ← NIL;
writer: TiogaAccess.Writer ← NIL;
MakeNodeSelection[];
reader ← TiogaAccess.FromSelection[];
writer ← TiogaAccess.Create[];
TranslateToOpenProperties[reader, writer];
TiogaAccess.WriteSelection[writer];
};
TiogaOps.CallWithLocks[OpenProperties];
quit ← TRUE;
};
ClosePropertiesOp: TEditInput.CommandProc ~ {
PROC [viewer: ViewerClasses.Viewer ← NIL] RETURNS [recordAtom: BOOL ← TRUE, quit: BOOL ← FALSE];
error: {none, cant, uncaught} ← none;
errorPosition: INT ← 0;
CloseProperties:
PROC [root: TiogaOps.Ref] ~ {
reader: TiogaAccess.Reader ← NIL;
writer: TiogaAccess.Writer ← NIL;
MakeNodeSelection[];
reader ← TiogaAccess.FromSelection[];
writer ← TiogaAccess.Create[];
TranslateFromOpenProperties[reader, writer !
Cant => {errorPosition ← TiogaAccess.GetPosition[reader]}
];
TiogaAccess.WriteSelection[writer];
};
TiogaOps.CallWithLocks[CloseProperties !
Cant => {error ← cant; CONTINUE};
RuntimeError.UNCAUGHT => {error ← uncaught; CONTINUE};
];
SELECT error
FROM
none => NULL;
cant => {
TEditSelectionOps.ShowGivenPosition[viewer: viewer, pos: errorPosition, skipCommentNodes: TRUE];
ViewerOps.BlinkViewer[viewer, 200];
};
uncaught => CloseProperties[NIL]; -- do without lock for easy debug
ENDCASE => ERROR;
quit ← TRUE;
};