DIRECTORY
Atom USING [MakeAtom],
IO USING [atom, bool, card, char, CreateViewerStreams, PutChar, PutF, rope, STREAM],
NameSymbolTable USING [MakeName, MakeNameFromRope],
NodeProps USING [DoSpecs, GetProp, PutProp],
PutGet,
Rope USING [ROPE, Substr],
RopeEdit USING [Concat, Substr],
RopeReader USING [Backwards, BumpIndex, FreeRopeReader, Get, GetIndex, GetRopeReader, GetString, Position, Ref, SetIndex, SetPosition],
T2FileOps USING [addLooksFirst, addLooksLast, cmt, comment, default, fmt, format, heavyDuty, IntBytes, itemSpace, lastLooksFirst, lastLooksLast, LengthByte, noClassData, noLooks, Op, prop, runs, startBI, startBranch, startBX, startClassData, startLI, startTI, ThirdByte],
TiogaLooks USING [BaseRuns, Look, Looks, noLooks, Runs],
TiogaLooksOps USING [Concat],
TiogaLooksSupport USING [Short, NewBase],
TiogaBasicClass USING [BasicClass],
TiogaItemClass USING [ItemClass],
TiogaNode USING [Name, Offset, Ref, RefBranchNode, RefItemNode, RefTextNode, RefBoxNode, RefBasicNode, RefListNode],
TiogaNodeOps USING [FetchBasicClass, FetchItemClass, LookupBasicID, LookupItemID, NewBasicNode, NewBranchNode, NewBoxNode, NewListNode, NewTextNode];
CIR:
PROCEDURE [br:TiogaNode.RefBranchNode, brInfo:PutGet.SpanInfo, wholeTree:
BOOLEAN, indent:
CARDINAL, textBuffer:
REF
TEXT, control:RopeReader.Ref] = {
Convert a branch to internal format
-- branch ::= optCmt {prop} optFormat {item} lastItem {child} {childHK} startHK nchild ;
Indent:
PROCEDURE [] = {
Print n leading blanks - only called if Debug=TRUE
FOR i:
CARDINAL
IN [1..indent]
DO
IO.PutChar[report, ' ];
ENDLOOP
};
NextOp:
PROC
RETURNS [T2FileOps.Op] =
INLINE {
Get the next token from the control stream
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
-- value within 7 bits so terminate
RETURN [LOOPHOLE[card]];
second ← LOOPHOLE[RopeReader.Backwards[control]];
card.second ← second.data;
IF ~second.others
THEN
-- value within 14 bits so terminate
RETURN [LOOPHOLE[card]];
third ← LOOPHOLE[RopeReader.Backwards[control]];
card.thirdBottom ← third.dataBottom;
card.thirdTop ← third.dataTop;
IF ~third.others
THEN
-- value within 21 bits so terminate
RETURN [LOOPHOLE[card]];
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
-- value within 7 bits so terminate
RETURN [LOOPHOLE[card]];
second ← LOOPHOLE[RopeReader.Get[control]];
card.second ← second.data;
IF ~second.others
THEN
-- value within 14 bits so terminate
RETURN [LOOPHOLE[card]];
third ← LOOPHOLE[RopeReader.Get[control]];
card.thirdBottom ← third.dataBottom;
card.thirdTop ← third.dataTop;
IF ~third.others
THEN
-- value within 21 bits so terminate
RETURN [LOOPHOLE[card]];
fourth ← LOOPHOLE[RopeReader.Get[control]];
card.fourth ← fourth.data;
RETURN [LOOPHOLE[card]] -- full 28 bit value
};
ReadAtom:
PROCEDURE []
RETURNS [
ATOM] =
INLINE {
Reads a rope and makes an ATOM
RETURN[Atom.MakeAtom[ReadRope[ReadF32[], control]]];
};
ReadContents:
PROCEDURE [parent:TiogaNode.Ref]
RETURNS [head:TiogaNode.Ref] = {
Read a list of items from the control and data stream until non-item
-- contents ::= [ labelledBranch | item | basic ] ;
N.B. op: contains an uninterpreted token on termination
previous, item:TiogaNode.Ref;
DO
SELECT op
FROM
IN [T2FileOps.startTI..T2FileOps.startLI) => item ← ReadTextItem[];
IN [T2FileOps.startLI..T2FileOps.startLI+T2FileOps.itemSpace) =>
-- list items
item ← ReadNonTextItem[offset:op-T2FileOps.startLI, new:TiogaNodeOps.NewListNode[]];
IN [T2FileOps.startBI..T2FileOps.startBI+T2FileOps.itemSpace) =>
-- basic items
item ← ReadNonTextItem[offset:op-T2FileOps.startBI, new:TiogaNodeOps.NewBasicNode[]];
IN [T2FileOps.startBX..T2FileOps.startBX+T2FileOps.itemSpace) =>
-- box items
item ← ReadNonTextItem[offset:op-T2FileOps.startBX, new:TiogaNodeOps.NewBoxNode[]];
T2FileOps.startBranch => {
-- orphan branch
Build a single rope out of the orphan's control and data and plug it into a new branch structure. Next crack the branch and finally step the readers over the branch data ready to eat the next item
controlRope, textRope: Rope.ROPE;
controlStart, textStart, controlLen, textLen: TiogaNode.Offset;
br: TiogaNode.RefBranchNode ← TiogaNodeOps.NewBranchNode[];
orphanBrInfo: PutGet.SpanInfo ← NEW[PutGet.SpanInfoRec];
textLen ← ReadF32[]; -- labelled branch text length
controlLen ← ReadF32[]; -- control length
[controlRope, controlStart] ← RopeReader.Position[control];
[textRope, textStart] ← RopeReader.Position[text];
orphanBrInfo.externalRepRope ← RopeEdit.Concat[RopeEdit.Substr[textRope, textStart, textLen], RopeEdit.Substr[controlRope, controlStart, controlLen], textLen, controlLen];
br.internalRepCreated ← FALSE;
orphanBrInfo.charsStart ← 0;
orphanBrInfo.ctrlStart ← orphanBrInfo.charsLen ← textLen;
orphanBrInfo.ctrlLen ← controlLen;
br.externalRepValid ← ~wholeTree; -- C.F. current.externalRepValid ← ~wholeTree
IF wholeTree
THEN
CIR[
br:br,
brInfo:orphanBrInfo,
wholeTree:TRUE,
indent:indent+5,
textBuffer:textBuffer,
control:control] -- don't attache, props
ELSE
NodeProps.PutProp[br, $BranchInfo, orphanBrInfo]; -- attache to branch stub
item ← br;
RopeReader.SetPosition[control, controlRope, controlStart+controlLen]; -- skip branch
RopeReader.SetPosition[text, textRope, textStart+textLen]; -- skip branch
};
ENDCASE => {
-- anything but an item causes termination of chain
IF head=NIL THEN head ← previous ← item;
IF item#
NIL
THEN {
-- if no items at all then return NIL
previous.next ← parent;
previous.last ← TRUE;
};
RETURN ;
};
IF previous=NIL THEN head ← previous ← item
ELSE {
previous.next ← item;
previous ← item;
};
previous.last ← FALSE; -- there might be some more
op ← NextOp[];
ENDLOOP
};
ReadFormat:
PROCEDURE []
RETURNS [n:TiogaNode.Name] =
INLINE {
-- format ::= symbol ;
len:TiogaNode.Offset ← ReadF32[];
IF len>
LAST[
NAT]
THEN {
-- mega-symbols need special treatment
format:Rope.ROPE ← ReadRope[len, control];
IF Debug
THEN {
Indent[];
IO.PutF[report, "Format[%g]\n", IO.rope[format]]
};
RETURN [NameSymbolTable.MakeNameFromRope[format]]
}
ELSE {
IF len > textBuffer.maxLength
THEN
textBuffer ← NEW[TEXT[len]];
textBuffer.length ← 0;
textBuffer.length ← RopeReader.GetString[control, textBuffer, len];
n ← NameSymbolTable.MakeName[textBuffer];
};
};
ReadItemList:
PROCEDURE [parent:TiogaNode.Ref]
RETURNS [head:TiogaNode.Ref] = {
Read a list of items from the control and data stream until lastItem
-- {item} lastItem
-- item ::= [defaultTextItem | textItem | defaultTextItemHeavyDuty | textItemHeavyDuty | listItem | boxItem] ;
previous, item:TiogaNode.Ref;
DO
SELECT op
FROM
IN [T2FileOps.startTI..T2FileOps.startLI) => item ← ReadTextItem[]; -- text items
IN [T2FileOps.startLI..T2FileOps.startBI) =>
-- list items
item ← ReadNonTextItem[offset:op-T2FileOps.startLI, new:TiogaNodeOps.NewListNode[]];
IN [T2FileOps.startBX..T2FileOps.startBX+T2FileOps.itemSpace) =>
-- box items
item ← ReadNonTextItem[offset:op-T2FileOps.startBX, new:TiogaNodeOps.NewBoxNode[]];
ENDCASE => {
-- anything else causes termination of chain
IF head=NIL THEN head ← previous ← item;
IF item#
NIL
THEN {
-- if no items at all then return NIL
previous.next ← parent;
previous.last ← TRUE;
};
RETURN ;
};
IF previous=NIL THEN head ← previous ← item
ELSE {
previous.next ← item;
previous ← item;
};
previous.last ← FALSE; -- there might be some more
op ← NextOp[];
ENDLOOP
};
ReadNonTextItem:
PROCEDURE [offset:
CARDINAL, new:TiogaNode.Ref]
RETURNS [TiogaNode.Ref] = {
offset - the sub-type index
parent - the parent node
new - a node of the correct type to be filled in
e.g. if a comment list item with format is expected, 'offset' will be cmt+fmt and 'new' will be a RefListItem
The SCANNER examines the item token and sets flags which tell the READER what to expect on the input stream
OPEN T2FileOps; -- ** ACHTUNG! ACHTUNG! ACHTUNG!
doHD, doCmt, doRuns, doFmt, doDefault:BOOLEAN ← FALSE;
classData:Rope.ROPE ← NIL;
** SCANNER
IF offset>=heavyDuty
THEN {
-- it has props and/or contents
offset ← offset - heavyDuty;
doHD ← TRUE;
};
SELECT offset
FROM
0 => NULL;
cmt => { doCmt ← TRUE};
fmt => { doFmt ← TRUE};
fmt+cmt => { doFmt ← doCmt ← TRUE};
default => { doDefault ← TRUE};
default+cmt => { doDefault ← doCmt ← TRUE};
default+fmt => { doDefault ← doFmt ← TRUE};
default+fmt+cmt => { doDefault ← doFmt ← doCmt ← TRUE};
ENDCASE => ERROR; -- offset out of range!!!
** READER
IF Debug
THEN {
Indent[];
IO.PutF[report, "OTHER "];
indent ← indent + 2;
};
new.comment ← doCmt;
WITH new
SELECT
FROM
bx:TiogaNode.RefBoxNode => {
-- BOX NODES
classInfo:TiogaItemClass.ItemClass;
IF doHD
THEN {
IF Debug THEN IO.PutF[report, "Heavy Duty\n"];
FOR op ← NextOp[], NextOp[]
WHILE op=prop
DO
-- read any properties
ReadProp[bx];
ENDLOOP;
op contains the token for the next item
IF Debug
THEN {
Indent[];
IO.PutF[report, "CONTENTS\n"];
};
bx.contents ← ReadContents[bx];
op now contains the next token
}
ELSE {
IF Debug THEN IO.PutF[report, "Regular\n"];
op ← NextOp[];
};
SELECT op
FROM
startClassData => classData ← ReadRope[ReadF32[], control];
noClassData => NULL;
ENDCASE => SIGNAL Tioga2FileFormatError;
bx.class ← TiogaNodeOps.LookupItemID[IF doDefault THEN $Box ELSE ReadAtom[]];
classInfo ← TiogaNodeOps.FetchItemClass[bx.class];
IF classInfo.set=NIL THEN SIGNAL ClassNotAvailable
ELSE
classInfo.set[bx, $Restore, classData]; -- finalise?
IF Debug
THEN {
Indent[];
IO.PutF[report, "Class:%g Comment:%g\n", IO.atom[classInfo.flavor], IO.bool[doCmt]];
};
IF doFmt THEN bx.format ← ReadFormat[];
};
bs:TiogaNode.RefBasicNode => {
-- BASIC NODES
classInfo:TiogaBasicClass.BasicClass;
IF doHD
THEN {
IF Debug THEN IO.PutF[report, "Heavy Duty\n"];
FOR op ← NextOp[], NextOp[]
WHILE op=prop
DO
-- read any properties
ReadProp[bs];
ENDLOOP;
op contains the token for the next item
}
ELSE {
IF Debug THEN IO.PutF[report, "Regular\n"];
op ← NextOp[];
};
SELECT op
FROM
startClassData => classData ← ReadRope[ReadF32[], control];
noClassData => NULL;
ENDCASE => SIGNAL Tioga2FileFormatError;
bs.class ← TiogaNodeOps.LookupBasicID[IF doDefault THEN $Basic ELSE ReadAtom[]];
classInfo ← TiogaNodeOps.FetchBasicClass[bs.class];
IF classInfo.set=NIL THEN SIGNAL ClassNotAvailable
ELSE
classInfo.set[bs, $Restore, classData]; -- finalise?
IF Debug
THEN {
Indent[];
IO.PutF[report, "Class:%g Comment:%g\n", IO.atom[classInfo.flavor], IO.bool[doCmt]]
};
IF doFmt THEN bs.format ← ReadFormat[];
};
li:TiogaNode.RefListNode => {
-- LIST NODES
classInfo:TiogaItemClass.ItemClass;
IF doHD
THEN {
IF Debug THEN IO.PutF[report, "Heavy Duty\n"];
FOR op ← NextOp[], NextOp[]
WHILE op=prop
DO
-- read any properties
ReadProp[li];
ENDLOOP;
op contains the token for the next item
IF Debug
THEN {
Indent[];
IO.PutF[report, "CONTENTS\n"];
};
li.contents ← ReadContents[li];
op now contains the next token
}
ELSE {
IF Debug THEN IO.PutF[report, "Regular\n"];
op ← NextOp[];
};
SELECT op
FROM
startClassData => classData ← ReadRope[ReadF32[], control];
noClassData => NULL;
ENDCASE => SIGNAL Tioga2FileFormatError;
li.class ← TiogaNodeOps.LookupItemID[IF doDefault THEN $List ELSE ReadAtom[]];
classInfo ← TiogaNodeOps.FetchItemClass[li.class];
IF classInfo.set=NIL THEN SIGNAL ClassNotAvailable
ELSE
classInfo.set[li, $Restore, classData]; -- finalise?
IF Debug
THEN {
Indent[];
IO.PutF[report, "Class:%g Comment:%g\n", IO.atom[classInfo.flavor], IO.bool[doCmt]]
};
IF doFmt THEN li.format ← ReadFormat[];
};
ENDCASE => ERROR; -- ReadNonTextItem called with inappropriate item type
IF Debug THEN indent ← indent - 2;
RETURN[new]
};
ReadProp:
PROCEDURE [br:TiogaNode.Ref] = {
Reads is a property pair and assigns it to the given node
-- prop ::= prop propName specs
-- propName ::= symbol ;
-- symbol ::= symbolLength symbolRope ;
-- symbolLength ::= FORWARD32 ;
-- symbolRope ::= {CHAR};
-- specs ::= specsLength specsRope ;
-- specsLength ::= FORWARD32 ;
atom:ATOM ← ReadAtom[];
specs:Rope.ROPE ← ReadRope[ReadF32[], control];
IF Debug
THEN {
Indent[];
IO.PutF[report, "Property[%g, %g]\n", IO.atom[atom], IO.rope[specs]]
};
NodeProps.PutProp[br, atom, NodeProps.DoSpecs[atom, specs, br]]
};
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] = {
-- runs ::= noOfRuns {run} ;
-- noOfRuns ::= FORWARD32 ;
-- run ::= [lookSeq| noLooks] runLength ;
-- lookSeq ::= {addlooks} lastLooks
-- addlooks ::= BYTE [addLooksFirst..addLooksLast] ;
-- lastLooks ::= BYTE [lastLooksFirst..lastLooksLast] ;
-- runLength ::= FORWARD32 ;
** noOfRuns
pos: TiogaNode.Offset ← 0;
numRuns: TiogaNode.Offset ← ReadF32[];
IF Debug
THEN {
Indent[];
IO.PutF[report, "Runs (%g) ", IO.card[numRuns]];
};
WHILE numRuns > 0
DO
-- ** {run}
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
ReadLooks: PROC [] = {
sets the appropriate bits in looks
FOR op:
CHAR ← NextOp[], NextOp[]
DO
-- ** [lookSeq | noLooks]
SELECT op
FROM
IN [T2FileOps.addLooksFirst..T2FileOps.addLooksLast] => {
add looks to looks vector
c:CHARACTER ← 'a+(op - T2FileOps.addLooksFirst);
IF Debug THEN IO.PutF[report, "%g ", IO.char[c]];
looks[c] ← TRUE;
};
IN [T2FileOps.lastLooksFirst..T2FileOps.lastLooksLast] => {
add looks to looks vector
c:CHARACTER ← 'a+(op - T2FileOps.lastLooksFirst);
IF Debug THEN IO.PutF[report, "%g ", IO.char[c]];
looks[c] ← TRUE;
EXIT;
};
T2FileOps.noLooks => {
IF Debug THEN IO.PutF[report, "NoLooks "];
EXIT;
};
ENDCASE => SIGNAL Tioga2FileFormatError;
ENDLOOP;
};
looks: TiogaLooks.Looks ← TiogaLooks.noLooks;
lookLen:TiogaNode.Offset;
ReadLooks[];
lookLen ← ReadF32[];
baseRuns[i] ← [pos←pos+lookLen, looks];
IF Debug THEN IO.PutF[report, "(%g) ", IO.card[lookLen]];
ENDLOOP;
lookRuns ←
IF lookRuns=
NIL
THEN baseRuns
ELSE
TiogaLooksOps.Concat[lookRuns,baseRuns,len,pos-len];
ENDLOOP;
IF Debug THEN IO.PutF[report, "total %g\n", IO.card[pos]];
};
ReadTextItem:
PROCEDURE []
RETURNS [item:TiogaNode.RefTextNode] = {
OPEN T2FileOps; -- ** ACHTUNG! ACHTUNG! ACHTUNG!
Reads a text node followed by any other items that follow until EOF
ReadCRRope:
PROCEDURE [rdr:RopeReader.Ref]
RETURNS [rope:Rope.
ROPE] =
INLINE {
Reads a rope from the given reader at current position. The rope is preceded by its length. The reader is advanced beyond the terminating CR. The CR is not returned as part of the rope.
rope ← ReadRope[ReadF32[], rdr];
RopeReader.BumpIndex[rdr, 1]; -- skip over CR
IF Debug
THEN {
Indent[];
IO.PutF[report,"%l%g ...%l\n",IO.rope["b"], IO.rope[Rope.Substr[rope, 0, 40]], IO.rope[" "]]
};
};
doHD, doCmt, doRuns, doFmt, doDefault:BOOLEAN ← FALSE;
offset:CARDINAL;
classInfo:TiogaItemClass.ItemClass;
SELECT op
FROM
-- decide if heavy duty item and set offset to get within-type info
IN [startTI..startTI+heavyDuty) => {offset ← op - startTI};
IN [startTI+heavyDuty..startLI) => {doHD ← TRUE; offset ← op - (startTI+heavyDuty)};
ENDCASE => ERROR; -- ReadTextItem called with non text item
SELECT offset
FROM
0 => NULL;
cmt => { doCmt ← TRUE};
runs => { doRuns ← TRUE};
runs+cmt => { doRuns ← doCmt ← TRUE};
fmt => { doFmt ← TRUE};
fmt+cmt => { doFmt ← doCmt ← TRUE};
fmt+runs => { doFmt ← doRuns ← TRUE};
fmt+runs+cmt => { doFmt ← doRuns ← doCmt ← TRUE};
default => { doDefault ← TRUE};
default+cmt => { doDefault ← doCmt ← TRUE};
default+runs => { doDefault ← doRuns ← TRUE};
default+runs+cmt => { doDefault ← doRuns ← doCmt ← TRUE};
default+fmt => { doDefault ← doFmt ← TRUE};
default+fmt+cmt => { doDefault ← doFmt ← doCmt ← TRUE};
default+fmt+runs => { doDefault ← doFmt ← doRuns ← TRUE};
default+fmt+runs+cmt => { doDefault ← doFmt ← doRuns ← doCmt ← TRUE};
ENDCASE => ERROR; -- offset out of range!!!
item ← TiogaNodeOps.NewTextNode[];
IF Debug
THEN {
Indent[];
IO.PutF[report, "TEXT "];
indent ← indent + 2;
};
IF doHD
THEN {
IF Debug
THEN {
IO.PutF[report, "Heavy Duty\n"];
};
FOR op ← NextOp[], NextOp[]
WHILE op=prop
DO
-- read any properties
ReadProp[item];
ENDLOOP;
op should contain the token NoClassData which serves as a property list terminator
}
ELSE
IF Debug
THEN
IO.PutF[report, "Regular\n"];
item.class ← TiogaNodeOps.LookupItemID[IF doDefault THEN $Text ELSE ReadAtom[]];
classInfo ← TiogaNodeOps.FetchItemClass[item.class];
IF classInfo=NIL THEN SIGNAL ClassNotAvailable;
IF Debug
THEN {
Indent[];
IO.PutF[report, "Class:%g Comment:%g\n", IO.atom[classInfo.flavor], IO.bool[doCmt]]
};
IF doRuns THEN item.runs ← ReadRuns[]; -- construct the runs descriptor
IF doFmt THEN item.format ← ReadFormat[];
item.comment ← doCmt;
item.rope ← IF doCmt THEN ReadCRRope[control] ELSE ReadCRRope[text];
IF Debug THEN indent ← indent - 2;
};
*** CREATEINTERNALREP STARTS HERE ***
-- optCmt {prop} optFormat {item} lastItem
text: RopeReader.Ref;
nKids: CARDINAL;
prev, current: TiogaNode.RefBranchNode ;
kidCtrlStart, kidTextStart: TiogaNode.Offset;
op: T2FileOps.Op;
IF brInfo=NIL THEN ERROR; -- client did not supply branch info. properties
IF Debug
THEN {
Indent[];
IO.PutF[report, "%gBRANCH Data: %g %g Control: %g %g\n",
IF brInfo.ctrlLen=0 THEN IO.rope["CHEAP "] ELSE IO.rope[""],
IO.card[brInfo.charsStart],
IO.card[brInfo.charsLen],
IO.card[brInfo.ctrlStart],
IO.card[brInfo.ctrlLen]];
};
text ← RopeReader.GetRopeReader[]; -- get text reader from RR cache
IF Debug
THEN {
Indent[]; IO.PutF[report, " CONTENTS: "];
indent ← indent + 2;
};
*** PARSE BRANCH CONTENTS DATA ***
-- cheapBranch | optCmt {prop} optFormat {item} lastItem
RopeReader.SetPosition[text, brInfo.externalRepRope, brInfo.charsStart];
RopeReader.SetPosition[control, brInfo.externalRepRope, brInfo.ctrlStart];
IF brInfo.ctrlLen=0
THEN {
CHEAP BRANCHES - If the control length is zero then this is an empty branch. It has no properties, format or children. If the text length is non-zero then it is a default text item (DTI). The text for a DTI equals the branch text.
IF brInfo.charsLen=0
THEN {
IF Debug THEN IO.PutF[report, "EMPTY\n"];
}
ELSE {
t:TiogaNode.RefTextNode ← br.contents ← TiogaNodeOps.NewTextNode[];
t.class ← TiogaNodeOps.LookupItemID[$Text];
t.next ← br;
t.rope ← RopeEdit.Substr[brInfo.externalRepRope, brInfo.charsStart, brInfo.charsLen-1]; -- skip CR
IF Debug
THEN
IO.PutF[report, "%l%g ...%l\n", IO.rope["b"], IO.rope[RopeEdit.Substr[t.rope, 0, 40]], IO.rope[" "]];
};
br.internalRepCreated ← TRUE; -- mark branch as cracked at last
indent ← indent - 2;
RopeReader.FreeRopeReader[text];
RETURN;
};
IF Debug THEN IO.PutF[report, "\n"];
op ← NextOp[];
DO
SELECT op
FROM
T2FileOps.comment =>
{ br.comment ← TRUE; op ← NextOp[] };
T2FileOps.prop =>
{ ReadProp[br]; op ← NextOp[] };
T2FileOps.format =>
{ br.format ← ReadFormat[]; op ← NextOp[] };
ENDCASE => {
br.contents ← NARROW[ReadItemList[parent:br]];
EXIT;
};
ENDLOOP;
kidCtrlStart ← RopeReader.GetIndex[control]; -- remember where the kids start
kidTextStart ← RopeReader.GetIndex[text];
RopeReader.FreeRopeReader[text]; -- only play with the control stream hereafter
REGULAR BRANCHES - This section deals with the general case, i.e. branches not covered by the above. This means: all branches with one or more of the following characteristics: children; format; properties; non-DTI contents.
-- {child} {childHK} startHK nchild ;
Set the control reader to read backwards and get nchild, then startHK, then {childHK}
RopeReader.SetPosition[control, brInfo.externalRepRope, brInfo.ctrlStart+brInfo.ctrlLen];
nKids ← ReadB32[];
If there are no children then startHK is not present because it can be computed trivially
IF Debug
THEN {
indent ← indent - 1;
Indent[];
IO.PutF[report, "%lChildren %g%l\n", IO.rope["i"], IO.card[nKids], IO.rope[" "]]
};
IF nKids>0
THEN {
BRANCH WITH CHILDREN - Going to create the child chain backwards. 'nextNode' points to the successor of the node that is being created now (current). Start of control (lastCtrlBytePlus1) and start of text (lastTextBytePlus1) for each child branch is computed by subtracting successive child lengths from the parent's text and control limits.
crntInfo:PutGet.SpanInfo;
FOR i:
CARDINAL
IN [1..nKids]
DO
current ← TiogaNodeOps.NewBranchNode[];
IF prev=NIL THEN br.child ← current ELSE prev.next ← current;
current.last ← (i=nKids); -- default is TRUE damnit
current.externalRepValid ← ~wholeTree;
Explanation of above statement: If we are going to crack the whole tree then brInfo nodes are not attached to the branch. So, even if the external rep has not been modified, there is no information attached to the branch to allow the section of the rope corresponding to this branch to be identified.
current.internalRepCreated ← FALSE; -- maybe not going to crack child here
if we are going to crack the whole tree then there is no need to leave any branch properties around so can re-use the one we were called with
IF wholeTree THEN crntInfo ← brInfo
ELSE crntInfo ← NEW[PutGet.SpanInfoRec];
crntInfo.externalRepRope ← brInfo.externalRepRope;
crntInfo.charsLen ← ReadB32[];
crntInfo.ctrlLen ← ReadB32[];
crntInfo.ctrlStart ← kidCtrlStart ;
crntInfo.charsStart ← kidTextStart ;
compute text and data starts and save for next iteration
kidCtrlStart ← kidCtrlStart + crntInfo.ctrlLen;
kidTextStart ← kidTextStart + crntInfo.charsLen;
IF wholeTree
THEN {
ropeSave:Rope.ROPE; ropeOffset:TiogaNode.Offset;
IF Debug
THEN {
Indent[];
IO.PutF[report, "CHILD %g:\n", IO.card[i]];
};
[ropeSave, ropeOffset] ← RopeReader.Position[control]; -- save vitals so sub-CIR can use
CIR[
br:current,
brInfo:crntInfo,
wholeTree:TRUE,
indent:indent+4,
textBuffer:textBuffer,
control:control];
RopeReader.SetPosition[control, ropeSave, ropeOffset]; -- restore vitals
}
ELSE NodeProps.PutProp[current, $BranchInfo, crntInfo];
prev ← current;
ENDLOOP;
current.next ← br; -- point child at parent
};
br.internalRepCreated ← TRUE; -- mark branch as cracked at last
indent ← indent - 1;
};
NewReport:
PROCEDURE []
RETURNS [] = {
Create a diagnostic window and describe all files read in
Debug ← TRUE;
[, report] ← IO.CreateViewerStreams["Tioga2 file description"];
};
IF Debug THEN NewReport[];