-- RunReader.mesa
-- written by Bill Paxton, March 1981
-- last edit by Bill Paxton, March 23, 1981 3:50 PM

-- RunReader provides fast inline access to look runs
-- designed for speed in reading a sequence of runs
-- either forwards or backwards

-- supports looks as defined by TextLooks.Mesa

-- create a reader by RunReader.Create
-- reader: RunReader.Ref ← RunReader.Create[];

-- position it where you wanto to read by SetPosition
-- RunReader.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 current position in the following ways:
-- [runs, index] ← RunReader.Position[reader];
-- runs ← RunReader.GetRuns[reader];
-- index ← RunReader.GetIndex[reader];

-- to read in ascending order (left to right)
-- initialize position before first character to be read
-- for example, use 0 to start at beginning
-- then call Get which reads to the right and increments the position
-- [count, looks] ← RunReader.Get[reader];

-- to read in descending order (right to left)
-- initialize position after first run to be read
-- for example, use size of rope to start at end
-- then call Backwards which reads to the left and decrements the position
-- [count, looks] ← RunReader.Backwards[reader];

-- can also get a run without changing the reader position
-- to look at the next run that Get would return, call Peek
-- [count, looks] ← RunReader.Peek[reader];
-- to look at what Backwards would return, call PeekBackwards
-- [count, looks] ← RunReader.PeekBackwards[reader];

-- can intermix reading or peeking to left and right
-- don’t need to reinitialize the reader to change direction

-- if read off either end, you will get an error

DIRECTORY
TextLooks;

RunReader: DEFINITIONS
SHARES TextLooks =
BEGIN OPEN looksI:TextLooks;

-- ***** RunReader Declarations

Base: TYPE = looksI.BaseRuns;
Looks: TYPE = looksI.Looks;
noLooks: Looks = looksI.noLooks;
Runs: TYPE = looksI.Runs;

NoMoreRuns: ERROR; -- if try to read off end

Ref: TYPE = REF Body;
Body: TYPE = PRIVATE RECORD [
current: NAT ← 0, -- index of current run
first: NAT ← 0, -- index of first run to read
after: NAT ← 0, -- index beyond last run to read
base: Base, -- current array of runs
changeLooks: BOOLEAN ← FALSE, -- if need to change looks from base
remove, add: Looks ← noLooks, -- the change
runs: Runs ← NIL, -- runs that we are reading
index: Card ← 0 -- index in runs
];

Card: TYPE = LONG CARDINAL;

-- ***** RunReader Operations

Create: PROC RETURNS [Ref] = INLINE { RETURN [NEW[Body]] };

SetPosition: PROC [reader: Ref, runs: Runs, index: Card ← 0] =
INLINE {
IF runs # GetRuns[reader] OR index # GetIndex[reader] THEN
reader↑ ← [0,0,0,NIL,FALSE,noLooks,noLooks,runs,index];
};

SetIndex: PROC [reader: Ref, index: Card ← 0] = INLINE {
IF index # GetIndex[reader] THEN {
runs: Runs ← GetRuns[reader];
reader↑ ← [0,0,0,NIL,FALSE,noLooks,noLooks,runs,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] = INLINE {
-- returns the current position of the reader
RETURN [GetRuns[reader], GetIndex[reader]] };

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

GetIndex: PROC [reader: Ref] RETURNS [index: Card];

ReaderProc: TYPE = PROC [reader: Ref] RETURNS [count: Card, looks: Looks];

Get: ReaderProc = INLINE {
-- get run, then increment reader location
current: NAT;
base: Base;
IF (current←reader.current) >= reader.after THEN
{ [count, looks] ← ReadRun[reader, get]; RETURN };
IF reader.changeLooks THEN
{ [count, looks] ← ReadChangedRun[reader, get]; RETURN };
reader.current ← current+1;
base ← reader.base;
count ← IF current=0 THEN base[0].after ELSE
base[current].after-base[current-1].after;
looks ← base[current].looks };

Backwards: ReaderProc = INLINE {
-- decrement reader location, then get run
current: NAT;
base: Base;
IF (current←reader.current) <= reader.first THEN
{ [count, looks] ← ReadRun[reader, backwards]; RETURN };
IF reader.changeLooks THEN
{ [count, looks] ← ReadChangedRun[reader, backwards]; RETURN };
base ← reader.base;
count ← IF (reader.current ← current ← current-1)=0 THEN base[0].after ELSE
base[current].after-base[current-1].after;
looks ← base[current].looks };

Peek: ReaderProc = INLINE {
-- get run without incrementing reader location
current: NAT;
base: Base;
IF (current←reader.current) >= reader.after THEN
{ [count, looks] ← ReadRun[reader, peek]; RETURN };
IF reader.changeLooks THEN
{ [count, looks] ← ReadChangedRun[reader, peek]; RETURN };
base ← reader.base;
count ← IF current=0 THEN base[0].after ELSE
base[current].after-base[current-1].after;
looks ← base[current].looks };

PeekBackwards: ReaderProc = INLINE {
-- like Backwards, but doesn’t change position
current: NAT;
base: Base;
IF (current←reader.current) <= reader.first THEN
{ [count, looks] ← ReadRun[reader, backwards]; RETURN };
IF reader.changeLooks THEN
{ [count, looks] ← ReadChangedRun[reader, backwards]; RETURN };
base ← reader.base;
count ← IF (current ← current-1)=0 THEN base[0].after ELSE
base[current].after-base[current-1].after;
looks ← base[current].looks };

Mode: PRIVATE TYPE = {get, backwards, peek, peekbackwards};
ReaderEscProc: TYPE = PROC [reader: Ref, mode: Mode] RETURNS [Card, Looks];
ReadRun, ReadChangedRun: PRIVATE ReaderEscProc;

END.