<> <> <<>> 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: BOOL _ TRUE; [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] = { <> brInfo: SpanInfo _ NARROW[NodeProps.GetProp[br, $BranchInfo]]; copy: SpanInfo; textBuffer: REF TEXT; control: RopeReader.Ref _ RopeReader.GetRopeReader[]; text: RopeReader.Ref _ RopeReader.GetRopeReader[]; wholeTree: BOOL _ TRUE; 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; <> <> [textBuffer, copy] _ GetStuff[wholeTree]; IF wholeTree THEN { copy^ _ brInfo^; brInfo _ copy }; <> [] _ 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] = { <> 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] = { <> 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 { <> 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 <> <> <> <> <> <> <> <> <<[rope, start, len] _ ReadSpecs[];>> <> <> <> <> 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: BOOL _ FALSE; 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, TRUE]]; itemClassInfo _ TiogaNodeOps.FetchItemClass[itemClassID] }; list => { new _ ls _ TiogaNodeOps.NewListNode[ itemClassID _ TiogaNodeOps.LookupItemID[IF doClass THEN ReadAtom[] ELSE $List, TRUE]]; itemClassInfo _ TiogaNodeOps.FetchItemClass[itemClassID] }; basic => new _ bs _ TiogaNodeOps.NewBasicNode[ basicClassID _ TiogaNodeOps.LookupBasicID[IF doClass THEN ReadAtom[] ELSE $Basic, TRUE]]; 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] = { <> 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: BOOLEAN _ FALSE; 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, TRUE]]; 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: BOOLEAN _ FALSE; 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 <> <> 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; <> 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.