PrettyPrinter.int:

//
// Simple PrettyPrinter -- Andrew C. Myers, March 1999
//   For use in Cornell University Computer Science 412/413
//
uses io.OutputStream
interface PrettyPrinter {
    // A pretty-printer formats text onto an
    // output stream "o" while keeping the width of the output
    // within "width" characters if possible 
    write(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)
        // 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.
}

createPrettyPrinter(o: OutputStream, width: int): PrettyPrinter
  // create a new PrettyPrinter that writes its output to the
  // underlying output stream "o".

PrettyPrinter.mod:

createPrettyPrinter(o: OutputStream, width: int): PrettyPrinter = 
    (new Impl).init(o, width)

class Impl implements PrettyPrinter {
    init(o: OutputStream, width_: int): Impl = (
        output = o;
        width = width_;
        current = input = (new Block).init(null, 0);
        this
    )
    write(s: string) = current.add((new StringItem).init(s))
    begin(indent: int) = (
        b: Block = (new Block).init(current, indent);
        current.add(b);
        current = b
    )
    end() = ( current = current.parent )
    allowBreak(n: int) = current.add((new AllowBreak).init(n))
    newline(n: int) = current.add((new Newline).init(n))
    flush() = (
        input.format(0, 0, width, true);
        input.sendOutput(output, 0, 0);
        output.flush();
        current = input = (new Block).init(null, 0)
    )

    output: OutputStream
    width: int
    input, current: Block
}

interface Item {
    next(): Item
    setNext(it: Item)
    format(lmargin, pos, rmargin: int, canBreak: bool): FmtResult
    sendOutput(o: OutputStream, lmargin, pos: int): int
}

interface FmtResult {
    success(): bool
    pos(): int
}

class Success implements FmtResult {
    success(): bool = true
    pos_: int
    pos(): int = pos_
}

succeeded(p: int): Success = (
    ret: Success = new Success;
    ret.pos_ = p;
    ret
)

class Failure implements FmtResult {
    success(): bool = false
    pos(): int = 0
}

failure: Failure = new Failure

tryFormat(i: Item, lmargin, pos, rmargin: int, canBreak: bool): FmtResult = (
    if (pos > rmargin) return failure;
    ret: FmtResult = i.format(lmargin, pos, rmargin, canBreak);
    if (!ret.success() | !i.next()) return ret;
    tryFormat(i.next(), lmargin, ret.pos(), rmargin, canBreak)
)

class Block implements Item {
    parent: Block
    first, last: Item
    indent: int
    next_: Item
    next(): Item = next_
    setNext(it: Item) = (next_ = it)

    init(parent_: Block, indent_: int): Block = (
        parent = parent_;
        first = last = null;
        indent = indent_;
        this
    )
    add(it: Item) = (
        if (first == null) first = it else last.setNext(it);
        last = it
    )
    format(lmargin, pos, rmargin: int, canBreak: bool): FmtResult = (
        if (!first) return succeeded(pos);
        ret: FmtResult = tryFormat(first, lmargin+indent, pos, rmargin, false);
        if (ret.success()) return ret;
        if (!canBreak) failure
        else tryFormat(first, lmargin+indent, pos, rmargin, true)
    )
    sendOutput(o: OutputStream, lmargin, pos: int): int = (
        it: Item = first;
        lmargin = lmargin + indent;
        while (it) (
            pos = it.sendOutput(o, lmargin, pos);
            it = it.next()
        );
        pos
    )
}

class StringItem implements Item {
    next_: Item
   
    init(s_: string): StringItem = ( s = s_; this )
    next(): Item = next_
    setNext(it: Item) = (next_ = it)
    format(lmargin, pos, rmargin: int, canBreak: bool): FmtResult = (
        pos = pos + length s;
        if (pos > rmargin) failure
        else succeeded(pos)
    )
    sendOutput(o: OutputStream, lmargin, pos: int): int = (
        o.print(s);
        pos + length s;
    )

    s: string
}

class AllowBreak implements Item {
    next_: Item
    init(n: int): AllowBreak = ( indent = n; broken = true; this )
    next(): Item = next_
    setNext(it: Item) = (next_ = it)
    format(lmargin, pos, rmargin: int, canBreak: bool): FmtResult = (
        broken = canBreak;
        if (canBreak) succeeded(lmargin + indent)
        else succeeded(pos);
    )
    sendOutput(o: OutputStream, lmargin, pos:int): int = (
        if (broken) (
            o.print("\N");
            i: int = 0;
            while (i < lmargin) ( o.print(" "); i++ );
            lmargin + indent
        ) else (
            pos
        )
    )

    indent: int
    broken: bool
}

class Newline implements Item {
    next_: Item
    init(n: int): Newline = ( indent = n; this )
    next(): Item = next_
    setNext(it: Item) = (next_ = it)
    format(lmargin, pos, rmargin: int, canBreak: bool): FmtResult = (
        if (canBreak) succeeded(lmargin+indent)
        else failure
    )
    sendOutput(o: OutputStream, lmargin, pos:int): int = (
        o.print("\N");
        i: int = 0;
        while (i < lmargin) ( o.print(" "); i++ );
        lmargin + indent
    )

    indent: int
}