CodeWriter.ii
uses io.OutputStream
interface I {
// A CodeWriter formats text onto an output stream o
while
// keeping the width of the output within width
characters
// if possible.
print(s: string)
// Print the string s
on the output stream
begin(indent: int)
// Start a new block with indentation increased
// by n
characters
end()
// Terminate the most recent outstanding begin
allowBreak(n: int, alt: string)
// Allow a newline. Indentation will be preserved.
newline(n: int)
// Force a newline. Indentation will be preserved.
flush()
// Send out the current batch of text
// to be formatted, closing all
// outstanding begin
's and resetting
// the indentation level to 0.
}
create(o: OutputStream, width: int): I
// create a new pretty-printer that writes its output to the
// underlying output stream o
CodeWriter.im
/**
* CodeWriter -- Andrew C. Myers, April 2001
* For use in Cornell University Computer Science CS 412/413
*/
create(o: OutputStream, w: int): I = (new C).init(o, w)
class C implements I {
output: OutputStream
width: int
input, current: Block
init(o: OutputStream, w: int): C = (
output = o;
width = w;
input = current = new Block.init(null, 0);
this
)
print(s: string) = current.add(new StringItem.init(s))
begin(n: int) = (
b: Block = new Block.init(current, n);
current.add(b);
current = b
)
end() = (current = current.parent)
allowBreak(n: int, alt: string) =
current.add(new AllowBreak.init(n, alt))
newline(n: int) = current.add(new Newline.init(n))
flush() = (
format(input,0, 0, width, width, true, true);
input.sendOutput(output, 0, 0);
output.flush();
current = input = new Block.init(null, 0);
)
}
interface FmtResult {
success(): bool
pos(): int
overrun(): int
}
class Success implements FmtResult {
success(): bool = true
pos_: int
pos(): int = pos_
overrun(): int = 0
}
succeeded(p: int): Success = (
ret: Success = new Success;
ret.pos_ = p;
ret
)
class Failure implements FmtResult {
overrun_: int
success(): bool = false
pos(): int = 0
overrun(): int = overrun_
}
failed(o: int): Failure = (
ret: Failure = new Failure;
ret.overrun_ = o;
ret
)
interface Item {
formatN(lmargin, pos, fin, rmargin: int,
can_break, no_fail: bool): FmtResult
sendOutput(o: OutputStream, lmargin, pos: int): int
isBreak(): bool
setNext(it: Item)
}
format(it: Item, lmargin, pos, rmargin, fin: int,
can_break, no_fail: bool): FmtResult = (
if (!no_fail & pos > rmargin) ( // overrun
return failed(pos - rmargin)
);
if (!it) ( // no items to format. Check against final position.
if (!no_fail & pos > fin) return failed(pos - fin);
return succeeded(pos);
);
it.formatN(lmargin, pos, rmargin, fin, can_break, no_fail)
)
/**
* A Block is a formatting unit containing a list of other items
* to be formatted.
*/
class Block implements Item {
parent: Block
first, last, next: Item
indent: int
init(p: Block, i: int): Block = (
parent = p;
first = last = next = null;
indent = i;
this
)
add(it: Item) = (
if (!first) (// just do first = it if you don't have "cast" working yet
first = it;
) else (
s: StringItem = cast(it, StringItem);
ls: StringItem = cast(last, StringItem);
if (s & ls) (
ls.appendString(s.s);
return
);
last.setNext(it)
);
last = it
)
formatN(lmargin, pos, rmargin, fin: int,
can_break, no_fail: bool): FmtResult = (
this_fin: int = rmargin;
this_nofail, this_break: bool = false;
while (true) (
first_failed: bool = true;
next_pos: int;
while (first_failed) (
r: FmtResult = format(first, pos + indent, pos, rmargin,
this_fin, this_break,
this_nofail & this_break);
if (r.success()) (
first_failed = false;
next_pos = r.pos()
) else (
if (!can_break) return r;
if (!this_break) this_break = true
else if (no_fail) this_nofail = true
else return r
)
);
r: FmtResult = format(next, lmargin, next_pos, rmargin, fin,
can_break, no_fail);
if (r.success() | !can_break | next.isBreak()) return r;
this_break = true;
if (next_pos > this_fin) next_pos = this_fin;
this_fin = next_pos - r.overrun();
)
)
sendOutput(o: OutputStream, lmargin, pos: int): int = (
it: Item = first;
pos = first.sendOutput(o, pos+indent, pos);
if (next)
pos = next.sendOutput(o, lmargin, pos);
pos
)
isBreak(): bool = false
setNext(it: Item) = (next = it)
}
class StringItem implements Item {
s: string
next: Item
init(s_: string): StringItem = (s = s_; this)
formatN(lmargin, pos, rmargin, fin: int,
can_break, no_fail: bool): FmtResult =
format(next, lmargin, pos + length s, rmargin, fin, can_break, no_fail)
sendOutput(o: OutputStream, lmargin, pos: int): int = (
o.print(s);
pos = pos + length s;
if (next)
pos = next.sendOutput(o, lmargin, pos);
pos
)
appendString(s_: string) = (s = s + s_)
isBreak(): bool = false
setNext(it: Item) = (next = it)
}
class AllowBreak implements Item {
indent: int
broken: bool
alt: string
next: Item
init(n: int, a: string): AllowBreak = (
indent = n;
alt = a;
broken = true;
this
)
formatN(lmargin, pos, rmargin, fin: int,
can_break, no_fail: bool): FmtResult = (
if (can_break) (pos = lmargin + indent; broken = true)
else (pos = pos + length alt; broken = false);
format(next, lmargin, pos, rmargin, fin, can_break, no_fail)
)
sendOutput(o: OutputStream, lmargin, pos: int): int = (
if (broken) (
o.print("\N");
i: int = 0;
while (i < lmargin + indent) (o.print(" "); i++);
pos = lmargin + indent
) else (
o.print(alt);
pos = pos + length alt
);
if (next)
pos = next.sendOutput(o, lmargin, pos);
pos
)
isBreak(): bool = true
setNext(it: Item) = (next = it)
}
class Newline implements Item {
indent: int
broken: bool
next: Item
init(n: int): Newline = (indent = n; broken = true; this)
formatN(lmargin, pos, rmargin, fin: int,
can_break, no_fail: bool): FmtResult = (
if (!can_break) return failed(1);
format(next, lmargin, lmargin + indent, rmargin, fin,
can_break, no_fail)
)
sendOutput(o: OutputStream, lmargin, pos: int): int = (
o.print("\N");
i: int = 0;
while (i < lmargin + indent) (o.print(" "); i++);
pos = lmargin + indent;
if (next)
pos = next.sendOutput(o, lmargin, pos);
pos
)
isBreak(): bool = true
setNext(it: Item) = (next = it)
}
cwTest.im
uses CodeWriter = CodeWriter.I,
create_CodeWriter = CodeWriter.create,
io.stdout, io.print
/**
* A testing driver for the CodeWriter module
*/
main(args:array[string]):int = (
//create a new CodeWriter object, set the right
//margin(line width) as 60.
cw: CodeWriter = create_CodeWriter(stdout, 60);
//Begin a new block.
cw.begin(4);
cw.print("(This is block 1:");
cw.allowBreak(0, " ");
cw.print("item1,");
cw.allowBreak(0, " ");
cw.print("item2,");
cw.allowBreak(0, " ");
cw.print("item3)");
cw.end();
//start a new line
cw.newline(0);
//Begin another block
cw.begin(4);
//The first line will start at the current position without
//indentation.
cw.print("(This is block 2:");
cw.allowBreak(0, " ");
cw.print("long item 1**********,");
cw.allowBreak(0, " ");
cw.print("long item 2**********,");
cw.allowBreak(0, " ");
cw.print("long item 3**********,");
cw.allowBreak(0, " ");
cw.begin(4);
cw.print("(This is an embeded block.");
cw.allowBreak(0, " ");
cw.print("subitem1)");
cw.allowBreak(0, " ");
cw.end();
cw.allowBreak(0, " ");
cw.begin(4);
cw.print("(This is another embeded block.");
cw.allowBreak(0, " ");
cw.print("subitem1,");
cw.allowBreak(0, " ");
cw.print("subitem2,");
cw.allowBreak(0, " ");
cw.print("subitem3)");
cw.end();
cw.end();
cw.allowBreak(0, " ");
//Begin another block, virtually similar to the first block
//except replacing a "allowBreak" with a "newLine". Because
//there exists one "newLine" item, every other "allowBreak"
//items will cause a line change.
cw.begin(4);
cw.print("(This is block 3:");
cw.allowBreak(0, " ");
cw.print("item1,");
cw.newline(0);
cw.print("item2,");
cw.allowBreak(0, " ");
cw.print("item3)");
cw.end();
cw.flush();
0
)