UnparserBufferImpl.mesa
Gnelson, December 6, 1983 2:05 am
Mike Spreitzer July 30, 1986 9:11:52 pm PDT
DIRECTORY Atom, IO, Rope, TiogaAccess, UnparserBuffer;
UnparserBufferImpl: CEDAR PROGRAM
IMPORTS Atom, IO, Rope, TiogaAccess
EXPORTS UnparserBuffer
= BEGIN OPEN UnparserBuffer;
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,
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.
bufferWidth: NAT ← 0, --sum of widths of chars in c
hasAlways: BOOLFALSE,
leftBroke: BOOLFALSE,
TRUE iff an object has (recursively) broken since last setb removed from buffer or its latest breakpoint removed from buffer, whichever is later.
indentation: NAT ← 0,
depth: NAT ← 0,
Used only when output is a TiogaAccess.Writer; this tracks the depth of the current node in the Writer. 0 corresponds to no indentation.
leftLooks: TiogaAccess.Looks ← ALL[FALSE],
leftCharProps, leftNodeProps: PropList ← NIL,
leftNodeFormat, leftNodeComment: ATOMNIL
];
Seq: TYPE = REF Sequence;
Sequence: TYPE = RECORD [elts: SEQUENCE length: NAT OF Element];
Element: TYPE = RECORD [
c: CHAR,
changes: Changes ← NIL,
s: INTEGER,
The indentations of the setbs that have been removed from the buffer but whose matching endbs have not yet been processed 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.
bs: BStuff
];
Changes: TYPE = REF ChangesPrivate;
ChangesPrivate: TYPE = RECORD [
dlooks: ROPENIL,
charProps, nodeProps: PropList ← NIL,
nodeFormat, nodeComment: ATOMNIL
];
clearProps: PropList = LIST[NIL];
BStuff: TYPE = RECORD [
type: {setb, breakpoint},
The following are only relevent for breakpoints:
cond: BreakCondition,
offset: INTEGER,
sepChars, sepWidth: NAT,
p: NAT--index in c of following buffer char
];
biggestN: CARDINAL = LAST[NAT];
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];
};
CreatePrivates: PUBLIC PROC [margin: INTEGER ← 80] RETURNS [ps: Privates] = {
ps ← NEW [PrivateParts ← [buff: NEW [Sequence[margin+3]] ]];
};
Init: PUBLIC PROC [h: Handle] = {
h.ps.bufferWidth ← h.ps.bl ← h.ps.cl ← h.ps.br ← h.ps.cr ← h.ps.sr ← h.ps.srx ← h.ps.indentation ← 0;
Setb[h]};
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;
nb, nc: NAT ← 0;
FOR ci: NAT ← h.ps.cl, Right[h, ci] WHILE ci # h.ps.cr DO
news[nc].c ← h.ps.buff[ci].c;
news[nc].changes ← h.ps.buff[ci].changes;
nc ← nc + 1;
ENDLOOP;
h.ps.cl ← 0; h.ps.cr ← nc;
FOR bi: NAT ← h.ps.bl, Right[h, bi] WHILE bi # h.ps.br DO
news[nb].bs ← h.ps.buff[bi].bs;
news[nb].bs.p ← IF h.ps.buff[bi].bs.p >= oldCL THEN h.ps.buff[bi].bs.p - oldCL ELSE (h.ps.n - oldCL + h.ps.buff[bi].bs.p);
nb ← nb + 1;
ENDLOOP;
h.ps.bl ← 0; h.ps.br ← nb;
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: CARDINALCARDINAL[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]};
Setb: PUBLIC PROC [h: Handle] = {ps: Privates = h.ps; {OPEN ps, h;
IF bl = br AND cl = cr
THEN {
Ensure[h, sr+2];
buff[sr].s ← indentation;
sr ← sr + 1}
ELSE {
Ensure[h, BCount[h]+2];
buff[br].bs ← [setb, width, 0, 0, 0, cr];
br ← Right[h, br]}
}};
Endb: PUBLIC PROC [h: Handle] = {ps: Privates = h.ps; {OPEN ps, h;
brl: NAT;
WHILE bl # br AND buff[brl ← Left[h, br]].bs.type = breakpoint DO br ← brl ENDLOOP;
IF bl # br THEN br ← Left[h, br]
ELSE {
WHILE cl # cr DO OutputChar[h] ENDLOOP;
IF sr = 0 THEN ERROR -- Endb with no matching Setb
ELSE {
IF srx = sr THEN {
srx ← srx - 1;
leftBroke ← TRUE};
sr ← sr - 1};
}}};
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]};
ao: BufferOutput.access => {
TiogaAccess.Put[ao.access, [
charSet: 0,
char: buff[cl].c,
looks: leftLooks,
comment: FALSE,
endOfNode: FALSE,
deltaLevel: 0,
propList: leftCharProps
]]};
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]]];
};
ao: BufferOutput.access => {
IF changes.dlooks # NIL THEN leftLooks ← ChangeLooks[leftLooks, changes.dlooks];
IF changes.charProps # NIL THEN leftCharProps ← Newize[changes.charProps];
IF changes.nodeProps # NIL THEN leftNodeProps ← Newize[changes.nodeProps];
IF changes.nodeFormat # NIL THEN leftNodeFormat ← changes.nodeFormat;
IF changes.nodeComment # NIL THEN leftNodeComment ← changes.nodeComment;
};
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]]];
ao: BufferOutput.access => h.ps.leftLooks ← ChangeLooks[h.ps.leftLooks, looks];
ENDCASE => ERROR};
ChangeLooks: PROC [old: TiogaAccess.Looks, delta: Rope.ROPE] RETURNS [new: TiogaAccess.Looks] =
{new ← old;
FOR i: INT IN [0 .. delta.Length[]) DO
c: CHAR = delta.Fetch[i];
look: TiogaAccess.Look = SELECT c FROM
IN ['a .. 'a + 32) => c,
IN ['A .. 'A + 32) => c - 'A + 'a,
' => FIRST[TiogaAccess.Look],
ENDCASE => ERROR;
SELECT TRUE FROM
c = ' => new ← ALL[FALSE];
c = look => new[look] ← TRUE;
ENDCASE => new[look] ← FALSE;
ENDLOOP};
Bp: PUBLIC PROC [h: Handle, cond: BreakCondition, offset: INTEGER, sep: ROPE] = {
ps: Privates = h.ps; {OPEN ps, h;
sepChars, sepWidth: NAT ← 0;
bp: NAT = cr;
IF cond = always THEN hasAlways ← TRUE ELSE {
Ensure[h, CCount[h] + (sepChars ← sep.Length[])];
FOR i: NAT IN [0 .. sepChars) DO
ch: CHAR = sep.Fetch[i];
buff[cr].c ← ch;
cr ← Right[h, cr];
buff[cr].changes ← NIL;
sepWidth ← sepWidth + width[ch];
ENDLOOP;
bufferWidth ← bufferWidth + sepWidth;
};
Ensure[h, BCount[h]+2];
buff[br].bs ← [breakpoint, cond, offset, sepChars, sepWidth, bp];
br ← Right[h, br];
LeftLoop[h]}};
Charb: PUBLIC PROC [h: Handle, ch: CHAR] = {ps: Privates = h.ps; {OPEN ps, h;
Ensure[h, CCount[h]+2];
buff[cr].c ← ch;
cr ← Right[h, cr];
buff[cr].changes ← NIL;
bufferWidth ← bufferWidth + width[ch];
LeftLoop[h]}};
Looksb: PUBLIC PROC [h: Handle, looks: Rope.ROPE] = {
ps: Privates = h.ps; {OPEN ps, h;
SELECT TRUE FROM
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
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
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
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
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 NOT (bl # br AND (cl = cr OR buff[bl].bs.p = cl)) THEN EXIT
ELSE IF buff[bl].bs.type = setb THEN {
Ensure[h, sr+2];
buff[sr].s ← indentation;
sr ← sr + 1;
bl ← Right[h, bl];
leftBroke ← FALSE}
ELSE IF
indentation + bufferWidth > margin OR
(SELECT buff[bl].bs.cond FROM width => FALSE, united => srx = sr OR hasAlways, lookLeft => leftBroke, always => TRUE, ENDCASE => ERROR)
THEN {
Breakline[h, buff[sr-1].s + buff[bl].bs.offset];
srx ← sr;
bufferWidth ← bufferWidth - buff[bl].bs.sepWidth;
cl ← RightL[h, cl, buff[bl].bs.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 buff[bl].bs.cond # united THEN {bl ← Right[h, bl]; leftBroke ← FALSE}
ELSE EXIT;
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;
};
ao: BufferOutput.access
=> {
deltaLevel: INTEGER = MIN[1, indent/ao.nestWidth - depth];
TiogaAccess.Put[ao.access, [
charSet: 0,
char: FIRST[CHAR],
looks: ALL[FALSE],
format: leftNodeFormat,
comment: leftNodeComment = $TRUE,
endOfNode: TRUE,
deltaLevel: deltaLevel,
propList: leftNodeProps
]];
depth ← depth + deltaLevel;
indent ← indent - depth * ao.nestWidth;
FOR sl: LIST OF CHAR ← h.spacers, sl.rest WHILE indent # 0 DO
rep: INTEGER ← indent/h.width[sl.first];
THROUGH [0 .. rep) DO
TiogaAccess.Put[ao.access, [
charSet: 0,
char: sl.first,
looks: leftLooks,
format: NIL,
comment: FALSE,
endOfNode: FALSE,
deltaLevel: 0,
propList: leftCharProps
]];
ENDLOOP;
indent ← indent - rep * h.width[sl.first];
ENDLOOP;
};
ENDCASE => ERROR;
indentation ← goalIndent}};
END.