DIRECTORY Atom, IO, RefText, Rope, UnparserBuffer; UnparserBufferImpl: CEDAR PROGRAM IMPORTS Atom, IO, RefText, Rope EXPORTS UnparserBuffer = BEGIN OPEN UnparserBuffer; LORA: TYPE ~ LIST OF REF ANY; Privates: TYPE = REF PrivateParts; PrivateParts: PUBLIC TYPE = RECORD [ n: Nat ฌ 0, --size of queues and stack bl, cl, br, cr, sr, srx: Nat--[0 .. n)-- ฌ 0, buff: Seq ฌ NIL, rsb: SetbPtr ฌ nilSetbPtr, --setb of innermost object at buffer insertion point hasAlways: BOOL ฌ FALSE, --there's an always break in the buffer hasMiser: BOOL ฌ FALSE, --pre-buffer should accumulate maySetb: BOOL ฌ TRUE, --at pre-buffer insertion, last miser is in innermost object or ancestor leftBroke: BOOL ฌ FALSE, prebIndent: Nat ฌ 0, minIndent: Nat ฌ 0, --least possible indendation, at insertion end of buffer; necessarily excludes misers, since they don't enter buffer bufferWidth: Nat ฌ 0, --sum of widths of chars in c indentation: Nat ฌ 0, ops, opsTail: LORA ฌ NIL, rm: OpBreak, --root of the current miser breaking problem rosb: SetbPtr ฌ nilSetbPtr, --setb of obs.first lm: OpBreak, --rightmost miser that affects coming pre-input obs: ObStack ฌ NIL --innermost first ]; Seq: TYPE = REF Sequence; Sequence: TYPE = RECORD [elts: SEQUENCE length: Nat OF Element]; Element: TYPE = RECORD [ c: CHAR, changes: Changes ฌ NIL, s: INTEGER, bs: BStuff ]; Changes: TYPE = REF ChangesPrivate; ChangesPrivate: TYPE = RECORD [ dlooks: ROPE ฌ NIL, charProps, nodeProps: PropList ฌ NIL, nodeFormat, nodeComment: ATOM ฌ NIL ]; clearProps: PropList = LIST[NIL]; BStuff: TYPE = RECORD [ p: Nat, --index in c of following buffer char variant: SELECT type: * FROM setb => [prevSB: SetbPtr, minIndent, nextSB: Nat], breakpoint => [cond: BreakCondition, offset: INTEGER, sepChars, sepWidth: Nat], ENDCASE]; biggestN: CARDINAL = LAST[Nat]; SetbPtr: TYPE ~ RECORD [inBuf: BOOL, ptr: Nat]; firstSetbPtr: SetbPtr ~ [FALSE, 0]; nilSetbPtr: SetbPtr ~ [FALSE, biggestN]; ObStack: TYPE ~ LIST OF Obj; Obj: TYPE ~ REF ObjPrivate; ObjPrivate: TYPE ~ RECORD [ si: Nat, --prebIndent at setb dm: OpBreak --determining miser: rightmost one that affects my setb indent ]; REFTEXT: TYPE ~ REF TEXT; --used for char insertions OpB: TYPE ~ REF OpBPrivate; OpBPrivate: TYPE ~ RECORD [kind: {begin, end}]; BreakList: TYPE ~ LIST OF OpBreak; OpBreak: TYPE ~ REF OpBreakPrivate; OpBreakPrivate: TYPE ~ RECORD [ cond: BreakCondition, offset: INTEGER, sep: ROPE, advantage: Nat, need: Nat ฌ 0, children: BreakList ฌ NIL, taken: BOOL ฌ FALSE, solns: Solns ฌ NIL]; Solns: TYPE ~ REF SolnSeq; SolnSeq: TYPE ~ RECORD [ maxNeed: Nat, --MAX[need, MAX(over all kids) kid.maxNeed] minA: INTEGER, --MAX[need, MAX(over all kids) kid.minA] - advantage size: Nat, --of subtree len: Nat, --occupied sequence slots elts: SEQUENCE maxLen: Nat OF SolnInterval]; SolnInterval: TYPE ~ RECORD [start, cost: Nat, take: NMY]; NMY: TYPE ~ {no, maybe, yes}; NewInittedHandle: PUBLIC PROC [publics: PublicParts] RETURNS [h: Handle] = { h ฌ NEW [PublicParts ฌ publics]; IF h.spacers = NIL THEN h.spacers ฌ LIST[IO.SP]; h.ps ฌ CreatePrivates[h.margin]; Init[h]; RETURN}; CreatePrivates: PUBLIC PROC [margin: Nat ฌ 80] RETURNS [ps: Privates] = { ps ฌ NEW [PrivateParts ฌ [buff: NEW [Sequence[margin+3]] ]]; ps.n ฌ margin+3; RETURN}; Init: PUBLIC PROC [h: Handle] = { h.ps.bl ฌ h.ps.cl ฌ h.ps.br ฌ h.ps.cr ฌ h.ps.sr ฌ h.ps.srx ฌ h.ps.bufferWidth ฌ h.ps.minIndent ฌ h.ps.indentation ฌ h.ps.prebIndent ฌ 0; h.ps.rsb ฌ h.ps.rosb ฌ nilSetbPtr; h.ps.hasAlways ฌ h.ps.hasMiser ฌ h.ps.maySetb ฌ h.ps.leftBroke ฌ FALSE; h.ps.ops ฌ h.ps.opsTail ฌ NIL; h.ps.rm ฌ h.ps.lm ฌ NIL; h.ps.obs ฌ NIL; Setb[h]; RETURN}; BCount: PROC [h: Handle] RETURNS [nb: Nat] = { nb ฌ IF h.ps.br >= h.ps.bl THEN h.ps.br - h.ps.bl ELSE (h.ps.n - h.ps.bl + h.ps.br)}; CCount: PROC [h: Handle] RETURNS [nc: Nat] = { nc ฌ IF h.ps.cr >= h.ps.cl THEN h.ps.cr - h.ps.cl ELSE (h.ps.n - h.ps.cl + h.ps.cr)}; Ensure: PROC [h: Handle, m: Nat] = INLINE { IF h.ps.n >= m THEN RETURN; IF h.ps.n = biggestN THEN ERROR; Enlarge[h, MIN[biggestN, CARDINAL[m]+m/2]]}; Enlarge: PROC [h: Handle, m: Nat] = { news: Seq = NEW [Sequence[m]]; oldCL: Nat = h.ps.cl; oldBL: Nat = h.ps.bl; nbr, ncr: Nat ฌ 0; IF h.ps.hasMiser THEN ERROR--I think Enlarge is only called while pre-buffer is being kept empty--; FOR ci: Nat ฌ h.ps.cl, Right[h, ci] WHILE ci # h.ps.cr DO news[ncr].c ฌ h.ps.buff[ci].c; news[ncr].changes ฌ h.ps.buff[ci].changes; ncr ฌ ncr + 1; ENDLOOP; h.ps.cl ฌ 0; h.ps.cr ฌ ncr; FOR bi: Nat ฌ h.ps.bl, Right[h, bi] WHILE bi # h.ps.br DO news[nbr].bs ฌ h.ps.buff[bi].bs; WITH x: news[nbr].bs SELECT FROM setb => {IF x.prevSB.inBuf THEN x.prevSB.ptr ฌ LeftL[h, x.prevSB.ptr, oldBL]; IF x.nextSB#biggestN THEN x.nextSB ฌ LeftL[h, x.nextSB, oldBL]}; breakpoint => NULL; ENDCASE => ERROR; news[nbr].bs.p ฌ LeftL[h, news[nbr].bs.p, oldCL]; nbr ฌ nbr.SUCC; ENDLOOP; IF h.ps.rsb.inBuf THEN h.ps.rsb.ptr ฌ LeftL[h, h.ps.rsb.ptr, oldBL]; h.ps.bl ฌ 0; h.ps.br ฌ nbr; FOR i: Nat IN [0 .. h.ps.sr) DO news[i].s ฌ h.ps.buff[i].s ENDLOOP; h.ps.buff ฌ news; h.ps.n ฌ m; }; Right: PROC [h: Handle, m: Nat] RETURNS [Nat] = INLINE {RETURN [IF m + 1 # h.ps.n THEN m + 1 ELSE 0]}; RightL: PROC [h: Handle, m, l: Nat] RETURNS [Nat] = INLINE {cm2: CARDINAL ฌ CARDINAL[m] + l; IF cm2 >= h.ps.n THEN cm2 ฌ cm2 - h.ps.n; RETURN [cm2]}; Left: PROC [h: Handle, m: Nat] RETURNS [Nat] = INLINE {RETURN [(IF m = 0 THEN h.ps.n ELSE m) - 1]}; LeftL: PROC [h: Handle, m, l: Nat] RETURNS [Nat] = INLINE {RETURN [IF m < l THEN (m + h.ps.n - l) ELSE (m - l)]}; PrevSbp: PROC [h: Handle, sbp: SetbPtr] RETURNS [SetbPtr] ~ { IF sbp.inBuf THEN WITH x: h.ps.buff[sbp.ptr].bs SELECT FROM setb => RETURN [x.prevSB]; ENDCASE => ERROR ELSE SELECT sbp.ptr FROM =0 => RETURN [nilSetbPtr]; RETURN [[FALSE, sbp.ptr.PRED]]; ENDCASE => ERROR}; Setb: PUBLIC PROC [h: Handle] = {ps: Privates = h.ps; {OPEN ps, h; IF hasMiser THEN { IF NOT maySetb THEN ERROR--INPUT RESTRICTION violated--; opsTail ฌ opsTail.rest ฌ LIST[NEW[OpBPrivate ฌ [begin]]]; obs ฌ CONS[NEW [ObjPrivate ฌ [prebIndent, lm]], obs]; } ELSE IF bl = br AND cl = cr THEN { Ensure[h, sr+2]; buff[sr].s ฌ indentation; rsb ฌ [FALSE, sr]; sr ฌ sr + 1} ELSE { Ensure[h, BCount[h]+2]; IF rsb.inBuf THEN WITH x: buff[rsb.ptr].bs SELECT FROM setb => {IF x.nextSB#biggestN THEN ERROR; x.nextSB ฌ br}; ENDCASE => ERROR; buff[br].bs ฌ [cr, setb[rsb, minIndent, biggestN]]; rsb ฌ [TRUE, br]; br ฌ Right[h, br]} }}; Endb: PUBLIC PROC [h: Handle] = {ps: Privates = h.ps; {OPEN ps, h; brl: Nat; IF hasMiser THEN { opsTail ฌ opsTail.rest ฌ LIST[NEW[OpBPrivate ฌ [end]]]; maySetb ฌ lm = obs.first.dm; IF obs.rest=NIL THEN {--have to lift root to next enclosing object prosb: SetbPtr ~ PrevSbp[h, rosb]; obs.first.si ฌ SbpMinIndent[h.ps, prosb]; rosb ฌ prosb; maySetb ฌ FALSE; } ELSE obs ฌ obs.rest; RETURN}; WHILE bl # br AND buff[brl ฌ Left[h, br]].bs.type = breakpoint DO br ฌ brl ENDLOOP; IF bl # br THEN { psb: SetbPtr ~ PrevSbp[h, rsb]; IF NOT rsb.inBuf THEN ERROR --Left[h, br] points at a setb in the buffer--; br ฌ Left[h, br]; IF psb.inBuf THEN WITH x: buff[psb.ptr].bs SELECT FROM setb => {IF x.nextSB#rsb.ptr THEN ERROR; x.nextSB ฌ biggestN}; ENDCASE => ERROR; rsb ฌ psb} ELSE {IF rsb.inBuf THEN ERROR --we just saw that buff had only breakpoints in it--; WHILE cl # cr DO OutputChar[h] ENDLOOP; IF sr = 0 THEN ERROR -- Endb with no matching Setb ELSE { psr: Nat ~ sr.PRED; IF srx = sr THEN { srx ฌ psr; leftBroke ฌ TRUE}; IF rsb=[FALSE, psr] THEN rsb ฌ PrevSbp[h, rsb]; sr ฌ psr}}}}; OutputChar: PROC [h: Handle] = {ps: Privates = h.ps; {OPEN ps, h; IF buff[cl].changes # NIL THEN OutputChanges[h, buff[cl].changes]; WITH h.output SELECT FROM so: BufferOutput.stream => { IO.PutChar[so.stream, buff[cl].c]}; ENDCASE => ERROR; indentation ฌ indentation + width[buff[cl].c]; bufferWidth ฌ bufferWidth - width[buff[cl].c]; cl ฌ Right[h, cl]; IF cl = cr AND buff[cr].changes # NIL THEN {OutputChanges[h, buff[cr].changes]; buff[cr].changes ฌ NIL}}}; OutputChanges: PROC [h: Handle, changes: Changes] = { ps: Privates = h.ps; {OPEN ps, h; WITH h.output SELECT FROM so: BufferOutput.stream => { IF changes.dlooks # NIL THEN IO.PutF[so.stream, "%l", [rope[changes.dlooks]]]; IF changes.charProps # NIL THEN IO.PutF[so.stream, "%p", [refAny[Newize[changes.charProps]]]]; IF changes.nodeProps # NIL THEN IO.PutF[so.stream, "%n", [refAny[Newize[changes.nodeProps]]]]; IF changes.nodeFormat # NIL THEN IO.PutF[so.stream, "%n", [atom[changes.nodeFormat]]]; IF changes.nodeComment # NIL THEN IO.PutF[so.stream, "%n", [boolean[changes.nodeComment = $TRUE]]]; }; ENDCASE => ERROR; }}; Newize: PROC [props: PropList] RETURNS [new: PropList] = INLINE { new ฌ IF props # clearProps THEN props ELSE NIL}; Oldize: PROC [props: PropList] RETURNS [new: PropList] = INLINE { new ฌ IF props # NIL THEN props ELSE clearProps}; OutputLooks: PROC [h: Handle, looks: Rope.ROPE] = { WITH h.output SELECT FROM so: BufferOutput.stream => IO.PutF[so.stream, "%l", [rope[looks]]]; ENDCASE => ERROR}; SbpMinIndent: PROC [ps: Privates, sbp: SetbPtr] RETURNS [Nat] ~ { IF sbp=nilSetbPtr THEN ERROR ELSE IF sbp.inBuf THEN WITH x: ps.buff[sbp.ptr].bs SELECT FROM setb => RETURN [x.minIndent]; ENDCASE => ERROR ELSE RETURN [ps.buff[sbp.ptr].s]}; Bp: PUBLIC PROC [h: Handle, cond: BreakCondition, offset: INTEGER, sep: ROPE] = { ps: Privates = h.ps; {OPEN ps, h; ocr: Nat ~ cr; sepChars, sepWidth: Nat ฌ 0; IF hasMiser THEN lm.need ฌ MAX[lm.need, prebIndent-margin]; IF cond=miser AND NOT hasMiser THEN { lm ฌ rm ฌ NEW [OpBreakPrivate ฌ [always, 0, NIL, 0]]; opsTail ฌ ops ฌ LIST[NIL]; obs ฌ LIST[NEW [ObjPrivate ฌ [SbpMinIndent[ps, rsb], rm]]]; rosb ฌ rsb; hasMiser ฌ TRUE}; IF cond = always THEN hasAlways ฌ TRUE ELSE { sepChars ฌ sep.Length[]; IF NOT hasMiser THEN Ensure[h, CCount[h] + sepChars + 1]; FOR i: Nat IN [0 .. sepChars) DO ch: CHAR = sep.Fetch[i]; IF NOT hasMiser THEN { buff[cr].c ฌ ch; cr ฌ Right[h, cr]; buff[cr].changes ฌ NIL}; sepWidth ฌ sepWidth + width[ch]; ENDLOOP; IF NOT hasMiser THEN bufferWidth ฌ bufferWidth + sepWidth; }; IF cond=miser THEN { advantage: INTEGER ~ prebIndent-(obs.first.si+offset); IF lm # obs.first.dm THEN ERROR--INPUT RESTRICTION violated--; IF advantage <= 0 THEN {Ropeb[h, sep]; RETURN} ELSE { ob: OpBreak ~ NEW [OpBreakPrivate ฌ [cond, offset, sep, advantage]]; obs.first.dm.children ฌ CONS[ob, obs.first.dm.children]; opsTail ฌ opsTail.rest ฌ LIST[ob]; lm ฌ ob; prebIndent ฌ prebIndent + sepWidth; RETURN}}; IF hasMiser THEN { ob: OpBreak ~ NEW [OpBreakPrivate ฌ [cond, offset, sep, 0]]; opsTail ฌ opsTail.rest ฌ LIST[ob]; lm ฌ obs.first.dm; prebIndent ฌ obs.first.si + offset; IF obs.rest=NIL THEN { SolveTree[h, ps]; FlushPreBuffer[h, ps]}; RETURN}; {unbrokenMinIndent: Nat ~ minIndent + sepWidth; brokenMinIndent: Nat ~ SbpMinIndent[ps, rsb] + offset; Ensure[h, BCount[h]+2]; buff[br].bs ฌ [ocr, breakpoint[cond, offset, sepChars, sepWidth]]; br ฌ Right[h, br]; minIndent ฌ MIN[unbrokenMinIndent, brokenMinIndent]; LeftLoop[h]}}}; SolveTree: PROC [h: Handle, ps: Privates] ~ {OPEN ps; IF rm.children.rest#NIL THEN ERROR --I think this never happens (MJS February 21, 1990)--; [] ฌ SubSolve[rm.children.first]; MarkSolution[rm.children.first, 0]; RETURN}; MarkSolution: PROC [brk: OpBreak, ia: Nat] ~ { solns: Solns ~ brk.solns; oa: Nat ฌ ia; IF ia >= solns[solns.len.PRED].start THEN brk.taken ฌ FALSE ELSE IF ia < solns[0].start THEN brk.taken ฌ TRUE ELSE FOR idx: Nat IN [1 .. solns.len) DO si: SolnInterval ~ solns[idx]; IF ia < si.start THEN { brk.taken ฌ SELECT si.take FROM no => FALSE, yes => TRUE, maybe => preferEarly, ENDCASE => ERROR; EXIT}; REPEAT FINISHED => ERROR; ENDLOOP; IF brk.taken THEN oa ฌ ia ฌ brk.advantage; FOR kids: BreakList ฌ brk.children, kids.rest WHILE kids#NIL DO MarkSolution[kids.first, oa]; ENDLOOP; RETURN}; preferEarly: BOOL ฌ TRUE; SeqPtrList: TYPE ~ LIST OF SeqPtr; SeqPtr: TYPE ~ RECORD [brk: OpBreak, startIA, nextIA, cost, nextIdx: Nat]; FindCostRun: PROC [brk: OpBreak, startIdx, decA, incC: Nat] RETURNS [SeqPtr] ~ { solns: Solns ~ brk.solns; cost: Nat ~ solns[startIdx].cost; startIA: Nat ~ MAX[solns[startIdx].start - decA, 0]; next: Nat; FOR next ฌ startIdx.SUCC, next.SUCC WHILE next < solns.len AND solns[next].cost = cost DO NULL ENDLOOP; IF next=solns.len THEN RETURN [[brk, startIA, biggestN, cost+incC, biggestN]]; RETURN [[brk, startIA, solns[next].start-decA, cost+incC, next]]}; SubSolve: PROC [brk: OpBreak] RETURNS [solns: Solns] ~ { maxNeed: Nat ฌ brk.need; minA: INTEGER ฌ brk.need; size: Nat ฌ 1; ohnes: SeqPtrList ฌ NIL; mits: SeqPtrList ฌ NIL; startIA: Nat ฌ 0; FOR kids: BreakList ฌ brk.children, kids.rest WHILE kids#NIL DO kidSolns: Solns ~ SubSolve[kids.first]; size ฌ size + kidSolns.size; maxNeed ฌ MAX[maxNeed, kidSolns.maxNeed]; minA ฌ MAX[minA, kidSolns.minA]; IF kidSolns.minA > 0 THEN ohnes ฌ CONS[[kids.first, 0, kidSolns.minA, biggestN, 0], ohnes] ELSE ohnes ฌ CONS[FindCostRun[kids.first, 0, 0, 0], ohnes]; IF kidSolns.minA > brk.advantage THEN mits ฌ CONS[[kids.first, 0, kidSolns.minA-brk.advantage, biggestN, 0], mits] ELSE { FOR j: Nat IN [1 .. kidSolns.len) DO IF kidSolns[j].start > brk.advantage THEN { mits ฌ CONS[FindCostRun[kids.first, j.PRED, brk.advantage, 1], mits]; EXIT}; REPEAT FINISHED => mits ฌ CONS[[kids.first, 0, biggestN, 1, biggestN], mits]; ENDLOOP; }; ENDLOOP; brk.solns ฌ solns ฌ NEW [SolnSeq[2*size+1]]; solns.maxNeed ฌ maxNeed; solns.minA ฌ minA ฌ minA - brk.advantage; solns.size ฌ size; solns.len ฌ 0; DO nextIA: Nat ฌ biggestN; costo: Nat ฌ 0; costm: Nat ฌ 1; FOR to: SeqPtrList ฌ ohnes, to.rest WHILE to#NIL DO SELECT to.first.nextIA - startIA FROM >0 => NULL; =0 => to.first ฌ FindCostRun[to.first.brk, to.first.nextIdx, 0, 0]; <0 => ERROR; ENDCASE => ERROR; nextIA ฌ MIN[nextIA, to.first.nextIA]; costo ฌ costo + to.first.cost; ENDLOOP; FOR tm: SeqPtrList ฌ mits, tm.rest WHILE tm#NIL DO SELECT tm.first.nextIA - startIA FROM >0 => NULL; =0 => tm.first ฌ FindCostRun[tm.first.brk, tm.first.nextIdx, brk.advantage, 1]; <0 => ERROR; ENDCASE => ERROR; nextIA ฌ MIN[nextIA, tm.first.nextIA]; costm ฌ costm + tm.first.cost; ENDLOOP; {nsi: SolnInterval ~ SELECT costm-costo FROM >0 => [startIA, costo, no], =0 => [startIA, costo, maybe], <0 => [startIA, costm, yes], ENDCASE => ERROR; new: BOOL; IF solns.len=0 THEN new ฌ TRUE ELSE { osi: SolnInterval ~ solns[solns.len.PRED]; new ฌ nsi.cost # osi.cost OR nsi.take # osi.take}; IF new THEN { IF solns.len >= solns.maxLen THEN ERROR; solns[solns.len] ฌ nsi; solns.len ฌ solns.len.SUCC}; IF nextIA=biggestN OR costo=0 THEN EXIT; startIA ฌ nextIA; }ENDLOOP; RETURN}; FlushPreBuffer: PROC [h: Handle, ps: Privates] ~ {OPEN ps, h; hasMiser ฌ FALSE; FOR ol: LORA ฌ ops.rest, ol.rest WHILE ol#NIL DO WITH ol.first SELECT FROM rt: REFTEXT => FOR i: NAT IN [0..rt.length) DO Charb[h, rt[i]] ENDLOOP; r: ROPE => Ropeb[h, r]; opo: OpB => SELECT opo.kind FROM begin => Setb[h]; end => Endb[h]; ENDCASE => ERROR; opb: OpBreak => IF opb.cond#miser THEN Bp[h, opb.cond, opb.offset, opb.sep] ELSE IF opb.taken THEN Bp[h, always, opb.offset, NIL] ELSE Ropeb[h, opb.sep]; cs: Changes => { IF cs.dlooks#NIL THEN Looksb[h, cs.dlooks]; IF cs.charProps#NIL THEN CharPropsb[h, Newize[cs.charProps]]; IF cs.nodeProps#NIL THEN NodePropsb[h, Newize[cs.nodeProps]]; IF cs.nodeFormat#NIL THEN NodeFormatb[h, cs.nodeFormat]; IF cs.nodeComment#NIL THEN NodeCommentb[h, cs.nodeComment=$TRUE]}; ENDCASE => ERROR; ENDLOOP; rm ฌ NIL; RETURN}; Charb: PUBLIC PROC [h: Handle, ch: CHAR] = {ps: Privates = h.ps; {OPEN ps, h; w: INTEGER ~ width[ch]; IF hasMiser THEN { prebIndent ฌ prebIndent + w; WITH opsTail.first SELECT FROM rt: REFTEXT => IF rt.length < rt.maxLength THEN { IF RefText.AppendChar[rt, ch] # rt THEN ERROR; RETURN}; ENDCASE => NULL; opsTail ฌ opsTail.rest ฌ LIST[RefText.New[textSize]]; RETURN}; Ensure[h, CCount[h]+2]; buff[cr].c ฌ ch; cr ฌ Right[h, cr]; buff[cr].changes ฌ NIL; bufferWidth ฌ bufferWidth + w; minIndent ฌ minIndent + w; LeftLoop[h]}}; textSize: Nat ฌ 20; EnsureChangeOp: PROC [ps: Privates] ~ { WITH ps.opsTail.first SELECT FROM cs: Changes => RETURN; ENDCASE => NULL; ps.opsTail ฌ ps.opsTail.rest ฌ LIST[NEW[ChangesPrivate ฌ []]]; RETURN}; Looksb: PUBLIC PROC [h: Handle, looks: Rope.ROPE] = { ps: Privates = h.ps; {OPEN ps, h; SELECT TRUE FROM hasMiser => {EnsureChangeOp[ps]; {cs: Changes ~ NARROW[opsTail.first]; cs.dlooks ฌ cs.dlooks.Concat[looks]}}; cl = cr => OutputChanges[h, NEW [ChangesPrivate ฌ [looks]]]; ENDCASE => { IF buff[cr].changes = NIL THEN buff[cr].changes ฌ NEW [ChangesPrivate ฌ []]; buff[cr].changes.dlooks ฌ Rope.Concat[buff[cr].changes.dlooks, looks]; }; }}; CharPropsb: PUBLIC PROC [h: Handle, props: PropList] = { ps: Privates = h.ps; {OPEN ps, h; op: PropList = Oldize[props]; SELECT TRUE FROM hasMiser => {EnsureChangeOp[ps]; {cs: Changes ~ NARROW[opsTail.first]; cs.charProps ฌ op}}; cl = cr => OutputChanges[h, NEW [ChangesPrivate ฌ [charProps: op]]]; ENDCASE => { IF buff[cr].changes = NIL THEN buff[cr].changes ฌ NEW [ChangesPrivate ฌ []]; buff[cr].changes.charProps ฌ op; }; }}; NodeFormatb: PUBLIC PROC [h: Handle, format: ATOM] = { ps: Privates = h.ps; {OPEN ps, h; SELECT TRUE FROM hasMiser => {EnsureChangeOp[ps]; {cs: Changes ~ NARROW[opsTail.first]; cs.nodeFormat ฌ format}}; cl = cr => OutputChanges[h, NEW[ChangesPrivate ฌ [nodeFormat: format]]]; ENDCASE => { IF buff[cr].changes = NIL THEN buff[cr].changes ฌ NEW [ChangesPrivate ฌ []]; buff[cr].changes.nodeFormat ฌ format; }; }}; NodePropsb: PUBLIC PROC [h: Handle, props: PropList] = { ps: Privates = h.ps; {OPEN ps, h; op: PropList = Oldize[props]; SELECT TRUE FROM hasMiser => {EnsureChangeOp[ps]; {cs: Changes ~ NARROW[opsTail.first]; cs.nodeProps ฌ op}}; cl = cr => OutputChanges[h, NEW [ChangesPrivate ฌ [nodeProps: op]]]; ENDCASE => { IF buff[cr].changes = NIL THEN buff[cr].changes ฌ NEW [ChangesPrivate ฌ []]; buff[cr].changes.nodeProps ฌ op; }; }}; NodeCommentb: PUBLIC PROC [h: Handle, comment: BOOL] = { ps: Privates = h.ps; {OPEN ps, h; atom: ATOM = IF comment THEN $TRUE ELSE $FALSE; SELECT TRUE FROM hasMiser => {EnsureChangeOp[ps]; {cs: Changes ~ NARROW[opsTail.first]; cs.nodeComment ฌ atom}}; cl=cr => OutputChanges[h, NEW[ChangesPrivate ฌ [nodeComment: atom]]]; ENDCASE => { IF buff[cr].changes = NIL THEN buff[cr].changes ฌ NEW [ChangesPrivate ฌ []]; buff[cr].changes.nodeComment ฌ atom; }; }}; Ropeb: PUBLIC PROC [h: Handle, r: Rope.ROPE] = { FOR i: INT IN [0 .. Rope.Length[r]) DO Charb[h, Rope.Fetch[r, i]] ENDLOOP}; Atomb: PUBLIC PROC [h: Handle, a: ATOM] = {Ropeb[h, Atom.GetPName[a]]}; LeftLoop: PROC [h: Handle] = {ps: Privates = h.ps; {OPEN ps, h; DO IF cl # cr AND (bl = br OR buff[bl].bs.p # cl) THEN OutputChar[h] ELSE IF cl = cr AND bl = br THEN EXIT ELSE WITH x: buff[bl].bs SELECT FROM setb => { IF x.nextSB#biggestN THEN WITH y: buff[x.nextSB].bs SELECT FROM setb => {IF y.prevSB # [TRUE, bl] THEN ERROR; y.prevSB ฌ [FALSE, sr]}; ENDCASE => ERROR ELSE {IF rsb # [TRUE, bl] THEN ERROR; rsb ฌ [FALSE, sr]}; Ensure[h, sr+2]; buff[sr].s ฌ indentation; sr ฌ sr + 1; bl ฌ Right[h, bl]; leftBroke ฌ FALSE; }; breakpoint => IF indentation + bufferWidth > margin OR (SELECT x.cond FROM width => FALSE, united => srx=sr OR hasAlways, lookLeft => leftBroke, always => TRUE, ENDCASE => ERROR) THEN { Breakline[h, buff[sr-1].s + x.offset]; srx ฌ sr; bufferWidth ฌ bufferWidth - x.sepWidth; cl ฌ RightL[h, cl, x.sepChars]; IF br = (bl ฌ Right[h, bl]) THEN hasAlways ฌ FALSE; leftBroke ฌ FALSE; } ELSE IF Right[h, bl] # br AND buff[Right[h, bl]].bs.type = breakpoint AND x.cond # united THEN { bl ฌ Right[h, bl]; leftBroke ฌ FALSE; } ELSE EXIT; ENDCASE => ERROR; ENDLOOP; IF hasAlways THEN ERROR; }}; Breakline: PROC [h: Handle, indent: INTEGER] = {ps: Privates = h.ps; {OPEN ps, h; goalIndent: INTEGER = indent; WITH h.output SELECT FROM so: BufferOutput.stream => { IO.PutChar[so.stream, IO.CR]; FOR sl: LIST OF CHAR ฌ h.spacers, sl.rest WHILE indent # 0 DO rep: INTEGER ฌ indent/h.width[sl.first]; THROUGH [0 .. rep) DO IO.PutChar[so.stream, sl.first]; ENDLOOP; indent ฌ indent - rep * h.width[sl.first]; ENDLOOP; }; ENDCASE => ERROR; indentation ฌ goalIndent; }}; END. ˆ UnparserBufferImpl.mesa Copyright ำ 1990, 1991 by Xerox Corporation. All rights reserved. Gnelson, December 6, 1983 2:05 am Last tweaked by Mike Spreitzer on February 21, 1990 10:19 am PST Last changed by Pavel on March 26, 1988 12:16:04 pm PST JKF October 2, 1988 1:13:14 pm PDT The "buffer" solves the problem once the breaking of the misers has been determined. Taken misers go into the buffer as always breaks; non-taken misers go in as their seps. The miser breaking is solved in the "pre-buffer", which accumulates all operations destined for the buffer. The solution technique makes the following INPUT RESTRICTION: if all the non-miser breakpoints were to break, the indendation at each miser breakpoint M does not depend on any other miser breakpoints except those directly contained in objects that are proper ancestors of the object that directly contains M. Another way to state this restriction scans the input left-to-right and keeps a state variable called "the last miser". Each miser breakpoint sets the last miser to itself. Associate with every object a "determining" miser, which is the last miser at the point of the object's setb. Every non-miser breakpoint sets the last miser to the determining miser of the innermost object. The input restriction is that just before each miser the last miser is one directly contained in a proper ancestor of the innermost object. The pre-buffer passes its input directly to the buffer, except between the insertion (into the pre-buffer) of a miser break and the next non-miser break that is a sibling of the miser or directly contained in an object that is an ancestor of the object that contains the miser. The input restriction enables conversion of the miser breaking problem into the following tree-based problem... buff.c & buff.changes is circular buffer with pointers [cl .. cr). changes[i] is the tioga changes between c[i-1] and c[i]. buff.bs is circular buffer with pointers [bl .. br). buff.s is stack with sr elts in it. TRUE iff an object has (recursively) broken since last setb removed from buffer or its latest breakpoint removed from buffer, whichever is later. indentation at the insertion end of pre-buffer, assuming misers not taken, others taken width that has been output on current line so far. the "pre-buffer": operations not yet input to the buffer, starting with the first miser break; first element is a dummy. The indentations of the setbs that have been removed from the buffer but whose matching endbs have not yet been input are stored in s[0], s[1], ... s[sr-1]. Furthermore, for i < srx, the setb whose indentation is stored in s[i] has been broken; for srx <= i < sr, s[i] is the indentation of a setb on the "current line" that may or may not be broken. Either [TRUE, index in buff.bs] or [FALSE, index in buff.s]. cost is biggestN for incoming advantage < minA. cost is 0 for incoming advantage >= maxNeed. last elt is for cost=0. first elt's start = MAX[minA, 0]. IA stands for Incoming (to brk, not kid) Advantage. startIA IN [s.startIA .. s.nextIA] for each SeqPtr s. [startIA .. nextIA) is subrange of [to.first.startIA .. to.first.nextIA) [startIA .. nextIA) is subrange of [tm.first.startIA .. tm.first.nextIA) [startIA .. nextIA) is subrange of [s.startIA .. s.nextIA) for each SeqPtr s. We now have bl # br AND (cl = cr OR buff[bl].bs.p = cl) This shouldn't be able to happen; the last two cases in the loop are supposed to screen this out. ส_•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ ฯeœ7™BKšœ!™!K™@K™7K™"—K˜Kšฯk œžœ!˜3K˜šัbnxœžœž˜!Kšžœžœ˜Kšžœ˜Kšœžœžœ˜K˜K™ญK™ฎK™•K™oK˜Kš žœžœžœžœžœžœ˜K˜Kšœ žœžœ˜"šœžœžœžœ˜$Kšœ ฯc˜&Kšœ  œ˜-šœ žœ˜KšœB™BKšœ8™8K™4K™#—Kšœ 4˜OKšœ žœžœ '˜@Kšœ žœžœ ˜6Kšœ žœžœ H˜^šœ žœžœ˜K™‘—˜K™W—Kšœ t˜ˆKšœ ˜3šœ˜K™2—šœžœžœ˜K™x—Kšœ  ,˜9Kšœ ˜/Kšœ  /˜˜JKšœ˜—K˜Kš žœžœžœžœ ˜4K˜Kšœžœžœ ˜Kšœ žœžœ˜/K˜Kšœ žœžœžœ ˜"Kšœ žœžœ˜#šœžœžœ˜Kšœžœžœ˜1Kšœ5žœ˜9Kšœžœžœžœ˜)—K˜Kšœžœžœ ˜šœ žœžœ˜Kšœ +˜9Kšœžœ 4˜CKšœ   ˜Kšœ  ˜#Kšœžœ žœ˜,K™/K™,K™9—K˜Kšœžœžœžœ˜:Kšžœžœ˜K˜šฯnœžœžœžœ˜LKšœžœ˜ Kš žœ žœžœ žœžœžœ˜0K˜ Kšœ˜Kšžœ˜K˜—šขœžœžœžœ˜IKšœžœžœ˜Kšžœžœ˜—Kšœ ˜ —š žœžœ žœžœ 4œ˜SKšžœ žœžœ˜'Kšžœžœžœ ˜2šžœ˜Kšœžœ˜šžœ žœ˜Kšœ ˜ Kšœ žœ˜—Kšžœžœžœ˜/Kšœ ˜ ———K˜šข œžœ&žœ˜AKšžœžœžœ$˜Bšžœ žœž˜šœ˜Kšžœ!˜#—Kšžœžœ˜—K˜.K˜.K˜Kš žœ žœžœžœ9žœ˜jK˜—šข œžœ"˜5Kšœžœ˜!šžœ žœž˜šœ˜Kš žœ กœžœžœžœ%กœ ˜NKšžœžœžœžœ<˜^Kšžœžœžœžœ<˜^Kšžœžœžœžœ3˜VKšžœžœžœžœ?˜cK˜—Kšžœžœ˜—šœ˜K˜——šขœžœžœžœ˜AKš œžœžœžœžœ˜1K˜—šขœžœžœžœ˜AKš œžœ žœžœžœ ˜1K˜—šข œžœžœ˜3šžœ žœž˜Kšœžœ&˜CKšžœžœ˜——K˜šข œžœžœ ˜AKšžœžœž˜š žœžœ žœžœžœž˜>Kšœžœ˜Kšžœž˜—Kšžœžœ˜"—K˜š ขœžœžœ+žœžœ˜QKšœžœ˜!Kšœ˜Kšœ˜Kšžœ žœ žœ˜;šžœ žœžœ žœ˜%Kšœ žœžœ˜5Kšœžœžœ˜Kšœžœžœ-˜;K˜ Kšœ žœ˜—šžœžœ žœžœ˜-Kšœ˜Kšžœžœ žœ%˜9šžœžœž˜ Kšœžœ˜šžœžœ žœ˜Kšœ˜K˜Kšœžœ˜—Kšœ ˜ Kšžœ˜—Kšžœžœ žœ&˜:K˜—šžœ žœ˜Kšœ žœ$˜6Kšžœžœž œ˜>Kšžœžœžœ˜.šžœ˜Kšœžœ3˜DKšœžœ˜8Kšœžœ˜"Kšœ˜Kšœ#˜#Kšžœ˜ ——šžœ žœ˜Kšœžœ+˜Kšžœ˜—K˜šขœžœžœžœ˜5Kšœžœ˜!šžœžœž˜šœ ˜ Kšœžœกœ กœ˜L—Kšœžœ˜<šžœ˜ Kšžœžœžœžœ˜LKšœกœ$กœ˜FKšœ˜——Kšœ˜K˜—šข œžœžœ!˜8Kšœžœ˜!K˜šžœžœž˜šœ ˜ Kšœžœ%˜:—Kšœžœ%˜Dšžœ˜ Kšžœžœžœžœ˜LKšœกœ˜ Kšœ˜——Kšœ˜K˜—šข œžœžœžœ˜6Kšœžœ˜!šžœžœž˜šœ ˜ Kšœžœ*˜?—Kšœžœ)˜Hšžœ˜ Kšžœžœžœžœ˜LKšœกœ˜%Kšœ˜——Kšœ˜K˜—šข œžœžœ!˜8Kšœžœ˜!K˜šžœžœž˜šœ ˜ Kšœžœ%˜:—Kšœžœ%˜Dšžœ˜ Kšžœžœžœžœ˜LKšœกœ˜ Kšœ˜——Kšœ˜K˜—šข œžœžœžœ˜8Kšœžœ˜!Kš œžœžœ žœžœ˜/šžœžœž˜šœ ˜ Kšœžœ)˜>—Kšœžœ(˜Ešžœ˜ Kšžœžœžœžœ˜LKšœกœ˜$Kšœ˜——Kšœ˜K˜—šขœžœžœžœ˜0Kš žœžœžœžœžœ˜K—K˜Kšขœžœžœžœ!˜GK˜šขœžœ&žœ˜?šž˜šžœ žœ žœž˜3Kšœ ˜ —šžœžœ žœ ž˜ Kšž˜—Kšœžœ žœ™7šžœžœžœž˜$˜ š žœžœžœžœž˜?Kš œ žœ žœžœžœžœ˜FKšžœž˜—Kš žœžœžœžœžœ žœ˜9K˜Kšœ˜K˜ K˜Kšœ žœ˜K˜—˜šžœ#˜%šžœžœžœ žœžœ-žœžœžœžœ˜…K˜Kšœ&˜&Kšœ ˜ Kšœ'˜'Kšœ˜šžœž˜ Kšœ žœ˜—Kšœ žœ˜Kšœ˜——š žœžœžœ)žœžœ˜`Kšœ˜Kšœ žœ˜Kšœ˜—šžœ˜Kšžœ˜——Kšžœžœ˜—Kšžœ˜—K˜šžœ ž˜Kšœa™aKšžœ˜—K˜K˜—šข œžœžœžœ˜QKšœ žœ ˜K˜šžœ žœž˜K˜šœ˜Kšžœžœžœ˜š žœžœžœžœžœ ž˜=Kšœžœ˜(šžœ ž˜Kšžœ˜ Kšžœ˜—Kšœ*˜*Kšžœ˜—K˜K˜—Kšžœžœ˜—K˜Kšœ˜šœ˜K˜——Kšžœ˜——…—NŽxu