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: ROPENIL;
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.