OutputStructureImpl.Mesa
written by Paxton. August 1983
Last Edited by: Paxton, August 26, 1983 4:48 pm
DIRECTORY
Atom      USING [GetPName],
CreateNode    USING [SpanInfo],
FileWriter    USING [blockSize, BumpWriter, Ref, WriteChar, WriteRope],
NameSymbolTable  USING [RopeFromName],
NodeProps    USING [GetProp, MapProps, GetSpecs],
OutputStructure,
Rope      USING [ROPE],
RopeEdit     USING [Size],
RopeReader    USING [FreeRopeReader, GetRopeReader, GetString, Ref, SetPosition],
RunReader    USING [FreeRunReader, GetRunReader, MergedGet, Ref, SetPosition],
T2FileOps,
TiogaLooks    USING [Look, Looks, noLooks, Runs],
TiogaLooksOps   USING [CountRuns],
TiogaBasicClass   USING [BasicClass],
TiogaBranchClass  USING [BranchClass, branchChildrenAtom, branchContentsAtom, defaultBranchClass],
TiogaItemClass   USING [ItemClass],
TiogaNode    USING [Name, nullName, Offset, Ref, RefBasicNode, RefBoxNode, RefBranchNode, RefItemNode, RefListNode, RefTextNode],
TiogaNodeOps   USING [FetchBasicClass, FetchItemClass, IsBranch];
OutputStructureImpl: CEDAR MONITOR
IMPORTS Atom, FileWriter, NameSymbolTable, NodeProps, RopeEdit, RopeReader, RunReader, TiogaBranchClass, TiogaLooksOps, TiogaNodeOps
EXPORTS OutputStructure
SHARES RopeReader, FileWriter, TiogaNode =
BEGIN
Output: PUBLIC PROC [
ctrl, data: FileWriter.Ref, node: TiogaNode.Ref,
contents: TiogaNode.Ref ← NIL, children: TiogaNode.RefBranchNode ← NIL]
RETURNS [flags: CHAR] = {
NoOfRuns: PROCEDURE [runs: TiogaLooks.Runs, size: INT] RETURNS [n: INT] = INLINE {
IF runs=NIL THEN RETURN [0];
[n, ,] ← TiogaLooksOps.CountRuns[runs, 0, size] };
Output32: PROC [len: TiogaNode.Offset, forwards: BOOLEAN] = {
first, second, fourth: T2FileOps.LengthByte;
third: T2FileOps.ThirdByte;
lenBytes: T2FileOps.IntBytes ← LOOPHOLE[len];
IF lenBytes.fourth # 0 THEN {
fourth.data ← lenBytes.fourth;
first.others ← second.others ← third.others ← TRUE };
IF lenBytes.thirdTop # 0 OR lenBytes.thirdBottom # 0 THEN {
third.dataTop ← lenBytes.thirdTop;
third.dataBottom ← lenBytes.thirdBottom;
first.others ← second.others ← TRUE };
IF lenBytes.second # 0 THEN {
second.data ← lenBytes.second; first.others ← TRUE };
first.data ← lenBytes.first;
The bytes are in the right place now write them out in the appropriate order
IF forwards THEN {
WriteChar[LOOPHOLE[first], ctrl];
IF first.others THEN {
WriteChar[LOOPHOLE[second], ctrl];
IF second.others THEN {
WriteChar[LOOPHOLE[third], ctrl];
IF third.others THEN WriteChar[LOOPHOLE[fourth], ctrl] }}}
ELSE {
IF first.others THEN {
IF second.others THEN {
IF third.others THEN WriteChar[LOOPHOLE[fourth], ctrl];
WriteChar[LOOPHOLE[third], ctrl] };
WriteChar[LOOPHOLE[second], ctrl] };
WriteChar[LOOPHOLE[first], ctrl] }};
OutputAtom: PROC [atom: ATOM] = INLINE { OutputControlRope[Atom.GetPName[atom]] };
OutputB32: PROCEDURE [len: TiogaNode.Offset] = INLINE { Output32[len, FALSE] };
OutputTemplateInfo: PROCEDURE [node: TiogaNode.Ref] = INLINE {
SELECT node.templateInfo FROM
normal => NULL;
application => FileWriter.WriteChar[T2FileOps.APPLICATION, ctrl];
formal => FileWriter.WriteChar[T2FileOps.FORMAL, ctrl];
ENDCASE => ERROR };
wholeTreeLevel: NAT = 3;
PushBranchData: PROC [ctrlLen, dataLen: INT] = {
top: NAT;
IF (top ← branchIOStack.top)=branchIOStack.length THEN { -- make it bigger
new: REF BranchIOStack ← NEW[BranchIOStack[top*2]];
FOR i: NAT ← 0, i+1 UNTIL i=top DO new[i] ← branchIOStack[i]; ENDLOOP;
FreeBranchIOStack[branchIOStack];
branchIOStack ← new };
branchIOStack[top].ctrlLen ← ctrlLen;
branchIOStack[top].dataLen ← dataLen;
branchIOStack.top ← top+1 };
OutputBranch: PROCEDURE [
branch: TiogaNode.RefBranchNode, level: NAT, pushData: BOOL,
contents: TiogaNode.Ref ← NIL, children: TiogaNode.RefBranchNode ← NIL] = {
IF branch.deleted THEN ERROR;
IF branch.externalRepValid THEN {
brInfo: CreateNode.SpanInfo ← NARROW[NodeProps.GetProp[branch, $BranchInfo]];
FileWriter.WriteRope[
brInfo.externalRepRope, brInfo.charsLen, data, externRopeReader, brInfo.charsStart];
FileWriter.WriteRope[
brInfo.externalRepRope, brInfo.ctrlLen, ctrl, externRopeReader, brInfo.ctrlStart];
IF pushData THEN PushBranchData[brInfo.ctrlLen, brInfo.charsLen] }
ELSE {
WriterPos: PROC [writer: FileWriter.Ref] RETURNS [len: INT] = INLINE {
RETURN [writer.blockCount*LONG[FileWriter.blockSize]+writer.block.length] };
doClass, doFormat: BOOL ← FALSE;
op: T2FileOps.Op ← T2FileOps.branch;
start: NAT;
specs: Rope.ROPE;
classInfo: TiogaBranchClass.BranchClass;
startCtrlLoc, startDataLoc: INT;
IF branch.hasbranchclass THEN
classInfo ← NARROW[NodeProps.GetProp[branch, $BranchClass]];
IF classInfo=NIL THEN classInfo ← TiogaBranchClass.defaultBranchClass;
IF pushData THEN {
start ← branchIOStack.top; startCtrlLoc ← WriterPos[ctrl]; startDataLoc ← WriterPos[data] };
IF classInfo.flavor # $Branch THEN { op ← op + T2FileOps.classBit; doClass ← TRUE };
IF branch.format#TiogaNode.nullName THEN { op ← op + T2FileOps.formatBit; doFormat ← TRUE };
IF branch.comment THEN op ← op + T2FileOps.commentBit;
WriteChar[op, ctrl];
IF doClass THEN OutputAtom[classInfo.flavor];
IF doFormat THEN OutputControlRope[NameSymbolTable.RopeFromName[branch.format]];
OutputTemplateInfo[branch];
IF branch.hasPropList THEN OutputProps[branch];
IF contents=NIL THEN contents ← branch.contents;
IF (contents=NIL AND branch.hasbranchclass AND
(specs ← NARROW[NodeProps.GetProp[branch, TiogaBranchClass.branchContentsAtom]])#NIL)
OR (classInfo.getContents#NIL AND (specs ← classInfo.getContents[branch])#NIL) THEN {
WriteChar[T2FileOps.BRANCHCONTENTSPECS, ctrl]; OutputControlRope[specs] }
ELSE IF contents # NIL THEN OutputContentsList[contents];
IF children=NIL THEN children ← branch.child;
IF (children=NIL AND branch.hasbranchclass AND
(specs ← NARROW[NodeProps.GetProp[branch, TiogaBranchClass.branchChildrenAtom]])#NIL)
OR (classInfo.getChildren#NIL AND (specs ← classInfo.getChildren[branch])#NIL) THEN {
WriteChar[T2FileOps.CHILDSPECS, ctrl]; OutputControlRope[specs] }
ELSE IF children=NIL THEN WriteChar[T2FileOps.NOCHILD, ctrl]
ELSE { -- output the children branches
count: NAT ← 0; -- number of children
childLevel: NAT ← level+1;
childrenPush: BOOL ← childLevel < wholeTreeLevel;
IF ~childrenPush THEN WriteChar[T2FileOps.STARTC, ctrl]
ELSE FileWriter.WriteChar[T2FileOps.ENDITEMS, ctrl];
FOR b: TiogaNode.RefBranchNode ← children, NARROW[b.next] DO
OutputBranch[b, childLevel, childrenPush];
count ← count+1;
IF b.last THEN EXIT;
ENDLOOP;
WriteChar[T2FileOps.ENDC, ctrl];
IF childrenPush THEN { -- write lengths and number of children
IF count # branchIOStack.top-start THEN ERROR; -- children should have pushed info
UNTIL branchIOStack.top=start DO -- pop and write ctrlLen and dataLen
item: NAT ← branchIOStack.top-1;
OutputB32[branchIOStack[item].ctrlLen];
OutputB32[branchIOStack[item].dataLen];
branchIOStack.top ← item;
ENDLOOP;
OutputB32[count] }};
IF pushData THEN PushBranchData[WriterPos[ctrl]-startCtrlLoc, WriterPos[data]-startDataLoc] }};
OutputControlRope: PROC [r: Rope.ROPE] = INLINE {
len: INT ← RopeEdit.Size[r];
OutputF32[len];
FileWriter.WriteRope[r, len, ctrl, externRopeReader];
};
OutputCountRopeCR: PROC [r: Rope.ROPE, writer: FileWriter.Ref] = INLINE {
Output a rope onto a given stream, preceeded its length and followed by a CR
OutputF32[RopeEdit.Size[r]];
OutputRope[r, writer];
};
OutputF32: PROCEDURE [len: TiogaNode.Offset] = INLINE {
FORWARD32 : : = 1..4 RECORD [more: BOOL, data: [0..127]]
Output32[len, TRUE];
};
OutputContentsList: PROCEDURE [ref: TiogaNode.Ref] = {
IF ref=NIL THEN RETURN;
DO
WITH ref SELECT FROM
t: TiogaNode.RefTextNode => OutputTI[t];
br: TiogaNode.RefBranchNode => OutputBranch[br, wholeTreeLevel, FALSE];
ENDCASE => OutputOther[ref];
IF ref.last THEN RETURN;
ref ← ref.next;
ENDLOOP };
OutputProperty: PROC [name: ATOM, value: REF] RETURNS [BOOL] = {
specs: Rope.ROPE ← NodeProps.GetSpecs[name, value, node]; -- node argument set by caller
IF specs=NIL AND (specs ← NarrowToRope[value])=NIL THEN RETURN [FALSE];
WriteChar[T2FileOps.PROPERTY, ctrl]; 
OutputAtom[name];
OutputControlRope[specs];
RETURN [FALSE] };
OutputRope: PROC [r: Rope.ROPE, writer: FileWriter.Ref] = INLINE {
Output a rope to the given stream followed by a CR
FileWriter.WriteRope[r, RopeEdit.Size[r], writer, externRopeReader];
WriteChar[15C, writer] };
OutputRuns: PROCEDURE [runs: TiogaLooks.Runs, noOfRuns: TiogaNode.Offset]
RETURNS [loc: TiogaNode.Offset ← 0] = {
cnt: TiogaNode.Offset ← 0;
RunReader.SetPosition[runReader, runs, 0];
OutputF32[noOfRuns];
WHILE (cnt𡤌nt+1) <= noOfRuns DO -- Process each run descriptor
looks: TiogaLooks.Looks;
len: TiogaNode.Offset;
[len, looks] ← RunReader.MergedGet[runReader];
IF looks=TiogaLooks.noLooks THEN WriteChar[T2FileOps.NOLOOKS, ctrl]
ELSE { -- write op's to set the bits; one op per bit that is set
last: CHAR;
initial: BOOLTRUE;
FOR c: CHAR IN TiogaLooks.Look DO
IF ~looks[c] THEN LOOP;
IF initial THEN { last ← c; initial ← FALSE } -- save the first one to write out last
ELSE WriteChar[T2FileOps.addLooksFirst+(c-FIRST[TiogaLooks.Look]), ctrl];
ENDLOOP;
WriteChar[T2FileOps.lastLooksFirst+(last-FIRST[TiogaLooks.Look]), ctrl] };
OutputF32[len];
loc ← loc+len;
ENDLOOP };
OutputTI: PROCEDURE [t: TiogaNode.RefTextNode] = {
ropeSize: TiogaNode.Offset ← RopeEdit.Size[t.rope];
noOfRuns: TiogaNode.Offset ← NoOfRuns[t.runs, ropeSize];
op: T2FileOps.Op;
doClass, doFormat, doProps, doRuns: BOOL ← FALSE;
classInfo: TiogaItemClass.ItemClass ← TiogaNodeOps.FetchItemClass[t.class];
op ← T2FileOps.text;
IF t.hasPropList OR t.templateInfo # normal THEN { op ← op + T2FileOps.propsBit; doProps ← TRUE };
IF classInfo.flavor # $Text THEN { op ← op + T2FileOps.classBit; doClass ← TRUE };
IF t.format#TiogaNode.nullName THEN { op ← op + T2FileOps.formatBit; doFormat ← TRUE };
IF noOfRuns # 0 THEN { op ← op + T2FileOps.runsBit; doRuns ← TRUE };
IF t.comment THEN op ← op + T2FileOps.commentBit;
WriteChar[op, ctrl];
IF doClass THEN OutputAtom[classInfo.flavor];
IF doFormat THEN OutputControlRope[NameSymbolTable.RopeFromName[t.format]];
IF doRuns AND OutputRuns[t.runs, noOfRuns] # ropeSize THEN ERROR;
OutputCountRopeCR[t.rope, IF t.comment THEN ctrl ELSE data];
IF doProps THEN { OutputTemplateInfo[t]; OutputProps[t]; WriteChar[T2FileOps.ENDPROPS, ctrl] }};
NarrowToRope: PROC [x: REF] RETURNS [Rope.ROPE] = {
IF x=NIL THEN RETURN [NIL];
WITH x SELECT FROM r: Rope.ROPE => RETURN [r]; ENDCASE;
RETURN [NIL] };
OutputProps: PROC [t: TiogaNode.Ref] = INLINE {
IF t.hasPropList THEN { node ← t; [] ← NodeProps.MapProps[t, OutputProperty, FALSE, FALSE] }};
OutputOther: PROCEDURE [t: TiogaNode.Ref, contents: TiogaNode.Ref ← NIL] = {
doFormat, doClass: BOOL ← FALSE;
op: T2FileOps.Op;
flavor: ATOM;
classData, contentSpecs: Rope.ROPE;
WITH t SELECT FROM
bs: TiogaNode.RefBasicNode => {
classInfo: TiogaBasicClass.BasicClass ← TiogaNodeOps.FetchBasicClass[bs.class];
op ← T2FileOps.basic;
IF (flavor ← classInfo.flavor) # $Basic THEN { doClass ← TRUE; op ← op + T2FileOps.classBit };
IF bs.data#NIL AND classInfo.get#NIL THEN classData ← NARROW[classInfo.get[bs, $Save]]
ELSE classData ← NarrowToRope[bs.data] };
bx: TiogaNode.RefBoxNode => {
classInfo: TiogaItemClass.ItemClass ← TiogaNodeOps.FetchItemClass[bx.class];
op ← T2FileOps.box;
IF (flavor ← classInfo.flavor) # $Box THEN { doClass ← TRUE; op ← op + T2FileOps.classBit };
IF contents=NIL THEN contents ← bx.contents;
IF (contents=NIL AND bx.hasPropList AND
(contentSpecs ← NARROW[NodeProps.GetProp[bx, TiogaItemClass.itemContentsAtom]])#NIL)
THEN NULL
ELSE IF contents#NIL AND classInfo.getContents#NIL THEN
contentSpecs ← classInfo.getContents[bx];
IF bx.data#NIL AND classInfo.get#NIL THEN classData ← NARROW[classInfo.get[bx, $Save]]
ELSE classData ← NarrowToRope[bx.data] };
ls: TiogaNode.RefListNode => {
classInfo: TiogaItemClass.ItemClass ← TiogaNodeOps.FetchItemClass[ls.class];
op ← T2FileOps.list;
IF (flavor ← classInfo.flavor) # $List THEN { doClass ← TRUE; op ← op + T2FileOps.classBit };
IF contents=NIL THEN contents ← ls.contents;
IF (contents=NIL AND ls.hasPropList AND
(contentSpecs ← NARROW[NodeProps.GetProp[ls, TiogaItemClass.itemContentsAtom]])#NIL)
THEN NULL
ELSE IF contents#NIL AND classInfo.getContents#NIL THEN
contentSpecs ← classInfo.getContents[ls];
IF ls.data#NIL AND classInfo.get#NIL THEN classData ← NARROW[classInfo.get[ls, $Save]]
ELSE classData ← NarrowToRope[ls.data] };
ENDCASE => ERROR;
IF t.format#TiogaNode.nullName THEN { op ← op + T2FileOps.formatBit; doFormat ← TRUE };
IF t.comment THEN op ← op + T2FileOps.commentBit;
WriteChar[op, ctrl];
IF doClass THEN OutputAtom[flavor];
IF doFormat THEN OutputControlRope[NameSymbolTable.RopeFromName[t.format]];
OutputTemplateInfo[t]; OutputProps[t];
IF contents=NIL THEN NULL
ELSE IF contentSpecs#NIL THEN {
WriteChar[T2FileOps.ITEMCONTENTSPECS, ctrl]; OutputControlRope[contentSpecs] }
ELSE {
IF TiogaNodeOps.IsBranch[contents] THEN WriteChar[T2FileOps.STARTBR, ctrl];
OutputContentsList[contents] };
IF classData=NIL THEN WriteChar[T2FileOps.NODATA, ctrl]
ELSE { WriteChar[T2FileOps.CLASSDATA, ctrl]; OutputControlRope[classData] }};
WriteChar: PROC [c: CHAR, writer: FileWriter.Ref] = INLINE { FileWriter.WriteChar[c, writer] };
branchIOStack: REF BranchIOStack ← GetBranchIOStack[];
externRopeReader: RopeReader.Ref ← RopeReader.GetRopeReader[];
runReader: RunReader.Ref ← RunReader.GetRunReader[];
flags ← 0C;
WITH node SELECT FROM
t: TiogaNode.RefTextNode => OutputTI[t];
br: TiogaNode.RefBranchNode => {
flags ← flags + T2FileOps.isBranchFlagBit; OutputBranch[br, 0, FALSE, contents, children] };
ENDCASE => OutputOther[node, contents];
RunReader.FreeRunReader[runReader];
RopeReader.FreeRopeReader[externRopeReader];
FreeBranchIOStack[branchIOStack] };
BranchIORecord: TYPE = RECORD [ctrlLen, dataLen: INT];
BranchIOStack: TYPE = RECORD [top: NAT ← 0, seq: SEQUENCE length: NAT OF BranchIORecord];
stack1, stack2, stack3: REF BranchIOStack;
GetBranchIOStack: ENTRY PROC RETURNS [stack: REF BranchIOStack] = {
ENABLE UNWIND => NULL;
IF stack3 # NIL THEN { stack ← stack3; stack3 ← NIL }
ELSE IF stack2 # NIL THEN { stack ← stack2; stack2 ← NIL }
ELSE IF stack1 # NIL THEN { stack ← stack1; stack1 ← NIL }
ELSE stack ← NEW[BranchIOStack[63]] };
FreeBranchIOStack: ENTRY PROC [stack: REF BranchIOStack] = {
ENABLE UNWIND => NULL;
IF stack3 = stack OR stack2 = stack OR stack1 = stack THEN ERROR;
stack.top ← 0;
IF stack3 = NIL THEN stack3 ← stack
ELSE IF stack2 = NIL THEN stack2 ← stack
ELSE IF stack1 = NIL THEN stack1 ← stack };
END.