<> <> <> 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] = { <> <> <> 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] = { <> 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] = { <> <> 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] = { <> 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.