TO DO
Input Rope Stream should use RopeReader package (via overlaid binding of RIS?)
remove "debugRopeFromROS" flag when no longer needed
DIRECTORY
IO,
IOUtils,
RefText,
Rope USING [Concat, Fetch, FromRefText, InlineFetch, InlineLength, Length, ROPE];
IOSimpleStreamsImpl:
CEDAR
PROGRAM
IMPORTS IO, IOUtils, RefText, Rope
EXPORTS IO
= BEGIN
STREAM: TYPE = IO.STREAM;
ROPE: TYPE = Rope.ROPE;
StreamProcs: TYPE = IO.StreamProcs;
Nowhere Stream
noWhereStream:
PUBLIC
STREAM ←
IO.CreateStream[
IO.CreateStreamProcs[
variety: $output, class: $Null, putChar: NoWherePutChar, close: NoWhereClose], NIL];
NoWherePutChar: PROC [self: STREAM, char: CHAR] = {};
NoWhereClose: PROC [self: STREAM, abort: BOOL] = {};
NoInput Stream
noInputStream:
PUBLIC
STREAM ← IO.CreateStream[
streamProcs: IO.CreateStreamProcs[
variety: $input, class: $Null,
getChar: NoInputGetChar,
endOf: NoInputEndOf,
backup: NoInputBackup,
close: NoInputClose],
streamData: NIL];
NoInputGetChar:
PROC [self:
STREAM]
RETURNS [
CHAR] = {
ERROR IO.EndOfStream[self] };
NoInputBackup:
PROC [self:
STREAM, char:
CHAR] = {
ERROR IO.Error[$IllegalBackup, self] };
NoInputEndOf:
PROC [self:
STREAM]
RETURNS [
BOOL] = {
RETURN[TRUE] };
NoInputClose: PROC [self: STREAM, abort: BOOL] = { };
Input Stream from ROPE (RIS)
InputRopeStreamData: TYPE = REF InputRopeStreamRecord;
InputRopeStreamRecord:
TYPE =
RECORD [
rope: ROPE,
length: INT,
index: INT
];
InputRopeStreamProcs:
REF StreamProcs = IO.CreateStreamProcs[
variety: $input, class: $ROPE,
getChar: InputRopeStreamGetChar,
endOf: InputRopeStreamEndOf,
reset: InputRopeStreamReset,
getIndex: InputRopeStreamGetIndex,
setIndex: InputRopeStreamSetIndex,
getLength: InputRopeStreamGetLength,
backup: InputRopeStreamBackup
];
RIS:
PUBLIC
PROC [rope:
ROPE, oldStream:
STREAM]
RETURNS [stream: STREAM] = {
IF oldStream = NIL THEN GOTO newStream;
WITH oldStream.streamData
SELECT
FROM
data: InputRopeStreamData => {
data^ ← [rope: rope, length: rope.InlineLength[], index: 0];
oldStream.streamProcs ← InputRopeStreamProcs;
RETURN[oldStream] };
ENDCASE => GOTO newStream;
EXITS newStream =>
RETURN[
IO.CreateStream[
InputRopeStreamProcs,
NEW[InputRopeStreamRecord ← [
rope: rope, length: rope.Length[], index: 0]]]];
};
InputRopeStreamGetChar:
PROC [self:
STREAM]
RETURNS [char:
CHAR] = {
data: InputRopeStreamData = NARROW[self.streamData];
IF data.index >= data.length THEN ERROR IO.EndOfStream[self];
char ← data.rope.InlineFetch[data.index];
data.index ← data.index + 1;
};
InputRopeStreamEndOf:
PROC [self:
STREAM]
RETURNS [
BOOL] = {
data: InputRopeStreamData = NARROW[self.streamData];
RETURN[data.index >= data.length];
};
InputRopeStreamBackup:
PROC [self:
STREAM, char:
CHAR] = {
data: InputRopeStreamData = NARROW[self.streamData];
IF data.index = 0
OR data.rope.Fetch[data.index-1] # char
THEN
ERROR IO.Error[$IllegalBackup, self];
data.index ← data.index - 1;
};
InputRopeStreamReset:
PROC [self:
STREAM] = {
data: InputRopeStreamData = NARROW[self.streamData];
data.index ← data.length;
};
InputRopeStreamGetIndex:
PROC [self:
STREAM]
RETURNS [
INT] = {
data: InputRopeStreamData = NARROW[self.streamData];
RETURN[data.index];
};
InputRopeStreamSetIndex:
PROC [self:
STREAM, index:
INT] = {
data: InputRopeStreamData = NARROW[self.streamData];
IF index NOT IN [0 .. data.length] THEN ERROR IO.Error[$BadIndex, self];
data.index ← index;
};
InputRopeStreamGetLength:
PROC [self:
STREAM]
RETURNS [length:
INT] = {
data: InputRopeStreamData = NARROW[self.streamData];
RETURN[data.length];
};
Output Stream to ROPE (ROS)
OutputRopeStreamData: TYPE = REF OutputRopeStreamRecord;
OutputRopeStreamRecord:
TYPE =
RECORD [
rope: ROPE,
text: REF TEXT
];
OutputRopeStreamProcs:
REF StreamProcs = IO.CreateStreamProcs[
variety: $output, class: $ROPE,
putChar: OutputRopeStreamPutChar,
eraseChar: OutputRopeStreamEraseChar,
reset: OutputRopeStreamReset,
getLength: OutputRopeStreamGetLength,
getIndex: OutputRopeStreamGetIndex,
close: OutputRopeStreamClose
];
ROS:
PUBLIC
PROC [oldStream:
STREAM]
RETURNS [stream: STREAM] = {
IF oldStream = NIL THEN GOTO newStream;
WITH oldStream.streamData
SELECT
FROM
data: OutputRopeStreamData => {
data.rope ← NIL;
IF data.text = NIL THEN data.text ← RefText.ObtainScratch[100]
ELSE data.text.length ← 0;
oldStream.streamProcs ← OutputRopeStreamProcs;
RETURN[oldStream] };
ENDCASE => GOTO newStream;
EXITS newStream =>
RETURN[
IO.CreateStream[
OutputRopeStreamProcs,
NEW[OutputRopeStreamRecord ← [
rope: NIL, text: RefText.ObtainScratch[100]]]]];
};
OutputRopeStreamPutChar:
PROC [self:
STREAM, char:
CHAR] = {
data: OutputRopeStreamData = NARROW[self.streamData];
text: REF TEXT = data.text;
IF text.length = text.maxLength
THEN {
data.rope ← data.rope.Concat[Rope.FromRefText[text]];
text.length ← 0 };
text[text.length] ← char;
text.length ← text.length + 1;
};
OutputRopeStreamEraseChar:
PROC [self:
STREAM, char:
CHAR] = {
data: OutputRopeStreamData = NARROW[self.streamData];
IF data.text.length # 0 THEN data.text.length ← data.text.length-1;
};
OutputRopeStreamReset:
PROC [self:
STREAM] = {
data: OutputRopeStreamData = NARROW[self.streamData];
data.rope ← NIL;
data.text.length ← 0;
};
OutputRopeStreamGetLength:
PROC [self:
STREAM]
RETURNS [length:
INT] = {
data: OutputRopeStreamData = NARROW[self.streamData];
RETURN[data.text.length + data.rope.Length[]];
};
OutputRopeStreamGetIndex:
PROC [self:
STREAM]
RETURNS [index:
INT] = {
RETURN[self.GetLength[]];
};
debugRopeFromROS: BOOL ← TRUE; -- crock until important clients get fixed
RopeFromROS:
PUBLIC
PROC [self:
STREAM, close:
BOOL]
RETURNS [
ROPE] = {
data: OutputRopeStreamData = NARROW[self.streamData];
result:
ROPE ←
IF data.text.length = 0
AND debugRopeFromROS
THEN
NIL
ELSE Rope.FromRefText[data.text];
IF close
THEN {
RefText.ReleaseScratch[data.text];
data.text ← NIL;
self.streamProcs ← IOUtils.closedStreamProcs;
IF data.rope # NIL THEN { result ← data.rope.Concat[result]; data.rope ← NIL }
}
ELSE {
data.text.length ← 0;
result ← data.rope ← data.rope.Concat[result];
};
RETURN[result];
};
OutputRopeStreamClose:
PROC [self:
STREAM, abort:
BOOL] = {
data: OutputRopeStreamData = NARROW[self.streamData];
data.rope ← NIL;
RefText.ReleaseScratch[data.text];
data.text ← NIL;
self.streamProcs ← IOUtils.closedStreamProcs;
};
Input Stream from REF TEXT (TIS)
InputTextStreamData: TYPE = REF InputTextStreamRecord;
InputTextStreamRecord:
TYPE =
RECORD [
text: REF READONLY TEXT,
index: NAT
];
InputTextStreamProcs:
REF StreamProcs = IO.CreateStreamProcs[
variety: $input, class: $TEXT,
getChar: InputTextStreamGetChar,
endOf: InputTextStreamEndOf,
reset: InputTextStreamReset,
getIndex: InputTextStreamGetIndex,
setIndex: InputTextStreamSetIndex,
getLength: InputTextStreamGetLength,
backup: InputTextStreamBackup
];
TIS:
PUBLIC
PROC [text:
REF
READONLY
TEXT, oldStream:
STREAM]
RETURNS [STREAM] = {
IF oldStream = NIL THEN GOTO newStream;
WITH oldStream.streamData
SELECT
FROM
data: InputTextStreamData => {
data^ ← [text: text, index: 0];
oldStream.streamProcs ← InputTextStreamProcs;
RETURN[oldStream] };
ENDCASE => GOTO newStream;
EXITS newStream =>
RETURN[
IO.CreateStream[
InputTextStreamProcs,
NEW[InputTextStreamRecord ← [
text: text, index: 0]]]];
};
InputTextStreamGetChar:
PROC [self:
STREAM]
RETURNS [char:
CHAR] = {
data: InputTextStreamData = NARROW[self.streamData];
IF data.index >= data.text.length THEN ERROR IO.EndOfStream[self];
char ← data.text[data.index];
data.index ← data.index + 1;
};
InputTextStreamEndOf:
PROC [self:
STREAM]
RETURNS [
BOOL] = {
data: InputTextStreamData = NARROW[self.streamData];
RETURN[data.index >= data.text.length];
};
InputTextStreamBackup:
PROC [self:
STREAM, char:
CHAR] = {
data: InputTextStreamData = NARROW[self.streamData];
IF data.index = 0
OR data.text[data.index-1] # char
THEN
ERROR IO.Error[$IllegalBackup, self];
data.index ← data.index - 1;
};
InputTextStreamReset:
PROC [self:
STREAM] = {
data: InputTextStreamData = NARROW[self.streamData];
data.index ← data.text.length;
};
InputTextStreamGetIndex:
PROC [self:
STREAM]
RETURNS [
INT] = {
data: InputTextStreamData = NARROW[self.streamData];
RETURN[data.index];
};
InputTextStreamSetIndex:
PROC [self:
STREAM, index:
INT] = {
data: InputTextStreamData = NARROW[self.streamData];
IF index NOT IN [0 .. data.text.length] THEN ERROR IO.Error[$BadIndex, self];
data.index ← index;
};
InputTextStreamGetLength:
PROC [self:
STREAM]
RETURNS [length:
INT] = {
data: InputTextStreamData = NARROW[self.streamData];
RETURN[data.text.length];
};
Output Stream to REF TEXT (TOS)
OutputTextStreamProcs:
REF StreamProcs = IO.CreateStreamProcs[
variety: $output, class: $TEXT,
putChar: OutputTextStreamPutChar,
eraseChar: OutputTextStreamEraseChar,
reset: OutputTextStreamReset,
getLength: OutputTextStreamGetLength,
getIndex: OutputTextStreamGetIndex,
close: NIL
];
TOS:
PUBLIC
PROC [text:
REF
TEXT, oldStream:
STREAM]
RETURNS [stream: STREAM] = {
IF oldStream = NIL THEN oldStream ← IO.CreateStream[OutputTextStreamProcs, NIL]
ELSE {
oldStream.streamProcs ← OutputTextStreamProcs;
WITH oldStream.streamData
SELECT
FROM
oldText: REF TEXT => IF text = NIL THEN text ← oldText;
ENDCASE;
};
IF text = NIL THEN text ← NEW[TEXT[100]];
text.length ← 0;
oldStream.streamData ← text;
RETURN[oldStream];
};
OutputTextStreamPutChar:
PROC [self:
STREAM, char:
CHAR] = {
self.streamData ← RefText.InlineAppendChar[to: NARROW[self.streamData], from: char];
};
OutputTextStreamEraseChar:
PROC [self:
STREAM, char:
CHAR] = {
text: REF TEXT = NARROW[self.streamData];
IF text.length > 0 THEN text.length ← text.length - 1;
};
OutputTextStreamReset:
PROC [self:
STREAM] = {
NARROW[self.streamData, REF TEXT].length ← 0;
};
OutputTextStreamGetLength:
PROC [self:
STREAM]
RETURNS [length:
INT] = {
RETURN[NARROW[self.streamData, REF TEXT].length];
};
OutputTextStreamGetIndex:
PROC [self:
STREAM]
RETURNS [index:
INT] = {
RETURN[NARROW[self.streamData, REF TEXT].length];
};