TokenIOImpl.mesa
Copyright © 1983, 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi August 24, 1983 3:10 pm
Last Edited by: Christian Jacobi, April 7, 1987 5:24:48 pm PDT
DIRECTORY
Atom, Basics, FS, IO, LRUCache, Properties, Rope, RuntimeError, TerminalIO, TokenIO, UserProfile;
TokenIOImpl:
CEDAR
MONITOR
IMPORTS Atom, FS, IO, LRUCache, Rope, RuntimeError, TerminalIO, UserProfile
EXPORTS TokenIO
SHARES IO =
BEGIN
OPEN TokenIO;
-- Terminology
-- read, write for encoded, (exported) IO
-- get/put for actual unencoded IO
--these x-codes are found on files
xTokenIOCode: BYTE = 237; -- this is really a TokenStreams file
xVersionCode: BYTE = 2; -- this is really a TokenStreams file of correct version
xTabMin: BYTE = 0;
xTabMax: BYTE = 100;
xIntSFirst: BYTE = 101;
xIntSLast: BYTE = 241;
xRes: BYTE = 242;
xIntN1: BYTE = 243;
xIntP1: BYTE = 244;
xIntN2: BYTE = 245;
xIntP2: BYTE = 246;
xInt4: BYTE = 247;
xReal: BYTE = 248;
xStream: BYTE = 249;
xRope: BYTE = 250;
xFillR: BYTE = 251;
xFillA: BYTE = 252;
xPush2: BYTE = 253;
xPush: BYTE = 254;
xPop: BYTE = 255;
shortNatLast: BYTE = xIntSLast-xIntSFirst;
tabSize: BYTE = xTabMax-xTabMin+1;
escChar: CHAR = 33C;
EncodingError: PUBLIC SIGNAL = CODE;
Stopped: PUBLIC SIGNAL = CODE;
Imp: TYPE = REF ImpRec;
ImpRec:
PUBLIC
TYPE =
RECORD [
againList: LIST OF Token ← NIL, --reserved for the implementor
readFunny: BOOL ← FALSE,
isOldV1: BOOL←FALSE,
isR: BOOL,
tab: LRUCache.Handle,
rTab: ARRAY [xTabMin..xTabMax] OF Rope.ROPE ← ALL[NIL],
catcher: PROC [Handle] ← NIL,
oldTable: REF ARRAY [1..100] OF ATOM ← NIL
];
PutC:
PROC [self:
IO.
STREAM, char:
CHAR] =
INLINE {
self.streamProcs.putChar[self, char];
};
PutB:
PROC [s:
IO.
STREAM, c:
BYTE] =
INLINE {
s.streamProcs.putChar[s, LOOPHOLE[c, CHAR]];
};
PutInt:
PROC [s:
IO.
STREAM, i:
INT32] =
TRUSTED INLINE {
s.streamProcs.unsafePutBlock[s, [@i, 0, 4]]
};
Put2B:
PROC [s:
IO.
STREAM, c:
CARD16] =
TRUSTED
INLINE {
s.streamProcs.unsafePutBlock[s, [@c, 0, 2]]
};
PutRope:
PROC [s:
IO.
STREAM, r: Rope.
ROPE] =
INLINE {
--NIL and "" is encoded differently; this fidelity is mainly important for atoms
IF r=NIL THEN PutB[s, 255]
ELSE {
leng: INT ~ Rope.Length[r];
IF leng<254
THEN PutB[s, leng]
ELSE {PutB[s, 254]; PutInt[s, leng]};
IO.PutRope[s, r];
}
};
GetC:
PROC [self:
IO.STREAM]
RETURNS [
CHAR] =
INLINE {
RETURN[self.streamProcs.getChar[self]];
};
GetB:
PROC [s:
IO.
STREAM]
RETURNS [c:
BYTE] =
INLINE {
c ← LOOPHOLE[s.streamProcs.getChar[s]];
};
Get2B:
PROC [s:
IO.
STREAM]
RETURNS [c:
CARD16] =
TRUSTED
INLINE {
IF s.streamProcs.unsafeGetBlock[s, [@c, 0, 2]]<2 THEN ERROR EncodingError
};
GetInt:
PROC [s:
IO.
STREAM]
RETURNS [i:
INT32] =
TRUSTED
INLINE {
IF s.streamProcs.unsafeGetBlock[s, [@i, 0, 4]]<4 THEN ERROR EncodingError;
};
GetRope:
PROC [s:
IO.
STREAM]
RETURNS [Rope.
ROPE] =
INLINE {
XGetChar: PROC [] RETURNS [CHAR] = { RETURN [GetC[s]] };
leng: INT ← GetB[s];
IF leng>=254
THEN {
IF leng=255 THEN RETURN [NIL] ELSE leng ← GetInt[s];
};
IF leng=0 THEN RETURN [""];
RETURN [ Rope.FromProc[len: leng, p: XGetChar] ];
};
MyRopeToAtom:
PROC [r: Rope.
ROPE]
RETURNS [
ATOM] =
INLINE {
RETURN [ IF r=NIL THEN NIL ELSE Atom.MakeAtom[r] ];
};
Write:
PUBLIC
PROC [h: Handle, t: Token] = {
WITH t
SELECT
FROM
int: Token.int => WriteInt[h, int.value];
push: Token.push => WritePush[h, push.value];
push2: Token.push2 => WritePush2[h, push2.value];
pop: Token.pop => WritePop[h];
atom: Token.atom => WriteAtom[h, atom.value];
rope: Token.rope => WriteRope[h, rope.value];
real: Token.real => WriteReal[h, real.value];
streamed: Token.streamed => ERROR;
endOfStream: Token.endOfStream => ERROR;
error: Token.error => ERROR;
ENDCASE => ERROR;
};
WriteInt:
PUBLIC
PROC [h: Handle, i:
INT] = {
IF h.stop^ THEN ERROR Stopped;
IF h.funny THEN TSOStopFunny[h];
SELECT
TRUE
FROM
i IN [0..shortNatLast] => {PutB[h.s, i+xIntSFirst]};
i IN [0..255] => {PutB[h.s, xIntP1]; PutB[h.s, i]};
i IN [0..INT[LAST[CARD16]]] => {PutB[h.s, xIntP2]; Put2B[h.s, i]};
i IN [-256..-1] => {PutB[h.s, xIntN1]; PutB[h.s, -i-1]};
i IN [-INT[LAST[CARD16]]..-1] => {PutB[h.s, xIntN2]; Put2B[h.s, -i-1]};
ENDCASE => {PutB[h.s, xInt4]; PutInt[h.s, i]};
};
WriteAtom:
PUBLIC
PROC [h: Handle, a:
ATOM] = {
r: Rope.ROPE ~ Atom.GetPName[a];
index: NAT; insert: BOOL;
imp: Imp ← h.imp;
IF h.stop^ THEN ERROR Stopped;
IF h.funny THEN TSOStopFunny[h];
[index: index, insert: insert] ← LRUCache.Include[imp.tab, r];
IF insert
THEN {PutB[h.s, xFillA]; PutB[h.s, index]; PutRope[h.s, r]}
ELSE PutB[h.s, index]
};
WriteRope:
PUBLIC
PROC [h: Handle, r: Rope.
ROPE] = {
index: NAT; insert: BOOL;
imp: Imp ← h.imp;
IF h.stop^ THEN ERROR Stopped;
IF h.funny THEN TSOStopFunny[h];
[index: index, insert: insert] ← LRUCache.Include[imp.tab, r];
IF insert
THEN {PutB[h.s, xFillR]; PutB[h.s, index]; PutRope[h.s, r]}
ELSE {PutB[h.s, xRope]; PutB[h.s, index]}
};
WritePush:
PUBLIC
PROC [h: Handle, a:
ATOM←
NIL] = {
IF h.stop^ THEN ERROR Stopped;
IF h.funny THEN TSOStopFunny[h];
PutB[h.s, xPush]; WriteAtom[h, a];
};
WritePush2:
PUBLIC
PROC [h: Handle, a:
ATOM←
NIL] = {
IF h.stop^ THEN ERROR Stopped;
IF h.funny THEN TSOStopFunny[h];
PutB[h.s, xPush2]; WriteAtom[h, a];
};
WritePop:
PUBLIC
PROC [h: Handle] = {
IF h.stop^ THEN ERROR Stopped;
IF h.funny THEN TSOStopFunny[h];
PutB[h.s, xPop];
};
WriteReal:
PUBLIC
PROC [h: Handle, r:
REAL] = {
long: Basics.LongNumber = Basics.LongNumber[real[real: r]];
IF h.stop^ THEN ERROR Stopped;
IF h.funny THEN TSOStopFunny[h];
PutB[h.s, xReal]; Put2B[h.s, long.lo]; Put2B[h.s, long.hi];
};
MarkAndWriteInt:
PUBLIC
PROC [h: Handle, value:
INT]
RETURNS [Mark] = {
pos: INT;
IF h.stop^ THEN ERROR Stopped;
IF h.funny THEN TSOStopFunny[h];
PutB[h.s, xInt4];
pos ← IO.GetIndex[h.s];
PutInt[h.s, value];
RETURN [NEW[INT←pos]]
};
UpdateMark:
PUBLIC
PROC [h: Handle, mark: Mark, value:
INT] = {
intPos: INT ← NARROW[mark, REF INT]^;
curPos: INT = IO.GetIndex[h.s];
IF h.stop^ THEN ERROR Stopped;
IO.SetIndex[h.s, intPos];
PutInt[h.s, value];
IO.SetIndex[h.s, curPos];
};
InternalRead:
PROC [h: Handle]
RETURNS [Token←[error[
NIL]]] = {
ENABLE {
IO.EndOfStream => {
IF NotDebugging[] THEN GOTO endOfStreamReturn
};
IO.Error => {
IF ec=Failure
THEN {
error: FS.ErrorDesc;
error ← FS.ErrorFromStream[h.s ! RuntimeError.UNCAUGHT => CONTINUE];
TerminalIO.PutF["**TokenIO => FS.Error[%g, ""%g""]\n",
[atom[error.code]], [rope[error.explanation]]
];
}; --you want to land in the debugger
};
FS.Error => {
TerminalIO.PutF["**TokenIO => FS.Error[%g, ""%g""]\n",
[atom[error.code]], [rope[error.explanation]]
];
}; --you want to land in the debugger
RuntimeError.
UNCAUGHT => {
TerminalIO.PutRope["** TokenIO => unknown error\n"];
IF NotDebugging[] THEN GOTO errorReturn;
};
};
FillTable:
PROC []
RETURNS [r: Rope.
ROPE] =
INLINE {
c: BYTE = GetB[h.s];
SELECT
TRUE
FROM
c IN [xTabMin..xTabMax] => r ← imp.rTab[c] ← GetRope[h.s];
ENDCASE => ERROR;
};
imp: Imp = h.imp;
b: BYTE;
IF (b ← GetB[h.s]) <= xRes
THEN {
--this if statement for speed only
SELECT
TRUE
FROM
b IN [xIntSFirst..xIntSLast] => RETURN [[int[b-xIntSFirst]]];
b IN [xTabMin..xTabMax] => RETURN [ [atom[MyRopeToAtom[imp.rTab[b]]]] ];
ENDCASE => ERROR;
}
ELSE {
SELECT b
FROM
xIntP2 => RETURN [[int[Get2B[h.s]]]];
xIntP1 => RETURN [[int[GetB[h.s]]]];
xPop => RETURN [[pop[NIL]]];
xPush2 =>
WITH InternalRead[h]
SELECT
FROM
atom: Token.atom => RETURN [[push2[atom.value]]];
ENDCASE => RETURN [[error[NIL]]];
xPush =>
WITH InternalRead[h]
SELECT
FROM
atom: Token.atom => RETURN [[push[atom.value]]];
ENDCASE => RETURN [[error[NIL]]];
xRope =>
IF (b ← GetB[h.s])
IN [xTabMin..xTabMax]
THEN RETURN [ [rope[imp.rTab[b]]] ]
ELSE ERROR EncodingError;
xFillR => RETURN [[rope[FillTable[]]]];
xIntN1 => {i: INT = GetB[h.s]; RETURN [[int[-i-1]]]};
xInt4 => RETURN [[int[GetInt[h.s]]]];
xIntN2 => {i: INT = Get2B[h.s]; RETURN [[int[-i-1]]]};
xFillA => RETURN [[atom[MyRopeToAtom[FillTable[]]]]];
xStream => {TSIStartFunny[h]; RETURN [[streamed[NIL]]]};
xReal => {
long: Basics.LongNumber = Basics.LongNumber[pair[ lo: Get2B[h.s], hi: Get2B[h.s]]];
RETURN [[real[long.real]]];
};
ENDCASE => ERROR;
}
EXITS
errorReturn => RETURN [[error[NIL]]];
endOfStreamReturn => {
TerminalIO.PutRope["** end of TokenStreams stream\n"];
RETURN [ [endOfStream[]] ]
};
};
OLDV1InternalReadToken:
PROC [h: Handle]
RETURNS [Token←[error[
NIL]]] =
BEGIN
ENABLE {
IO.EndOfStream => IF NotDebugging[] THEN GOTO endOfStreamReturn;
IO.Error => IF NotDebugging[] THEN GOTO endOfStreamReturn;
RuntimeError.
UNCAUGHT => {
TerminalIO.PutRope["** unknown TokenStreams error\n"];
IF NotDebugging[] THEN GOTO errorReturn;
}
};
xLongInt: BYTE = 255;
xPushFlag: BYTE = 254;
xRope: BYTE = 253;
xPopFlag: BYTE = 252;
xFillTable: BYTE = 0;
xReadTableMin:
BYTE = 1;
-- >0 !
dummy: BYTE = xReadTableMin-1;
xReadTableMax: BYTE = 100;
xFirstShortNat: BYTE = xReadTableMax+1;
xLastShortNat: BYTE = 247;
shortNatLast: BYTE = xLastShortNat-xFirstShortNat;
xTwoByteCard: BYTE = 251;
xTwoByteNegative: BYTE = 250;
xOneBytePos: BYTE = 249;
xOneByteNeg: BYTE = 248;
OldGetRope:
PROC [h: Handle]
RETURNS [Rope.
ROPE] = {
XGetChar: PROC [] RETURNS [CHAR] = { RETURN [GetC[h.s]] };
leng: INT ← GetB[h.s];
IF leng=255 THEN leng ← GetInt[h.s];
IF leng=0 THEN RETURN [""];
RETURN [ Rope.FromProc[len: leng, p: XGetChar] ];
};
OldGetAtom:
PROC [h: Handle]
RETURNS [a:
ATOM] = {
r: Rope.ROPE = OldGetRope[h];
IF Rope.InlineIsEmpty[r] THEN RETURN [NIL]
ELSE RETURN [ Atom.MakeAtom[r] ];
};
OldFillTable:
PROC[h: Handle]
RETURNS [a:
ATOM] = {
c: CARDINAL = GetB[h.s];
SELECT
TRUE
FROM
c
IN [xReadTableMin..xReadTableMax] => {
imp: Imp ← h.imp;
a ← imp.oldTable[c] ← OldGetAtom[h];
};
ENDCASE => ERROR
};
b: BYTE;
IF (b ← GetB[h.s])<=xLastShortNat
THEN {
--this if statement for speed only
SELECT
TRUE
FROM
b IN [xFirstShortNat..xLastShortNat] => {RETURN [[int[b-xFirstShortNat]]]};
b
IN [xReadTableMin..xReadTableMax] => {
imp: Imp ← h.imp;
RETURN [ [atom[imp.oldTable[b]]] ]
};
b=xFillTable => {RETURN [[atom[OldFillTable[h]]]]};
ENDCASE => ERROR;
}
ELSE {
SELECT b
FROM
xPopFlag => RETURN [[pop[NIL]]];
xPushFlag =>
WITH OLDV1InternalReadToken[h]
SELECT
FROM
atom: Token.atom => RETURN [[push[atom.value]]];
ENDCASE => RETURN [[error[NIL]]];
xTwoByteCard => RETURN [[int[Get2B[h.s]]]];
xRope => RETURN [ [rope[OldGetRope[h]]] ];
xOneBytePos => RETURN [[int[GetB[h.s]]]];
xOneByteNeg => {i: INT = GetB[h.s]; RETURN [[int[-i-1]]]};
xLongInt => RETURN [[int[GetInt[h.s]]]];
xTwoByteNegative => {i: INT = Get2B[h.s]; RETURN [[int[-i-1]]]};
ENDCASE => ERROR;
}
EXITS
errorReturn => RETURN [[error[NIL]]];
endOfStreamReturn => {
TerminalIO.PutRope["** end of TokenStreams stream\n"];
RETURN [ [endOfStream[]] ]
};
END;
--features used to debug TokenStreams and other ChipNDale IO
-- debugging must be optional; the MOD operation costs 5% runtime on read!
-- we wont speak about the new...
debugMax: NAT = 10;
debugBuffer:
REF
ARRAY [0..debugMax)
OF
REF Token
← NEW[ARRAY [0..debugMax) OF REF Token];
debugIndex: [0..debugMax] ← 0;
doDebug: BOOL ← FALSE;
Remember:
PROC [token: Token] =
INLINE {
--debugging must optional; the MOD operation costs 5% runtime on read!
--we wont speak about the new...
debugBuffer[debugIndex] ← NEW[Token←token];
debugIndex ← (debugIndex+1) MOD debugMax;
};
NotDebugging:
PROC
RETURNS [
BOOL] = {
RETURN [ UserProfile.Boolean["ChipNDale.CatchLowLevelErrors", TRUE] ]
};
Read:
PUBLIC
PROC [h: Handle]
RETURNS [Token←[error[
NIL]]] = {
IF h.funny
THEN {
imp: Imp ← h.imp;
IF imp.againList#
NIL
THEN {
token: Token ← imp.againList.first;
imp.againList ← imp.againList.rest;
IF imp.againList=NIL THEN h.funny ← imp.isOldV1 OR imp.readFunny;
RETURN [token]
}
ELSE
IF imp.readFunny
THEN {
TSISkipFunny[h];
}
ELSE
IF imp.isOldV1
THEN {
token: Token ← OLDV1InternalReadToken[h];
IF doDebug THEN Remember[token];
RETURN [token]
}
ELSE ERROR
};
BEGIN
token: Token = InternalRead[h];
IF doDebug THEN Remember[token];
RETURN [token]
END;
};
ReadAgain:
PUBLIC
PROC [h: Handle, t: Token] = {
imp: Imp = h.imp;
h.funny ← TRUE;
imp.againList ← CONS[t, imp.againList];
};
Close:
PUBLIC
PROC [h: Handle, closeStream:
BOOL←
TRUE] = {
IF closeStream THEN IO.Close[h.s];
};
CommonCreate:
PROC [stream:
IO.
STREAM, stop:
REF
BOOL, isR:
BOOL←
TRUE, catcher:
PROC[Handle]←
NIL]
RETURNS [Handle] = {
IF stop=NIL THEN stop ← NEW[BOOL←FALSE];
RETURN [
NEW[HandleRec ← [
stop: stop,
funny: FALSE,
imp:
NEW[ImpRec←[
tab: LRUCache.Create[tabSize],
catcher: catcher, isR: isR
]],
stream: NIL,
s: stream,
properties: NEW[Properties.PropList ← NIL]
]]];
};
CreateReader:
PUBLIC
PROC [stream:
IO.
STREAM, stop:
REF
BOOL←
NIL, catcher:
PROC [Handle]]
RETURNS [h: Handle] = {
b: CARDINAL;
h ← CommonCreate[stream, stop, TRUE, catcher];
b ← GetB[h.s ! IO.EndOfStream => GOTO short ];
IF b#xTokenIOCode
THEN {
TerminalIO.PutRope["file not produced with TokenStreams\n"];
IF b=57 THEN TerminalIO.PutRope[" might be a SIL file\n"];
IF b=5 THEN TerminalIO.PutRope[" might be a bcd file\n"];
ERROR EncodingError
};
b ← GetB[h.s ! IO.EndOfStream => GOTO short ];
IF b#xVersionCode
THEN {
IF b=1
THEN {
imp: Imp ← h.imp;
TerminalIO.PutRope["old file version !!!\n"];
imp.oldTable ← NEW[ARRAY [1..100] OF ATOM ← ALL[NIL]];
imp.isOldV1 ← TRUE; h.funny ← TRUE;
}
ELSE {
TerminalIO.PutRope["file not produced with TokenStreams"];
ERROR EncodingError
}
};
EXITS short => {
TerminalIO.PutRope["TokenStreams stream to short"];
ERROR EncodingError
};
};
CreateWriter:
PUBLIC
PROC [stream:
IO.
STREAM, stop:
REF
BOOL←
NIL, truth:
BOOL←
TRUE]
RETURNS [h: Handle] = {
h ← CommonCreate[stream, stop, FALSE];
h.truth ← truth;
[] ←
IO.GetIndex[stream
!
IO.Error => {
TerminalIO.PutRope["TokenStreams stream needs GetIndex"];
ERROR EncodingError
}
];
PutB[h.s, xTokenIOCode];
PutB[h.s, xVersionCode];
};
Error:
PROC [h: Handle, t: Token] = {
ReadAgain[h, t];
SIGNAL EncodingError;
};
ReadInt:
PUBLIC
PROC [h: Handle]
RETURNS [
INT𡤀] = {
t: Token ← Read[h];
WITH t
SELECT
FROM
int: Token.int => RETURN [int.value];
ENDCASE => Error[h, t];
};
ReadAtom:
PUBLIC
PROC [h: Handle]
RETURNS [
ATOM←
NIL] = {
t: Token ← Read[h];
WITH t
SELECT
FROM
atom: Token.atom => RETURN [atom.value];
ENDCASE => Error[h, t];
};
ReadRope:
PUBLIC
PROC [h: Handle]
RETURNS [Rope.
ROPE←
NIL] = {
t: Token ← Read[h];
WITH t
SELECT
FROM
rope: Token.rope => RETURN [rope.value];
ENDCASE => Error[h, t];
};
ReadPop:
PUBLIC
PROC [h: Handle] = {
t: Token ← Read[h];
WITH t
SELECT
FROM
pop: Token.pop => NULL;
ENDCASE => Error[h, t];
};
ReadPush:
PUBLIC
PROC [h: Handle]
RETURNS [
ATOM←
NIL] = {
t: Token ← Read[h];
WITH t
SELECT
FROM
push: Token.push => RETURN [push.value];
ENDCASE => Error[h, t];
};
ReadPush2:
PUBLIC
PROC [h: Handle]
RETURNS [
ATOM←
NIL] = {
t: Token ← Read[h];
WITH t
SELECT
FROM
push2: Token.push2 => RETURN [push2.value];
ENDCASE => Error[h, t];
};
ReadReal:
PUBLIC
PROC [h: Handle]
RETURNS [
REAL𡤀] = {
t: Token ← Read[h];
WITH t
SELECT
FROM
real: Token.real => RETURN [real.value];
ENDCASE => Error[h, t];
};
Skip:
PUBLIC
PROC [h: Handle] = {
num, nest: INT ← 0;
DO
t: Token ← Read[h];
WITH t
SELECT
FROM
push: Token.push => {
imp: Imp ← h.imp;
IF imp.catcher=NIL THEN nest ← nest+1
ELSE {
ReadAgain[h, t];
imp.catcher[h];
}
};
push2: Token.push2 => nest ← nest+1;
pop: Token.pop => IF num=0 THEN RETURN ELSE nest ← nest-1;
streamed: Token.streamed => TSISkipFunny[h];
endOfStream: Token.endOfStream => {ReadAgain[h, t]; RETURN};
ENDCASE => NULL;
IF nest<0 THEN EXIT;
num ← num+1
ENDLOOP
};
-- STREAM STREAM STREAM STREAM STREAM
ReadStream:
PUBLIC
PROC [h: Handle]
RETURNS [s:
IO.
STREAM] = {
imp: Imp ← h.imp;
IF ~imp.isR THEN ERROR;
IF h.stream=NIL THEN h.stream ← IO.CreateStream[streamProcs: tSIProcs, streamData: h];
s ← h.stream;
};
WriteStream:
PUBLIC
PROC [h: Handle]
RETURNS [s:
IO.
STREAM] = {
imp: Imp ← h.imp;
IF imp.isR THEN ERROR;
IF h.stream=NIL THEN h.stream ← IO.CreateStream[streamProcs: tSOProcs, streamData: h];
s ← h.stream;
IF ~h.funny THEN TSOStartFunny[h];
};
tSOProcs:
REF
IO.StreamProcs ←
IO.CreateStreamProcs[
variety: $output,
class: $TokenStreamsOutStream,
putChar: TSOPutChar,
putBlock: TSOPutBlock
];
tSIProcs:
REF
IO.StreamProcs ←
IO.CreateStreamProcs[
variety: $input,
class: $TokenStreamsInStream,
getChar: TSIGetChar
];
TSIGetChar:
PROC [self:
IO.
STREAM]
RETURNS [ch:
CHAR] = {
h: Handle ← NARROW[self.streamData];
imp: Imp ← h.imp;
IF ~imp.isR THEN ERROR;
IF ~imp.readFunny THEN ERROR EncodingError;
ch ← GetC[h.s];
IF ch=escChar
THEN {
ch ← GetC[h.s];
IF ch=escChar THEN RETURN [escChar];
imp.readFunny ← FALSE;
ch ← 0c;
}
};
TSIStartFunny:
PROC [h: Handle] = {
imp: Imp ← h.imp;
IF ~imp.isR THEN ERROR;
imp.readFunny ← TRUE; h.funny ← TRUE;
};
TSISkipFunny:
PROC [h: Handle] = {
--stops and skips until the next token
c: CHAR;
imp: Imp ← h.imp;
IF ~imp.isR THEN ERROR;
IF ~imp.readFunny THEN RETURN;
DO
c ← GetC[h.s];
IF c=escChar
THEN {
c ← GetC[h.s];
IF c#escChar THEN EXIT
};
ENDLOOP;
imp.readFunny ← FALSE;
h.funny ← imp.againList#NIL OR imp.isOldV1;
};
TSOStopFunny:
PROC [h: Handle] = {
imp: Imp ← h.imp;
IF imp.isR THEN ERROR;
IF ~h.funny THEN RETURN;
PutC[h.s, escChar];
PutB[h.s, 1];
h.funny ← FALSE;
};
TSOStartFunny:
PROC [h: Handle] = {
imp: Imp ← h.imp;
IF imp.isR THEN ERROR;
IF h.funny THEN RETURN;
h.funny ← TRUE;
PutB[h.s, xStream];
};
TSOPutChar:
PROC [self:
IO.
STREAM, char:
CHAR] = {
h: Handle ← NARROW[self.streamData];
IF ~h.funny THEN TSOStartFunny[h];
IF char=escChar THEN PutC[h.s, escChar];
PutC[h.s, char];
};
TSOPutBlock:
PROC [self:
IO.
STREAM, block:
REF
READONLY
TEXT, startIndex:
NAT, count:
NAT] = {
AddNat:
PROC [a, b:
NAT]
RETURNS [
NAT] =
INLINE {
RETURN [MIN[CARDINAL[a]+CARDINAL[b], NAT.LAST]];
};
stopIndexPlusOne: NAT ← AddNat[startIndex, count];
h: Handle ← NARROW[self.streamData];
IF ~h.funny THEN TSOStartFunny[h];
FOR i:
NAT
IN [startIndex .. stopIndexPlusOne)
DO
IF block[i]=escChar THEN PutC[self, escChar];
PutC[self, block[i]];
ENDLOOP;
};
END.