CreateNodeImpl.mesa
Last edited by Paxton - August 26, 1983 1:21 pm
DIRECTORY
Atom USING [MakeAtom],
NameSymbolTable USING [MakeName],
NodeProps USING [DoSpecs, GetProp, PutProp],
CreateNode,
Rope USING [ROPE],
RopeEdit USING [Substr],
RopeReader USING [Backwards, BumpIndex, FreeRopeReader, Get, GetIndex, GetRopeReader, GetString, Peek, Position, Ref, SetIndex, SetPosition],
T2FileOps,
TiogaLooks USING [BaseRuns, Look, Looks, noLooks, Runs],
TiogaLooksOps USING [Concat],
TiogaLooksSupport USING [Short, NewBase],
TiogaBasicClass USING [BasicClass],
TiogaBranchClass USING [BranchClass, branchChildrenAtom, branchContentsAtom, defaultBranchClass, Lookup],
TiogaItemClass USING [ItemClass],
TiogaNode USING [BasicClassID, ItemClassID, Name, Offset, Ref, RefBranchNode, RefItemNode, RefTextNode, RefBoxNode, RefBasicNode, RefListNode],
TiogaNodeOps USING [FetchBasicClass, FetchItemClass, LookupBasicID, LookupItemID, NewBasicNode, NewBranchNode, NewBoxNode, NewListNode, NewTextNode],
TiogaTreeOps USING [LastSibling, Parent];
CreateNodeImpl: CEDAR MONITOR
IMPORTS Atom, NameSymbolTable, NodeProps, RopeEdit, RopeReader, TiogaBranchClass, TiogaLooksOps, TiogaLooksSupport, TiogaNodeOps, TiogaTreeOps
EXPORTS CreateNode SHARES TiogaNode = BEGIN OPEN CreateNode;
fileFormatError: PUBLIC SIGNAL = CODE; -- raised for all format faults
noBranchParseInfo: PUBLIC SIGNAL = CODE; -- raised when client does not supply BranchInfo
CreateFromInfo: PUBLIC PROC [rope: Rope.ROPE, textStart, textLen, controlStart, controlLen: INT]
RETURNS [node: TiogaNode.Ref] = {
textBuffer: REF TEXT;
control: RopeReader.Ref ← RopeReader.GetRopeReader[];
text: RopeReader.Ref ← RopeReader.GetRopeReader[];
info: SpanInfo;
wholeTree: BOOLTRUE;
[textBuffer, info] ← GetStuff[wholeTree];
info.externalRepRope ← rope;
info.charsStart ← textStart;
info.charsLen ← textLen;
info.ctrlStart ← controlStart;
info.ctrlLen ← controlLen;
node ← CIR[NIL, info, wholeTree, textBuffer, control, text];
RopeReader.FreeRopeReader[control];
RopeReader.FreeRopeReader[text];
FreeStuff[textBuffer, info] };
CreateInternalRep: PUBLIC PROC [br: TiogaNode.RefBranchNode] = {
The primary client interface to the tree parser
brInfo: SpanInfo ← NARROW[NodeProps.GetProp[br, $BranchInfo]];
copy: SpanInfo;
textBuffer: REF TEXT;
control: RopeReader.Ref ← RopeReader.GetRopeReader[];
text: RopeReader.Ref ← RopeReader.GetRopeReader[];
wholeTree: BOOLTRUE;
parent: TiogaNode.Ref;
IF brInfo=NIL THEN ERROR noBranchParseInfo;
IF (parent ← TiogaTreeOps.Parent[br])=NIL OR (parent ← TiogaTreeOps.Parent[parent])=NIL
OR (parent ← TiogaTreeOps.Parent[parent])=NIL THEN wholeTree ← FALSE;
If at least 3 levels deep, do the whole thing. Else keep on doing incremental parsing.
Needless to say, this heuristic can be changed if a better one comes along.
[textBuffer, copy] ← GetStuff[wholeTree];
IF wholeTree THEN { copy^ ← brInfo^; brInfo ← copy };
If the whole tree is to be cracked then there is no need to plant information blocks on each branch. Instead, a copy of the initial branch info is created and passed around the tree, the information being modified to suit the branch about to be cracked.
[] ← CIR[br, brInfo, wholeTree, textBuffer, control, text];
RopeReader.FreeRopeReader[control];
RopeReader.FreeRopeReader[text];
FreeStuff[textBuffer, copy] };
CIR: PROC [br: TiogaNode.RefBranchNode, brInfo: SpanInfo,
wholeTree: BOOL, textBuffer: REF TEXT, control, text: RopeReader.Ref]
RETURNS [node: TiogaNode.Ref] = {
NextOp: PROC RETURNS [T2FileOps.Op] = INLINE { RETURN[RopeReader.Get[control]] };
ReadB32: PROCEDURE [] RETURNS [n: INT] = {
Read a 24 bit number packed as 1..4 bytes using the given character reader. Each character provides 7 data bits and a boolean indicating whether or not it is the last in the sequence.
first, second, fourth: T2FileOps.LengthByte;
third: T2FileOps.ThirdByte;
card: T2FileOps.IntBytes;
first ← LOOPHOLE[RopeReader.Backwards[control]];
card.first ← first.data;
IF ~first.others THEN RETURN [LOOPHOLE[card]]; -- value within 7 bits so terminate
second ← LOOPHOLE[RopeReader.Backwards[control]];
card.second ← second.data;
IF ~second.others THEN RETURN [LOOPHOLE[card]]; -- value within 14 bits so terminate
third ← LOOPHOLE[RopeReader.Backwards[control]];
card.thirdBottom ← third.dataBottom;
card.thirdTop ← third.dataTop;
IF ~third.others THEN RETURN [LOOPHOLE[card]]; -- value within 21 bits so terminate
fourth ← LOOPHOLE[RopeReader.Backwards[control]];
card.fourth ← fourth.data;
RETURN [LOOPHOLE[card]] }; -- full 28 bit value
ReadF32: PROCEDURE [] RETURNS [n: INT] = {
Read a 24 bit number packed as 1..4 bytes using the given character reader. Each character provides 7 data bits and a boolean indicating whether or not it is the last in the sequence.
first, second, fourth: T2FileOps.LengthByte;
third: T2FileOps.ThirdByte;
card: T2FileOps.IntBytes;
first ← LOOPHOLE[RopeReader.Get[control]];
card.first ← first.data;
IF ~first.others THEN RETURN [LOOPHOLE[card]]; -- value within 7 bits so terminate
second ← LOOPHOLE[RopeReader.Get[control]];
card.second ← second.data;
IF ~second.others THEN RETURN [LOOPHOLE[card]]; -- value within 14 bits so terminate
third ← LOOPHOLE[RopeReader.Get[control]];
card.thirdBottom ← third.dataBottom;
card.thirdTop ← third.dataTop;
IF ~third.others THEN RETURN [LOOPHOLE[card]]; -- value within 21 bits so terminate
fourth ← LOOPHOLE[RopeReader.Get[control]];
card.fourth ← fourth.data;
RETURN [LOOPHOLE[card]] }; -- full 28 bit value
ReadAtom: PROCEDURE [] RETURNS [ATOM] = INLINE {
Change this to be like ReadFormat. Want to avoid the NEW ROPE unless it is a new atom.
RETURN[Atom.MakeAtom[ReadRope[ReadF32[], control]]] };
ReadFormat: PROC RETURNS [n: TiogaNode.Name] = {
len: TiogaNode.Offset ← ReadF32[];
IF len>LAST[NAT] THEN ERROR; -- too big for a format name
IF len > textBuffer.maxLength THEN textBuffer ← NEW[TEXT[len]];
textBuffer.length ← 0;
textBuffer.length ← RopeReader.GetString[control, textBuffer, len];
n ← NameSymbolTable.MakeName[textBuffer] };
ReadContents: PROCEDURE [parent: TiogaNode.Ref, itemClassInfo: TiogaItemClass.ItemClass ← NIL]
RETURNS [head: TiogaNode.Ref] = {
previous, node: TiogaNode.Ref;
IF op=T2FileOps.STARTBR THEN DO -- branches
current: TiogaNode.RefBranchNode ← TiogaNodeOps.NewBranchNode[];
IF previous=NIL THEN head ← current ELSE { previous.next ← current; previous.last ← FALSE };
previous ← current;
[] ← CIR[current, NIL, TRUE, textBuffer, control, text];
SELECT RopeReader.Peek[control] FROM
T2FileOps.NODATA, T2FileOps.CLASSDATA, T2FileOps.ENDOFFILE => {
[] ← NextOp[]; current.next ← parent; RETURN };
ENDCASE;
ENDLOOP;
IF op=T2FileOps.ITEMCONTENTSPECS THEN { -- itemClassInfo supplied by caller
Insert: PROC [n: TiogaNode.Ref, previous: TiogaNode.Ref ← NIL] = {
IF head=NIL THEN head ← n
ELSE {
IF previous=NIL THEN previous ← TiogaTreeOps.LastSibling[head];
previous.next ← n; previous.last ← FALSE };
node ← n; n.last ← TRUE };
rope: Rope.ROPE;
start, len: INT;
[rope, start, len] ← ReadSpecs[];
op ← NextOp[]; -- the op after the specs
IF itemClassInfo.getContents=NIL THEN -- trouble. save the specs
NodeProps.PutProp[parent, TiogaItemClass.itemContentsAtom, RopeEdit.Substr[rope, start, len]]
ELSE itemClassInfo.getContents[TiogaNodeOps.NarrowToItemNode[parent], rope, start, len, Insert] }
ERROR } -- put in the real thing once add getContents to item class
ELSE DO
SELECT op FROM
>= T2FileOps.text => SELECT op FROM -- text, list, box, or basic
<= T2FileOps.lastText => node ← ReadTextNode[op-T2FileOps.text];
<= T2FileOps.lastList => node ← ReadNonTextNode[op-T2FileOps.list, list];
<= T2FileOps.lastBox => node ← ReadNonTextNode[op-T2FileOps.box, box];
<= T2FileOps.lastBasic => node ← ReadNonTextNode[op-T2FileOps.basic, basic];
ENDCASE => EXIT;
ENDCASE => EXIT;
IF previous=NIL THEN head ← node
ELSE { previous.next ← node; previous.last ← FALSE };
previous ← node;
op ← NextOp[];
ENDLOOP;
IF node#NIL THEN node.next ← parent };
KindOfNonTextNode: TYPE = { box, list, basic };
ReadNonTextNode: PROCEDURE [offset: CARDINAL, kind: KindOfNonTextNode]
RETURNS [new: TiogaNode.Ref] = {
isComment, doFmt, doClass: BOOLFALSE;
itemClassID: TiogaNode.ItemClassID;
itemClassInfo: TiogaItemClass.ItemClass;
basicClassID: TiogaNode.BasicClassID;
classData: Rope.ROPE;
ls: TiogaNode.RefListNode;
bx: TiogaNode.RefBoxNode;
bs: TiogaNode.RefBasicNode;
{ OPEN T2FileOps;
IF offset >= commentBit*2 THEN ERROR fileFormatError;
IF offset >= commentBit THEN { isComment ← TRUE; offset ← offset-commentBit };
IF offset >= formatBit THEN { doFmt ← TRUE; offset ← offset-formatBit };
IF offset > 0 THEN doClass ← TRUE };
SELECT kind FROM
box => {
new ← bx ← TiogaNodeOps.NewBoxNode[
itemClassID ← TiogaNodeOps.LookupItemID[IF doClass THEN ReadAtom[] ELSE $Box]];
itemClassInfo ← TiogaNodeOps.FetchItemClass[itemClassID] };
list => {
new ← ls ← TiogaNodeOps.NewListNode[
itemClassID ← TiogaNodeOps.LookupItemID[IF doClass THEN ReadAtom[] ELSE $List]];
itemClassInfo ← TiogaNodeOps.FetchItemClass[itemClassID] };
basic => new ← bs ← TiogaNodeOps.NewBasicNode[
basicClassID ← TiogaNodeOps.LookupBasicID[IF doClass THEN ReadAtom[] ELSE $Basic]];
ENDCASE => ERROR;
new.comment ← isComment;
IF doFmt THEN new.format ← ReadFormat[];
op ← NextOp[];
ReadProperties[new];
SELECT kind FROM
box => bx.contents ← ReadContents[bx, itemClassInfo];
list => ls.contents ← ReadContents[ls, itemClassInfo];
basic => NULL;
ENDCASE => ERROR;
SELECT op FROM
T2FileOps.NODATA => NULL;
T2FileOps.CLASSDATA => classData ← ReadRope[ReadF32[], control];
ENDCASE => ERROR;
SELECT kind FROM
box => {
IF itemClassInfo.set=NIL THEN bx.data ← classData ELSE
itemClassInfo.set[bx, $Restore, classData, FALSE] };
list => {
IF itemClassInfo.set=NIL THEN ls.data ← classData ELSE
itemClassInfo.set[ls, $Restore, classData, FALSE] };
basic => {
basicClassInfo: TiogaBasicClass.BasicClass ← TiogaNodeOps.FetchBasicClass[basicClassID];
IF basicClassInfo.set=NIL THEN bs.data ← classData ELSE
basicClassInfo.set[bs, $Restore, classData, FALSE] };
ENDCASE => ERROR };
ReadSpecs: PROCEDURE RETURNS [rope: Rope.ROPE, pos, len: INT] = {
len ← ReadF32[];
[rope, pos] ← RopeReader.Position[control];
RopeReader.SetIndex[control, pos+len] };
ReadProp: PROCEDURE [n: TiogaNode.Ref] = {
key: ATOM ← ReadAtom[];
rope: Rope.ROPE;
len, pos: INT;
val: REF;
[rope, pos, len] ← ReadSpecs[];
val ← NodeProps.DoSpecs[key, rope, pos, len, n];
IF val # NIL THEN NodeProps.PutProp[n, key, val] };
ReadRope: PROCEDURE [len: INT, rdr: RopeReader.Ref] RETURNS [Rope.ROPE] = {
Read a rope using given reader - reader is assumed to be positioned ready to read first character
rope: Rope.ROPE;
pos: TiogaNode.Offset;
[rope, pos] ← RopeReader.Position[rdr]; -- find out where the start is
RopeReader.SetIndex[rdr, pos+len]; -- step reader over it
RETURN [RopeEdit.Substr[rope, pos, len]] -- return it
};
ReadRuns: PROCEDURE [] RETURNS [lookRuns: TiogaLooks.Runs] = {
pos: TiogaNode.Offset ← 0;
numRuns: TiogaNode.Offset ← ReadF32[];
WHILE numRuns > 0 DO
num: NAT ← TiogaLooksSupport.Short[MIN[numRuns,LAST[NAT]]];
baseRuns: TiogaLooks.BaseRuns ← TiogaLooksSupport.NewBase[num];
len: TiogaNode.Offset ← pos;
numRuns ← numRuns-num;
FOR i: NAT IN [0..num) DO -- run
looks: TiogaLooks.Looks ← TiogaLooks.noLooks;
lookLen: TiogaNode.Offset;
op: CHAR;
IF (op ← NextOp[]) # T2FileOps.NOLOOKS THEN DO
SELECT op FROM
>= T2FileOps.lastLooksFirst => {
c: CHAR ← 'a+(op - T2FileOps.lastLooksFirst);
IF op > T2FileOps.lastLooksLast THEN SIGNAL fileFormatError;
looks[c] ← TRUE; EXIT };
>= T2FileOps.addLooksFirst => {
c: CHAR ← 'a+(op - T2FileOps.addLooksFirst);
looks[c] ← TRUE };
ENDCASE => SIGNAL fileFormatError;
op ← NextOp[]; ENDLOOP;
lookLen ← ReadF32[];
baseRuns[i] ← [pos←pos+lookLen, looks];
ENDLOOP;
lookRuns ← IF lookRuns=NIL THEN baseRuns ELSE
TiogaLooksOps.Concat[lookRuns,baseRuns,len,pos-len];
ENDLOOP }; 
ReadTextNode: PROCEDURE [offset: CARDINAL] RETURNS [item: TiogaNode.RefTextNode] = {
isComment, doRuns, doFmt, doClass, doProps: BOOLEANFALSE;
rdr: RopeReader.Ref;
{ OPEN T2FileOps;
IF offset >= propsBit*2 THEN ERROR fileFormatError;
IF offset >= propsBit THEN { doProps ← TRUE; offset ← offset-propsBit };
IF offset >= runsBit THEN { doRuns ← TRUE; offset ← offset-runsBit };
IF offset >= commentBit THEN { isComment ← TRUE; offset ← offset-commentBit };
IF offset >= formatBit THEN { doFmt ← TRUE; offset ← offset-formatBit };
IF offset > 0 THEN doClass ← TRUE };
item ← TiogaNodeOps.NewTextNode[
TiogaNodeOps.LookupItemID[IF doClass THEN ReadAtom[] ELSE $Text]];
item.comment ← isComment;
IF doFmt THEN item.format ← ReadFormat[];
IF doRuns THEN item.runs ← ReadRuns[];
rdr ← IF isComment THEN control ELSE text;
item.rope ← ReadRope[ReadF32[], rdr];
RopeReader.BumpIndex[rdr, 1]; -- skip over CR
IF doProps THEN {
ReadProperties[item];
IF op # T2FileOps.ENDPROPS THEN ERROR fileFormatError;
op ← NextOp[] }};
ReadProperties: PROC [n: TiogaNode.Ref] = {
DO SELECT op FROM
T2FileOps.PROPERTY => { ReadProp[n]; op ← NextOp[] };
T2FileOps.APPLICATION => { n.templateInfo ← application; op ← NextOp[] };
T2FileOps.FORMAL => { n.templateInfo ← formal; op ← NextOp[] };
ENDCASE => RETURN;
ENDLOOP };
offset: CARDINAL;
isComment, doFmt, doClass: BOOLEANFALSE;
branchClassInfo: TiogaBranchClass.BranchClass;
nKids: INT ← 0;
prev, current: TiogaNode.RefBranchNode;
kidCtrlStart, kidTextStart: TiogaNode.Offset;
op, childOp: T2FileOps.Op;
IF brInfo # NIL THEN { -- otherwise, assume readers already set at correct positions
RopeReader.SetPosition[text, brInfo.externalRepRope, brInfo.charsStart];
RopeReader.SetPosition[control, brInfo.externalRepRope, brInfo.ctrlStart] };
IF br=NIL THEN RETURN [ReadContents[NIL]]; -- we're being called to read a non-branch node
op ← NextOp[]; -- start of the branch info
IF op NOT IN [T2FileOps.branch..T2FileOps.lastBranch] THEN
ERROR fileFormatError; -- supposed to be a branch
offset ← op-T2FileOps.branch;
{ OPEN T2FileOps;
IF offset >= commentBit THEN { isComment ← TRUE; offset ← offset-commentBit };
IF offset >= formatBit THEN { doFmt ← TRUE; offset ← offset-formatBit };
IF offset > 0 THEN doClass ← TRUE };
IF doClass THEN
NodeProps.PutProp[br, $BranchClass, branchClassInfo ← TiogaBranchClass.Lookup[ReadAtom[]]]
ELSE branchClassInfo ← TiogaBranchClass.defaultBranchClass;
IF doFmt THEN br.format ← ReadFormat[];
br.comment ← isComment;
op ← NextOp[];
ReadProperties[br];
IF op=T2FileOps.BRANCHCONTENTSPECS THEN { -- contents saved in class dependent manner
Insert: PROC [item: TiogaNode.RefItemNode, previous: TiogaNode.RefItemNode ← NIL] = {
IF br.contents=NIL THEN br.contents ← item
ELSE {
IF previous=NIL THEN previous ← NARROW[TiogaTreeOps.LastSibling[br.contents]];
previous.next ← item; previous.last ← FALSE };
item.next ← br; item.last ← TRUE };
rope: Rope.ROPE;
start, len: INT;
[rope, start, len] ← ReadSpecs[];
op ← NextOp[]; -- the op after the specs
IF branchClassInfo.setContents=NIL THEN -- trouble. save the specs
NodeProps.PutProp[br, TiogaBranchClass.branchContentsAtom, RopeEdit.Substr[rope, start, len]]
ELSE branchClassInfo.setContents[br, rope, start, len, Insert] }
ELSE br.contents ← NARROW[ReadContents[br]]; -- narrow to item node
IF (childOp ← op)=T2FileOps.NOCHILD THEN { br.internalRepCreated ← TRUE; RETURN };
IF childOp=T2FileOps.CHILDSPECS THEN { -- children saved in class dependent manner
Insert: PROC [child: TiogaNode.RefBranchNode, previous: TiogaNode.RefBranchNode ← NIL] = {
IF br.child=NIL THEN br.child ← child
ELSE {
IF previous=NIL THEN previous ← NARROW[TiogaTreeOps.LastSibling[br.child]];
previous.next ← child; previous.last ← FALSE };
child.next ← br; child.last ← TRUE };
rope: Rope.ROPE;
start, len: INT;
[rope, start, len] ← ReadSpecs[];
IF branchClassInfo.setChildren=NIL THEN -- trouble. save the specs
NodeProps.PutProp[br, TiogaBranchClass.branchChildrenAtom, RopeEdit.Substr[rope, start, len]]
ELSE branchClassInfo.setChildren[br, rope, start, len, Insert];
br.internalRepCreated ← TRUE;
RETURN };
IF childOp=T2FileOps.STARTC OR
((brInfo=NIL OR wholeTree) AND childOp=T2FileOps.ENDITEMS) THEN {
DO -- parse a child each time through loop
current ← TiogaNodeOps.NewBranchNode[];
IF prev=NIL THEN br.child ← current ELSE { prev.next ← current; prev.last ← FALSE };
current.next ← br; prev ← current;
[] ← CIR[current, NIL, TRUE, textBuffer, control, text];
nKids ← nKids+1;
IF RopeReader.Peek[control]=T2FileOps.ENDC THEN { [] ← NextOp[]; EXIT };
ENDLOOP;
IF childOp=T2FileOps.ENDITEMS THEN { --read and discard ctrl&data lengths and num children
op ← NextOp[]; -- actually is the first byte of number info
THROUGH [0..nKids*2) DO -- tricky because numbers written backwards on the file
read a single byte with top bit 0; then read sequence of bytes with top bit 1
can read until see byte with top bit 0 because quaranteed to have another number following
byte: T2FileOps.LengthByte ← LOOPHOLE[op];
IF byte.others THEN ERROR;
DO
byte ← LOOPHOLE[op ← NextOp[]];
IF ~byte.others THEN EXIT; -- this is the last byte of the next number
ENDLOOP;
ENDLOOP;
Now read as many bytes as needed to store num children (have already read first one)
UNTIL nKids < 128 DO [] ← NextOp[]; nKids ← nKids/128; ENDLOOP };
br.internalRepCreated ← TRUE; RETURN };
IF childOp # T2FileOps.ENDITEMS THEN ERROR fileFormatError;
kidCtrlStart ← RopeReader.GetIndex[control]; -- remember where the kids start
kidTextStart ← RopeReader.GetIndex[text];
RopeReader.SetPosition[control, brInfo.externalRepRope, brInfo.ctrlStart+brInfo.ctrlLen];
nKids ← ReadB32[];
THROUGH [1..nKids] DO -- create children branch nodes to be converted later
crntInfo: SpanInfo ← NEW[SpanInfoRec];
current ← TiogaNodeOps.NewBranchNode[];
IF prev=NIL THEN br.child ← current ELSE { prev.next ← current; prev.last ← FALSE };
current.next ← br; prev ← current;
crntInfo.externalRepRope ← brInfo.externalRepRope;
crntInfo.charsLen ← ReadB32[]; crntInfo.ctrlLen ← ReadB32[];
crntInfo.ctrlStart ← kidCtrlStart ; crntInfo.charsStart ← kidTextStart ;
kidCtrlStart ← kidCtrlStart + crntInfo.ctrlLen;
kidTextStart ← kidTextStart + crntInfo.charsLen;
NodeProps.PutProp[current, $BranchInfo, crntInfo];
current.externalRepValid ← TRUE;
current.internalRepCreated ← FALSE;
ENDLOOP;
br.internalRepCreated ← TRUE };
text1, text2, text3: REF TEXT; -- shared text buffers
info1, info2, info3: SpanInfo; -- shared SpanInfo records
GetSpanInfo: ENTRY PROC RETURNS [info: SpanInfo] = {
ENABLE UNWIND => NULL;
IF info3 # NIL THEN { info ← info3; info3 ← NIL }
ELSE IF info2 # NIL THEN { info ← info2; info2 ← NIL }
ELSE IF info1 # NIL THEN { info ← info1; info1 ← NIL }
ELSE info ← NEW[SpanInfoRec] };
GetStuff: ENTRY PROC [spanInfo: BOOL] RETURNS [text: REF TEXT, info: SpanInfo] = INLINE {
ENABLE UNWIND => NULL;
IF text3 # NIL THEN { text ← text3; text3 ← NIL }
ELSE IF text2 # NIL THEN { text ← text2; text2 ← NIL }
ELSE IF text1 # NIL THEN { text ← text1; text1 ← NIL }
ELSE text ← NEW[TEXT[100]];
IF ~spanInfo THEN RETURN;
IF info3 # NIL THEN { info ← info3; info3 ← NIL }
ELSE IF info2 # NIL THEN { info ← info2; info2 ← NIL }
ELSE IF info1 # NIL THEN { info ← info1; info1 ← NIL }
ELSE info ← NEW[SpanInfoRec] };
FreeSpanInfo: ENTRY PROC [info: SpanInfo] = {
ENABLE UNWIND => NULL;
IF info3 = info OR info2 = info OR info1 = info THEN ERROR;
IF info3 = NIL THEN info3 ← info
ELSE IF info2 = NIL THEN info2 ← info
ELSE IF info1 = NIL THEN info1 ← info };
FreeStuff: ENTRY PROC [text: REF TEXT, info: SpanInfo] = INLINE {
ENABLE UNWIND => NULL;
IF text3 = text OR text2 = text OR text1 = text THEN ERROR;
IF text3 = NIL THEN text3 ← text
ELSE IF text2 = NIL THEN text2 ← text
ELSE IF text1 = NIL THEN text1 ← text;
IF info=NIL THEN RETURN;
IF info3 = info OR info2 = info OR info1 = info THEN ERROR;
IF info3 = NIL THEN info3 ← info
ELSE IF info2 = NIL THEN info2 ← info
ELSE IF info1 = NIL THEN info1 ← info };
END.