UnparserBufferImpl:
CEDAR
PROGRAM
IMPORTS Atom, IO, Rope, TiogaAccess
EXPORTS UnparserBuffer
= BEGIN OPEN UnparserBuffer;
The comments here apply to any handle.
n : INT = 256; both queues and the stack will have n elements
bl, cl, br, cr, sr, srx: INTEGER; in [0, n).
c: ARRAY [0 .. n) OF CHAR; a queue with pointers cl, cr
dl: ARRAY [0 .. n) OF ROPE; dl[i] is the looks change between c[i-1] and c[i]
b: ARRAY [0 .. n) OF a queue with pointers bl, br
RECORD [type: {setb, breakpoint},
united: BOOL, relevant only if type = breakpoint
offset: INTEGER, relevant only if type = breakpoint
p: [0 .. n)]; index in c of following buffer char
s: ARRAY [0 .. n) OF INTEGER; a stack with pointer sr
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.
indentation: INTEGER;
margin: PUBLIC INTEGER;
bufferWidth: INTEGER;
output: PUBLIC BufferOutput;
Width: PUBLIC PROC [c: CHAR] RETURNS [INTEGER];
depth: INTEGER;
Used only when output is a TiogaAccess.Writer; this tracks the depth of the current node in the Writer. 0 corresponds to no indentation.
NewHandle:
PUBLIC
PROC [output: BufferOutput, margin:
INTEGER ← 80]
RETURNS [Handle] =
{h: Handle ←
NEW[HandleRec ← [output: output, spacers:
LIST[
IO.
SP], margin: margin]];
RETURN [h]};
Init:
PUBLIC
PROC [h: Handle] =
{h.bufferWidth ← h.bl ← h.cl ← h.br ← h.cr ← h.sr ← h.srx ← h.indentation ← 0;
now do a setb
h.s[0] ← 0;
Right:
PROC [m:
INTEGER]
RETURNS [
INTEGER] =
INLINE
{IF m + 1 = n THEN RETURN [0] ELSE RETURN [m + 1]};
Left:
PROC [m:
INTEGER]
RETURNS [
INTEGER] =
INLINE
{
IF m = 0
THEN
RETURN [n - 1]
ELSE
RETURN [m - 1]};
Setb:
PUBLIC
PROC[h: Handle] =
{
OPEN h;
IF bl = br AND cl = cr
THEN {s[sr] ← indentation;
sr ← sr + 1;
IF sr = n THEN ERROR} -- Setb's too deeply nested
ELSE {b[br] ← [setb,
FALSE, 0, cr];
br ← Right[br];
IF br = bl THEN ERROR}}; -- Too many active Setbs and Bps
Endb:
PUBLIC
PROC[h: Handle] =
{
OPEN h;
WHILE bl # br AND b[Left[br]].type = breakpoint
DO br ← Left[br] ENDLOOP;
IF bl # br
THEN br ← Left[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;
sr ← sr - 1}}};
OutputChar:
PROC [h: Handle] =
{
OPEN h;
WITH h.output
SELECT
FROM
so: BufferOutput.stream
=> {
IF dl[cl] #
NIL
THEN
IO.PutF[so.stream, "%l", [rope[dl[cl]]]];
IO.PutChar[so.stream, c[cl]]};
ao: BufferOutput.access
=> {
IF dl[cl] #
NIL
THEN curLooks ← ChangeLooks[curLooks, dl[cl]];
TiogaAccess.Put[ao.access, [
charSet: 0,
char: c[cl],
looks: curLooks,
comment: FALSE,
endOfNode: FALSE,
deltaLevel: 0
]]};
ENDCASE => ERROR;
indentation ← indentation + width[c[cl]];
bufferWidth ← bufferWidth - width[c[cl]];
cl ← Right[cl];
IF cl = cr AND dl[cr] # NIL
THEN {OutputLooks[h, dl[cr]]; dl[cr] ← NIL}};
OutputLooks:
PROC [h: Handle, looks: Rope.
ROPE] =
{
OPEN h;
WITH h.output
SELECT
FROM
so: BufferOutput.stream => IO.PutF[so.stream, "%l", [rope[looks]]];
ao: BufferOutput.access => curLooks ← ChangeLooks[curLooks, 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, united:
BOOL, offset:
INTEGER] =
{
OPEN h;
b[br] ← [breakpoint, united, offset, cr];
br ← Right[br];
IF br = bl THEN ERROR; -- Too many active Setbs and Bps
LeftLoop[h]};
Looksb:
PUBLIC
PROC [h: Handle, looks: Rope.
ROPE] =
{
OPEN h;
SELECT
TRUE
FROM
cl = cr => OutputLooks[h, looks];
ENDCASE => dl[cr] ← Rope.Concat[dl[cr], looks]};
Charb:
PUBLIC
PROC [h: Handle, ch:
CHAR] =
{
OPEN h;
c[cr] ← ch;
cr ← Right[cr];
IF cr = cl THEN ERROR; -- Too many characters
dl[cr] ← NIL;
bufferWidth ← bufferWidth + width[ch];
LeftLoop[h]};
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] =
{
OPEN h;
DO SELECT TRUE FROM
cl # cr AND (bl = br OR b[bl].p # cl) => OutputChar[h];
bl # br AND (cl = cr OR b[bl].p = cl) AND b[bl].type = setb
=> {s[sr] ← indentation;
sr ← sr + 1;
IF sr = n THEN ERROR; -- Too many nested Setb
bl ← Right[bl]};
bl # br AND (cl = cr OR b[bl].p = cl) AND b[bl].type = breakpoint
AND (srx = sr AND b[bl].united OR indentation + bufferWidth > margin)
=> {Breakline[h, s[sr-1] + b[bl].offset]; srx ← sr; bl ← Right[bl]};
bl # br AND (cl = cr OR b[bl].p = cl) AND b[bl].type = breakpoint
AND Right[bl] # br AND b[Right[bl]].type = breakpoint
AND ~ b[IF fix THEN bl ELSE Right[bl]].united
=> bl ← Right[bl]
ENDCASE => EXIT ENDLOOP};
Breakline:
PROC[h: Handle, indent:
INTEGER] =
{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 - h.depth];
TiogaAccess.Put[ao.access, [
charSet: 0,
char: FIRST[CHAR],
looks: ALL[FALSE],
format: ao.format,
comment: FALSE,
endOfNode: TRUE,
deltaLevel: deltaLevel
]];
h.depth ← h.depth + deltaLevel;
indent ← indent - h.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: h.curLooks,
format: NIL,
comment: FALSE,
endOfNode: FALSE,
deltaLevel: 0
]];
ENDLOOP;
indent ← indent - rep * h.width[sl.first];
ENDLOOP;
};
ENDCASE => ERROR;
h.indentation ← goalIndent};
Newlineb:
PUBLIC
PROC[h: Handle, offset:
INTEGER] =
{m:
INTEGER = h.margin;
Bp[h, TRUE, offset];
h.margin ← -1;
LeftLoop[h];
END.