-- RunReader.mesa
-- written by Bill Paxton, March 1981
-- last edit by Bill Paxton, December 22, 1981 2:02 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, TextLooksSupport;
RunReader: CEDAR DEFINITIONS
IMPORTS TextLooksSupport
SHARES TextLooks =
BEGIN
-- ***** RunReader Declarations
Base: TYPE = TextLooks.BaseRuns;
Looks: TYPE = TextLooks.Looks;
noLooks: Looks = TextLooks.noLooks;
Runs: TYPE = TextLooks.Runs;
Offset: TYPE = TextLooks.Offset;
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: BOOLEANFALSE, -- if need to change looks from base
remove, add: Looks ← noLooks, -- the change
runs: Runs ← NIL, -- runs that we are reading
index: Offset ← 0 -- index in runs
];
-- ***** RunReader Operations
Create: PROC RETURNS [Ref];
SetPosition: PROC [reader: Ref, runs: Runs, index: Offset ← 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: Offset ← 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: 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] = 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: Offset];
ReaderProc: TYPE = PROC [reader: Ref] RETURNS [count: Offset, 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 };
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;
IF reader.changeLooks THEN
looks ← TextLooksSupport.ModifyLooks[looks,reader.remove,reader.add] };
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 };
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;
IF reader.changeLooks THEN
looks ← TextLooksSupport.ModifyLooks[looks,reader.remove,reader.add] };
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 };
base ← reader.base;
count ← IF current=0 THEN base[0].after ELSE
base[current].after-base[current-1].after;
looks ← base[current].looks;
IF reader.changeLooks THEN
looks ← TextLooksSupport.ModifyLooks[looks,reader.remove,reader.add] };
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 };
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;
IF reader.changeLooks THEN
looks ← TextLooksSupport.ModifyLooks[looks,reader.remove,reader.add] };
MergedGet: ReaderProc;
-- merges adjacent runs with same looks
-- regular Get may give same looks on successive calls
-- e.g., if Concat[A,B] produces in a concat-node
-- and the last run in A has the same looks as the first in B
-- Get will first return last run from A, then first from B
-- MergedGet will combine these into a single result1
Mode: PRIVATE TYPE = {get, backwards, peek, peekbackwards};
ReaderEscProc: TYPE = PROC [reader: Ref, mode: Mode] RETURNS [Offset, Looks];
ReadRun: PRIVATE ReaderEscProc;
-- here are some operations making use of readers
Equal: PROC [r1,r2: Runs, rdr1,rdr2: Ref] RETURNS [BOOLEAN] = INLINE {
-- uses readers rdr1 and rdr2 to read runs r1 and r2 to test for equality
RETURN [EqSubstrs[r1,r2,0,0,LAST[Offset],rdr1,rdr2]] };
EqSubstrs: PROC [r1,r2: Runs, start1,start2,len: Offset, rdr1,rdr2: Ref]
RETURNS [BOOLEAN];
-- uses readers rdr1 and rdr2 to read runs r1 and r2 to test for equality
-- returns TRUE if r1 starting at start1 is same as
-- len looks of r2 starting at start2
-- returns FALSE if not enough looks available in either
-- i.e., if start1+len > size[r1] or start2+len > size[r2]
-- the next routines provide caches of readers
-- so can avoid creating a lot of garbage
GetRunReader: PROC RETURNS [reader: Ref];
FreeRunReader: PROC [reader: Ref];
-- ***** Initialization
Start: PROC; -- for initialization only
END.