TiogaStreamsImpl.Mesa
Last Edited by: Spreitzer, May 5, 1985 3:03:21 pm PDT
DIRECTORY
IO, IOUtils, Rope, TextNode, TiogaFileOps, TiogaOps, TiogaStreams;
TiogaStreamsImpl:
CEDAR
PROGRAM
IMPORTS IO, IOUtils, Rope, TN: TextNode, TFO: TiogaFileOps, TiogaOps
EXPORTS TiogaStreams =
BEGIN
ROPE: TYPE = Rope.ROPE;
IsATS:
PUBLIC
PROC [s:
IO.
STREAM]
RETURNS [is:
BOOLEAN] =
{is ← IF s = NIL THEN FALSE ELSE IF s.streamData = NIL THEN FALSE ELSE (ISTYPE[s.streamData, TiogaInStreamData] OR ISTYPE[s.streamData, TiogaOutStreamData])};
CreateInput:
PUBLIC
PROC [from:
TN.Ref]
RETURNS [in:
IO.
STREAM] =
BEGIN
sd: TiogaInStreamData ← NEW [TiogaInStreamDataRep ← [next: 0, node: from, root: from]];
IF from = NIL THEN sd.end ← TRUE ELSE NodeHello[sd];
in ← IO.CreateStream[streamProcs: tiogaInStreamProcs, streamData: sd];
END;
tiogaInStreamProcs: REF IO.StreamProcs ← IO.CreateStreamProcs[variety: input, class: $TiogaInStream, getChar: GetChar, endOf: EndOf, backup: BackupIn, getIndex: GetInIndex, setIndex: SetInIndex];
GetChar:
PROC [self:
IO.
STREAM]
RETURNS [char:
CHAR] =
BEGIN
sd: TiogaInStreamData ← NARROW[self.streamData];
TryForNextChar[sd];
IF sd.end THEN ERROR IO.EndOfStream[self];
char ← sd.nodeRope.Fetch[sd.next];
sd.next ← sd.next + 1;
END;
EndOf:
PROC [self:
IO.
STREAM]
RETURNS [end:
BOOLEAN] =
BEGIN
sd: TiogaInStreamData ← NARROW[self.streamData];
TryForNextChar[sd];
end ← sd.end;
END;
BackupIn:
PROC [self:
IO.
STREAM, char:
CHAR] = {
past: INT ← self.GetIndex[];
oldChar: CHAR;
IF past = 0 THEN ERROR IO.Error[IllegalBackup, self];
self.SetIndex[past-1];
oldChar ← self.GetChar[];
IF oldChar # char THEN ERROR IO.Error[IllegalBackup, self];
self.SetIndex[past-1];
};
GetInIndex:
PROC [self:
IO.
STREAM]
RETURNS [index:
INT] =
TRUSTED
BEGIN
sd: TiogaInStreamData ← NARROW[self.streamData];
TryForNextChar[sd];
index ← TN.LocOffset[loc1: [sd.root, 0], loc2: [sd.node, sd.next], break: 1, skipCommentNodes: TRUE];
END;
SetInIndex:
PROC [self:
IO.
STREAM, index:
INT] =
TRUSTED
BEGIN
sd: TiogaInStreamData ← NARROW[self.streamData];
new: TN.Location ← TN.LocRelative[location: [sd.root, 0], count: index, break: 1, skipCommentNodes: TRUE];
IF new.node #
NIL
THEN
BEGIN
sd.node ← new.node;
sd.next ← new.where;
sd.end ← FALSE;
NodeHello[sd];
END
ELSE ERROR IO.Error[BadIndex, self]; --not strictly the right thing to do!
END;
CurInNode:
PUBLIC
PROC [s:
IO.
STREAM]
RETURNS [n:
TN.Ref] =
BEGIN
sd: TiogaInStreamData ← NARROW[s.streamData];
n ← sd.node;
END;
SkipChildren:
PUBLIC
PROC [s:
IO.
STREAM] =
BEGIN
Work:
PROC [of:
TN.Ref]
RETURNS [next:
TN.Ref, dd:
INT] =
TRUSTED
BEGIN
[next, dd] ← TN.Forward[of];
IF dd > 1 THEN ERROR;
IF dd < 1 THEN RETURN;
dd ← dd - 1;
WHILE dd = 0 DO [next, dd] ← Work[next] ENDLOOP;
dd ← dd + 1;
END;
sd: TiogaInStreamData ← NARROW[s.streamData];
new: TN.Ref;
dd: INT;
[new, dd] ← Work[sd.node];
IF sd.depth+dd <= 0 OR new = NIL THEN {sd.end ← TRUE; RETURN};
sd.node ← new;
sd.depth ← sd.depth + dd;
sd.next ← 0;
NodeHello[sd];
END;
TiogaInStreamData: TYPE = REF TiogaInStreamDataRep;
TiogaInStreamDataRep:
TYPE =
RECORD [
end: BOOLEAN ← FALSE,
next, nodeLen, depth: INT ← 0,
nodeRope: ROPE ← NIL,
node, root: TN.Ref];
TryForNextChar:
PROC [sd: TiogaInStreamData] =
TRUSTED
BEGIN
IF sd.end THEN RETURN;
WHILE sd.next >= sd.nodeLen
DO
dd: INT;
next: TN.Ref;
[next, dd] ← TN.Forward[sd.node];
IF sd.depth+dd <= 0 OR next = NIL THEN {sd.end ← TRUE; RETURN};
sd.next ← sd.next - sd.nodeLen;
sd.node ← next;
sd.depth ← sd.depth + dd;
NodeHello[sd];
ENDLOOP;
END;
NodeHello:
PROC [sd: TiogaInStreamData] =
TRUSTED
BEGIN
tn: TN.RefTextNode ← sd.node;
IF tn.comment THEN {sd.nodeRope ← NIL; sd.nodeLen ← 0}
ELSE {sd.nodeRope ← TN.NodeRope[tn].Concat["\n"]; sd.nodeLen ← sd.nodeRope.Length[]};
END;
CreateOutput:
PUBLIC
PROC [to:
TFO.Ref, defaultFormat:
ROPE ←
NIL]
RETURNS [out:
IO.
STREAM] =
BEGIN
sd: TiogaOutStreamData ← NEW [TiogaOutStreamDataRep ← [root: to, nodes: LIST[to], deltaDepth: 1, defaultFormat: defaultFormat]];
pfp: IOUtils.PFProcs;
out ← IO.CreateStream[streamProcs: tiogaOutStreamProcs, streamData: sd];
pfp ← IOUtils.CopyPFProcs[out];
[] ← IOUtils.SetPFCodeProc[pfProcs: pfp, char: 'l, codeProc: FormatL];
[] ← IOUtils.SetPFProcs[stream: out, pfProcs: pfp];
PrepareForChar[sd];
END;
tiogaOutStreamProcs: REF IO.StreamProcs ← IO.CreateStreamProcs[variety: output, class: $TiogaOutStream, putChar: PutChar, putBlock: PutBlock, close: CloseOut];
PutChar:
PROC [self:
IO.
STREAM, char:
CHAR] =
BEGIN
sd: TiogaOutStreamData ← NARROW[self.streamData];
PrepareForChar[sd];
sd.contents ← sd.contents.Concat[Rope.FromChar[char]];
END;
PutBlock:
PROC [self:
IO.
STREAM, block:
REF
READONLY
TEXT, startIndex, count:
NAT] =
BEGIN
sd: TiogaOutStreamData ← NARROW[self.streamData];
PrepareForChar[sd];
sd.contents ← sd.contents.Concat[Rope.FromRefText[block].Substr[start: startIndex, len: count]];
END;
CloseOut:
PROC [self:
IO.
STREAM, abort:
BOOL ←
FALSE] =
BEGIN
EndNode[self];
IOUtils.AmbushStream[self: self, streamProcs: IOUtils.closedStreamProcs, streamData: NIL];
END;
CurOutNode:
PUBLIC
PROC [s:
IO.
STREAM]
RETURNS [n:
TFO.Ref] =
BEGIN
sd: TiogaOutStreamData ← NARROW[s.streamData];
n ← sd.nodes.first;
END;
SetFormat:
PUBLIC
PROC [of:
IO.
STREAM, format:
ROPE] =
TRUSTED
BEGIN
n: TFO.Ref ← CurOutNode[of];
TFO.SetFormat[n, format];
END;
FormatL:
PROC [stream:
IO.
STREAM, val:
IO.Value, format: IOUtils.Format, char:
CHAR] = {
sd: TiogaOutStreamData ← NARROW[stream.streamData];
WITH val
SELECT
FROM
rv: IO.Value[rope] => FormatWork[stream, sd, rv.value];
ENDCASE => ERROR IO.Error[PFUnprintableValue, stream];
};
FormatWork:
PROC [stream:
IO.
STREAM, sd: TiogaOutStreamData, chars:
ROPE] = {
index: INT ← sd.contents.Length[];
FOR i:
INT
IN [0 .. chars.Length[])
DO
c: CHAR ← chars.Fetch[i];
SELECT c
FROM
' => FormatWork[stream, sd, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"];
IN ['a .. 'z] => {
IF sd.looks[c] = NIL OR sd.looks[c].first.afterLast # notClosed THEN sd.looks[c] ← CONS[[first: index, afterLast: notClosed], sd.looks[c]]
};
IN ['A .. 'Z] => {l: Look ← c + ('a - 'A);
IF sd.looks[l] # NIL AND sd.looks[l].first.afterLast = notClosed THEN sd.looks[l].first.afterLast ← index;
};
ENDCASE => ERROR IO.Error[PFUnprintableValue, stream];
ENDLOOP;
};
EndNode:
PUBLIC
PROC [s:
IO.
STREAM, idempotently:
BOOLEAN ←
FALSE] =
TRUSTED
BEGIN
sd: TiogaOutStreamData ← NARROW[s.streamData];
IF
NOT sd.ready
THEN
BEGIN
IF idempotently THEN RETURN;
PrepareForChar[sd];
END;
TFO.SetContents[sd.nodes.first, sd.contents];
SetLooks[sd];
sd.ready ← FALSE;
sd.contents ← NIL;
END;
SetLooks:
PROC [sd: TiogaOutStreamData] = {
len: INT ← sd.contents.Length[];
FOR l: Look
IN Look
DO
FOR lr: LooksRuns ← sd.looks[l], lr.rest
WHILE lr #
NIL
DO
afterLast: INT ← IF lr.first.afterLast = notClosed THEN len ELSE lr.first.afterLast;
TFO.AddLooks[x: sd.nodes.first, start: lr.first.first, len: afterLast - lr.first.first, look: l, root: sd.root];
ENDLOOP;
ENDLOOP;
sd.looks ← ALL[NIL];
};
ChangeDepth:
PUBLIC
PROC [s:
IO.
STREAM, deltaDepth:
INTEGER ← 0
-- <= 1 --, autoEndNode:
BOOLEAN ←
TRUE, idempotently:
BOOLEAN ←
FALSE] =
BEGIN
sd: TiogaOutStreamData ← NARROW[s.streamData];
newDD: INT;
IF autoEndNode THEN EndNode[s, idempotently];
IF sd.ready THEN ERROR NotNow[s];
newDD ← sd.deltaDepth + deltaDepth;
IF newDD + sd.depth <= 0 THEN ERROR BadDeltaDepth[s, newDD];
IF newDD > 1 THEN ERROR BadDeltaDepth[s, newDD];
sd.deltaDepth ← newDD;
END;
NotNow: PUBLIC ERROR [s: IO.STREAM] = CODE;
BadDeltaDepth: PUBLIC ERROR [s: IO.STREAM, totalDeltaDepth: INT] = CODE;
TiogaOutStreamData: TYPE = REF TiogaOutStreamDataRep;
TiogaOutStreamDataRep:
TYPE =
RECORD [
root: TFO.Ref ← NIL,
nodes: LIST OF TFO.Ref ← NIL,
ready: BOOLEAN ← FALSE,
depth, deltaDepth: INT ← 0,
contents: ROPE ← NIL,
defaultFormat: ROPE ← NIL,
looks: ARRAY Look OF LooksRuns ← ALL[NIL]];
LooksRuns: TYPE = LIST OF LooksRun;
LooksRun: TYPE = RECORD [first, afterLast: INT];
Look: TYPE = CHAR['a .. 'z];
notClosed: INT = -1;
PrepareForChar:
PROC [sd: TiogaOutStreamData] =
TRUSTED
BEGIN
IF sd.ready THEN RETURN;
WHILE sd.deltaDepth < 0
DO
sd.nodes ← sd.nodes.rest;
sd.depth ← sd.depth - 1;
sd.deltaDepth ← sd.deltaDepth + 1;
ENDLOOP;
IF sd.deltaDepth = 0 THEN sd.nodes.first ← TFO.InsertNode[x: sd.nodes.first, child: FALSE]
ELSE {sd.nodes ← CONS[TFO.InsertAsLastChild[x: sd.nodes.first], sd.nodes]; sd.depth ← sd.depth + 1};
IF sd.defaultFormat # NIL THEN TFO.SetFormat[sd.nodes.first, sd.defaultFormat];
sd.deltaDepth ← 0;
sd.contents ← NIL;
sd.ready ← TRUE;
END;
true: REF ANY ← NEW [BOOL ← TRUE];
CopyChildren:
PUBLIC
PROC [from:
TN.Ref, to:
TFO.Ref] =
BEGIN
Work:
PROC [from:
TN.Ref, to:
TFO.Ref]
RETURNS [next:
TN.Ref, dd:
INT] =
TRUSTED
BEGIN
last: TFO.Ref ← to;
lastIsParent: BOOLEAN ← TRUE;
[next, dd] ← TN.Forward[from];
IF dd > 1 THEN ERROR;
IF dd < 1 THEN RETURN;
dd ← dd - 1;
WHILE dd = 0
DO
toKid: TFO.Ref;
tn: TN.RefTextNode ← next;
r: ROPE ← TN.NodeRope[tn];
toKid ← last.InsertNode[child: lastIsParent];
toKid.SetContents[r];
IF tn.comment THEN TiogaOps.PutProp[n: LOOPHOLE[toKid], name: $Comment, value: true];
last ← toKid; lastIsParent ← FALSE;
[next, dd] ← Work[next, toKid];
ENDLOOP;
dd ← dd + 1;
END;
[] ← Work[from, to];
END;
END.