-- LooksReader.mesa
-- written by Bill Paxton, March 1981
-- last edit by Bill Paxton, March 25, 1981 10:18 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 TiogaLooks.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
Inline,
TextLooks,
RunReader;

LooksReader: DEFINITIONS
IMPORTS RunReader, Inline =
BEGIN OPEN looksI:TextLooks, readerI:RunReader;

Card: TYPE = LONG CARDINAL;
Looks: TYPE = looksI.Looks;
noLooks: Looks = looksI.noLooks;
Runs: TYPE = looksI.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: readerI.Ref -- reader for runs
];

Create: PROC [] RETURNS [Ref] = INLINE {
-- create a reader for chars and looks
RETURN [NEW[Body ← [0,0,noLooks,NEW[readerI.Body]]]] };

SetPosition: PROC [reader: Ref, runs: Runs, index: Card ← 0] =
INLINE {
-- set position of reader
reader.rem ← reader.len ← 0;
readerI.SetPosition[reader.runreader, runs, index] };

SetIndex: PROC [reader: Ref, index: Card ← 0] = INLINE {
reader.rem ← reader.len ← 0;
readerI.SetIndex[reader.runreader, index] };

BackupIndex: PROC [reader: Ref, amount: Card] = INLINE {
SetIndex[reader, GetIndex[reader]-amount] };

BumpIndex: PROC [reader: Ref, amount: Card] = INLINE {
SetIndex[reader, GetIndex[reader]+amount] };

Position: PROC [reader: Ref] RETURNS [runs: Runs, index: Card] =
-- return position of looks reader
INLINE { RETURN [GetRuns[reader], GetIndex[reader]] };

GetRuns: PROC [reader: Ref] RETURNS [Runs] = INLINE {
RETURN [readerI.GetRuns[reader.runreader]] };

GetIndex: PROC [reader: Ref] RETURNS [index: Card] = INLINE { RETURN [
readerI.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: Card;
[len, reader.looks] ← readerI.Get[reader.runreader];
IF len > LAST[NAT] THEN {
readerI.BackupIndex[reader.runreader,len-LAST[NAT]];
len ← LAST[NAT] };
reader.len ← rem ← LOOPHOLE[Inline.LowHalf[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: Card;
[len, reader.looks] ← readerI.Backwards[reader.runreader];
IF len > LAST[NAT] THEN {
readerI.BumpIndex[reader.runreader,len-LAST[NAT]];
len ← LAST[NAT] };
reader.len ← LOOPHOLE[Inline.LowHalf[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] ← readerI.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] ← readerI.PeekBackwards[reader.runreader]
ELSE looks ← reader.looks;
RETURN [looks]};

END.