IPEncodingImpl.mesa
Last edited by:
Doug Wyatt, April 21, 1983 2:28 pm
DIRECTORY
FileIO USING [Open],
IO USING [Close, PutRope, STREAM],
IPBasic USING [PrimitiveOrSymbol, Rational],
IPEncoding USING [Reader, ReaderProcs, ReadProc, Token, TokenType, Version, WriteProc, Writer, WriterProcs],
Rope USING [ActionType, Concat, Equal, Map, ROPE, Substr],
RopeIO USING [FromFile],
RopeReader USING [Backwards, Create, Get, GetIndex, GetRope, Peek, Ref, SetIndex, SetPosition];
IPEncodingImpl:
CEDAR
PROGRAM
IMPORTS FileIO, IO, Rope, RopeIO, RopeReader
EXPORTS IPEncoding
= BEGIN OPEN IPEncoding, IPBasic;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Node: TYPE = REF NodeRep;
NodeRep:
TYPE =
RECORD[
next: Node,
encoding: ROPE, -- name of the encoding
version: Version, -- version number
read: ReadProc,
write: WriteProc
];
encodingList: Node ← NIL; -- head of the list of registered encodings
Error: PUBLIC ERROR[message: ROPE] = CODE;
closedReaderProcs:
REF
READONLY ReaderProcs =
NEW[ReaderProcs = [
GetToken: ClosedGetToken,
GetInt: ClosedGetInt,
GetRational: ClosedGetRational,
GetReal: ClosedGetReal,
GetRope: ClosedGetRope
]];
closedWriterProcs:
REF
READONLY WriterProcs =
NEW[WriterProcs = [
PutOp: ClosedPutOp,
PutInt: ClosedPutInt,
PutRational: ClosedPutRational,
PutReal: ClosedPutReal,
PutRope: ClosedPutRope
]];
ClosedGetToken:
PROC[self: Reader, index:
INT]
RETURNS[Token] = {
ERROR Error["closed"] };
ClosedGetInt:
PROC[self: Reader, token: Token]
RETURNS[
INT] = {
ERROR Error["closed"] };
ClosedGetRational:
PROC[self: Reader, token: Token]
RETURNS[Rational] = {
ERROR Error["closed"] };
ClosedGetReal:
PROC[self: Reader, token: Token]
RETURNS[
REAL] = {
ERROR Error["closed"] };
ClosedGetRope:
PROC[self: Reader, token: Token]
RETURNS[
ROPE] = {
ERROR Error["closed"] };
ClosedPutOp:
PROC[self: Writer, op: PrimitiveOrSymbol] = {
ERROR Error["closed"] };
ClosedPutInt:
PROC[self: Writer, i:
INT] = {
ERROR Error["closed"] };
ClosedPutRational:
PROC[self: Writer, r: Rational] = {
ERROR Error["closed"] };
ClosedPutReal:
PROC[self: Writer, r:
REAL] = {
ERROR Error["closed"] };
ClosedPutRope:
PROC[self: Writer, type: TokenType, text:
ROPE] = {
ERROR Error["closed"] };
OpenReader:
PUBLIC
PROC[fileName:
ROPE]
RETURNS[reader: Reader] = {
Prepare to read an Interpress master with the given file name.
Parse the header to verify that it is a valid master,
and return a reader appropriate for its encoding.
encoding: ROPE ← NIL;
version: Version ← [0,0];
ropeReader: RopeReader.Ref ← NIL;
rope: ROPE ← RopeIO.FromFile[fileName];
ropeReader ← RopeReader.Create[];
ropeReader.SetPosition[rope, 0];
[encoding, version] ← ParseHeader[ropeReader];
RETURN[CreateReader[encoding, ropeReader]];
};
CreateReader:
PUBLIC
PROC[encoding:
ROPE, ropeReader: RopeReader.Ref]
RETURNS[Reader] = {
Open a reader for a specified encoding.
node: Node = Find[encoding];
IF node=NIL THEN ERROR Error[Rope.Concat["unknown encoding: ", encoding]]
ELSE { reader: Reader = node.read[ropeReader]; RETURN[reader] };
};
herald: ROPE = "Interpress/";
ParseHeader:
PROC[reader: RopeReader.Ref]
RETURNS[encoding: ROPE, version: Version] = {
SkipIf:
PROC[char:
CHAR] = {
IF reader.Peek[]=char THEN [] ← reader.Get[];
};
Match:
PROC[rope:
ROPE]
RETURNS[
BOOL] = {
Action: Rope.ActionType = { RETURN[reader.Get[]#c] };
RETURN[NOT Rope.Map[base: rope, action: Action]];
};
SkipTo:
PROC[char:
CHAR] = {
DO IF reader.Get[]=char THEN EXIT ENDLOOP;
};
BackOver:
PROC[char:
CHAR] = {
IF reader.Backwards[]#char THEN ERROR;
};
BackNumber:
PROC[end:
CHAR]
RETURNS[
NAT] = {
f: NAT ← 0;
n: NAT ← 0;
DO c:
CHAR = reader.Backwards[];
IF c
IN['0..'9]
THEN {
IF f=0 THEN f ← 1
ELSE IF f<1000 THEN f ← 10*f
ELSE ERROR Error["version number too big"];
n ← n+(c-'0)*f;
}
ELSE IF c=end THEN EXIT
ELSE ERROR Error["malformed version number"];
ENDLOOP;
RETURN[n];
};
enc, stop: INT ← 0;
GetIndex: PROC RETURNS[INT] = { RETURN[reader.GetIndex[]] };
SetIndex: PROC[index: INT] = { reader.SetIndex[index] };
SubString:
PROC[start, stop:
INT]
RETURNS[
ROPE] = {
rope: ROPE = reader.GetRope[];
RETURN[Rope.Substr[rope, start, stop-start]];
};
SkipIf['\n]; -- ignore possible cr inserted by Tioga
IF NOT Match[herald] THEN ERROR Error["not an Interpress master"];
enc ← GetIndex[]; -- index of first char of encoding name
SkipTo[' ];
stop ← GetIndex[]; -- index of first char following header
BackOver[' ];
version.minor ← BackNumber['.];
version.major ← BackNumber['/];
encoding ← SubString[enc, GetIndex[]];
SetIndex[stop];
RETURN[encoding, version];
};
CloseReader:
PUBLIC
PROC[self: Reader] = {
self.procs ← closedReaderProcs;
self.ropeReader ← NIL;
self.buffer ← NIL;
};
OpenWriter:
PUBLIC
PROC[name:
ROPE, encoding:
ROPE]
RETURNS[Writer] = {
Prepare to write an Interpress master with the given file name.
Write the header and return a writer for the given encoding.
s: STREAM = FileIO.Open[name, overwrite];
s.PutRope[herald]; s.PutRope[encoding];
s.PutRope["/2.0 "]; -- *** cheat, for now ***
RETURN[CreateWriter[encoding, s]];
};
CreateWriter:
PUBLIC
PROC[encoding:
ROPE, stream:
STREAM]
RETURNS[Writer] = {
Open a writer for a specified encoding; start writing wherever the stream is positioned.
node: Node = Find[encoding];
IF node=NIL THEN ERROR Error[Rope.Concat["unknown encoding: ", encoding]]
ELSE RETURN[node.write[stream]];
};
CloseWriter:
PUBLIC
PROC[self: Writer] = {
self.procs ← closedWriterProcs;
self.stream.Close[];
};
Find:
PROC[encoding:
ROPE]
RETURNS[Node] = {
FOR node: Node ← encodingList, node.next
UNTIL node=
NIL
DO
IF encoding.Equal[node.encoding] THEN RETURN[node];
ENDLOOP;
RETURN[NIL];
};
Register:
PUBLIC
PROC[encoding:
ROPE, version: Version,
read: ReadProc, write: WriteProc] = {
IF Find[encoding]=
NIL
THEN {
node: Node =
NEW[NodeRep ← [next: encodingList,
encoding: encoding, version: version, read: read, write: write]];
encodingList ← node;
}
ELSE ERROR Error["encoding registered twice"];
};
END.