-- LooksReader.mesa
-- written by Bill Paxton, March 1981
-- last edit by Bill Paxton, May 28, 1982 10:24 am
-- LooksReader provides fast inline access to looks
-- designed for speed in reading a sequence of looks
-- either forwards or backwards
-- supports looks as defined by TextLooks.Mesa
-- create a reader by calling Create
-- reader: LooksReader.Ref ← Create[];
-- position it where you want to read by SetPosition
-- LooksReader.SetPosition[reader, runs, index];
-- where runs is TextLooks.Runs to read from
-- and index is the initial position for the reader
-- you can reposition a reader at any time
-- read its position in the following ways:
-- [runs, index] ← LooksReader.Position[reader];
-- runs ← LooksReader.GetRuns[reader];
-- index ← LooksReader.GetIndex[reader];
-- to read in ascending order (left to right)
-- initialize position before first looks to be read
-- for example, use 0 to start at beginning
-- then call Get which reads to the right and increments the position
-- looks ← LooksReader.Get[reader];
-- to read in descending order (right to left)
-- initialize position after first looks to be read
-- for example, use Size[runs] to start at end
-- then call Backwards which reads to the left and decrements the position
-- looks ← LooksReader.Backwards[reader];
-- can also get looks without changing the reader position
-- to look at the next looks that Get would return, call Peek
-- looks ← LooksReader.Peek[reader];
-- to look at what Backwards would return, call PeekBackwards
-- looks ← LooksReader.PeekBackwards[reader];
-- can intermix reading or peeking to left and right
-- don't need to reinitialize the reader to change direction
DIRECTORY
TextLooks,
RunReader;
LooksReader: CEDAR DEFINITIONS
IMPORTS RunReader =
BEGIN
Offset: TYPE = TextLooks.Offset;
Looks: TYPE = TextLooks.Looks;
noLooks: Looks = TextLooks.noLooks;
Runs: TYPE = TextLooks.Runs;
Ref: TYPE = REF Body;
Body: TYPE = PRIVATE RECORD [
rem: NAT, -- remainder of current run
len: NAT, -- length of current run
looks: Looks, -- looks for current run
runreader: RunReader.Ref -- reader for runs
];
Create: PROC [] RETURNS [Ref];
-- create a reader for chars and looks
SetPosition: PROC [reader: Ref, runs: Runs, index: Offset ← 0] =
INLINE {
-- set position of reader
reader.rem ← reader.len ← 0;
RunReader.SetPosition[reader.runreader, runs, index] };
SetIndex: PROC [reader: Ref, index: Offset ← 0] = INLINE {
reader.rem ← reader.len ← 0;
RunReader.SetIndex[reader.runreader, index] };
BackupIndex: PROC [reader: Ref, amount: Offset] = INLINE {
SetIndex[reader, GetIndex[reader]-amount] };
BumpIndex: PROC [reader: Ref, amount: Offset] = INLINE {
SetIndex[reader, GetIndex[reader]+amount] };
Position: PROC [reader: Ref] RETURNS [runs: Runs, index: Offset] =
-- return position of looks reader
INLINE { RETURN [GetRuns[reader], GetIndex[reader]] };
GetRuns: PROC [reader: Ref] RETURNS [Runs] = INLINE {
RETURN [RunReader.GetRuns[reader.runreader]] };
GetIndex: PROC [reader: Ref] RETURNS [index: Offset] = INLINE { RETURN [
RunReader.GetIndex[reader.runreader]-reader.rem]};
ReaderProc: TYPE = PROC [reader: Ref] RETURNS [Looks];
Get: ReaderProc = INLINE {
-- get looks, then increment reader location
rem: NAT;
IF (rem←reader.rem) = 0 THEN { -- go to the next run
len: Offset;
[len, reader.looks] ← RunReader.Get[reader.runreader];
IF len > LAST[NAT] THEN {
RunReader.BackupIndex[reader.runreader,len-LAST[NAT]];
len ← LAST[NAT] };
reader.len ← rem ← len};
reader.rem ← rem-1;
RETURN[reader.looks]};
Backwards: ReaderProc = INLINE {
-- decrement reader location, then get looks
rem: NAT;
IF (rem←reader.rem) >= reader.len THEN { -- go to the previous run
len: Offset;
[len, reader.looks] ← RunReader.Backwards[reader.runreader];
IF len > LAST[NAT] THEN {
RunReader.BumpIndex[reader.runreader,len-LAST[NAT]];
len ← LAST[NAT] };
reader.len ← len; rem ← 0 };
reader.rem ← rem+1;
RETURN[reader.looks]};
Peek: ReaderProc = INLINE {
-- get looks without incrementing reader location
looks: Looks;
IF reader.rem = 0 THEN
[,looks] ← RunReader.Peek[reader.runreader]
ELSE looks ← reader.looks;
RETURN [looks]};
PeekBackwards: ReaderProc = INLINE {
-- like Backwards, but doesn't change position
looks: Looks;
IF reader.rem >= reader.len THEN
[,looks] ← RunReader.PeekBackwards[reader.runreader]
ELSE looks ← reader.looks;
RETURN [looks]};
-- the following two routines implement a small cache of looks readers
-- so can avoid creating a lot of garbage
-- but don't use unless sure will not free more than once
-- no harm if fail to free (garbage collector will get it)
-- but chaos if free twice
GetLooksReader: PROC RETURNS [Ref];
FreeLooksReader: PROC[Ref];
Start: PROC; -- for initialization only
END.