GetT2FileImpl.mesa
Written by Mik Lamming - March 14, 1983 12:08 pm
Last edited by Lamming - June 9, 1983 1:31 pm
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];
GetT2FileImpl: CEDAR PROGRAM
IMPORTS Atom, IO, NameSymbolTable, NodeProps, PutGet, Rope, RopeEdit, RopeReader, TiogaLooksOps, TiogaLooksSupport, TiogaNodeOps
EXPORTS PutGet = BEGIN
Debug:BOOLEAN = FALSE;        -- says if to generate trace info.
report:IO.STREAM;         -- trace info. stream
Tioga2FileFormatError: PUBLIC SIGNAL = CODE; -- raised for all format faults
ClassNotAvailable: PUBLIC SIGNAL = CODE;  -- raised when item class not registered
NoBranchParseInfo: PUBLIC SIGNAL = CODE;  -- raised when client does not supply BranchInfo
CreateInternalRep: PUBLIC PROC [br:TiogaNode.RefBranchNode, wholeTree:BOOLEANFALSE] = {
The primary client interface to the tree parser
brInfo:PutGet.SpanInfo ← NARROW[NodeProps.GetProp[br, $BranchInfo]];
copy:PutGet.SpanInfo;
control:RopeReader.Ref ← RopeReader.GetRopeReader[];
PutGet.GCBigInterval[];
IF wholeTree THEN {
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 root branch info is created and passed around the tree, the information being modified to suit the branch about to be cracked.
copy ← NEW[PutGet.SpanInfoRec];
copy^ ← brInfo^;
};
CIR[
br:br,
brInfo:IF wholeTree THEN copy ELSE brInfo,
wholeTree:wholeTree,
indent:0,
textBuffer: NEW[TEXT[32]],
control:control];
RopeReader.FreeRopeReader[control];
PutGet.GCRestoreInterval[]
};
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:BOOLEANFALSE;
classData:Rope.ROPENIL;
** 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 ;
--  specsRope ::= {CHAR};
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:BOOLEANFALSE;
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[];
END.