Struct syntex_syntax::print::pp::Printer
[−]
[src]
pub struct Printer<'a> { pub out: Box<Write + 'a>, // some fields omitted }
In case you do not have the paper, here is an explanation of what's going on.
There is a stream of input tokens flowing through this printer.
The printer buffers up to 3N tokens inside itself, where N is linewidth. Yes, linewidth is chars and tokens are multi-char, but in the worst case every token worth buffering is 1 char long, so it's ok.
Tokens are String, Break, and Begin/End to delimit blocks.
Begin tokens can carry an offset, saying "how far to indent when you break inside here", as well as a flag indicating "consistent" or "inconsistent" breaking. Consistent breaking means that after the first break, no attempt will be made to flow subsequent breaks together onto lines. Inconsistent is the opposite. Inconsistent breaking example would be, say:
foo(hello, there, good, friends)
breaking inconsistently to become
foo(hello, there good, friends);
whereas a consistent breaking would yield:
foo(hello, there good, friends);
That is, in the consistent-break blocks we value vertical alignment more than the ability to cram stuff onto a line. But in all cases if it can make a block a one-liner, it'll do so.
Carrying on with high-level logic:
The buffered tokens go through a ring-buffer, 'tokens'. The 'left' and 'right' indices denote the active portion of the ring buffer as well as describing hypothetical points-in-the-infinite-stream at most 3N tokens apart (i.e. "not wrapped to ring-buffer boundaries"). The paper will switch between using 'left' and 'right' terms to denote the wrapped-to-ring-buffer and point-in-infinite-stream senses freely.
There is a parallel ring buffer, 'size', that holds the calculated size of each token. Why calculated? Because for Begin/End pairs, the "size" includes everything between the pair. That is, the "size" of Begin is actually the sum of the sizes of everything between Begin and the paired End that follows. Since that is arbitrarily far in the future, 'size' is being rewritten regularly while the printer runs; in fact most of the machinery is here to work out 'size' entries on the fly (and give up when they're so obviously over-long that "infinity" is a good enough approximation for purposes of line breaking).
The "input side" of the printer is managed as an abstract process called SCAN, which uses 'scan_stack', 'scan_stack_empty', 'top' and 'bottom', to manage calculating 'size'. SCAN is, in other words, the process of calculating 'size' entries.
The "output side" of the printer is managed by an abstract process called PRINT, which uses 'print_stack', 'margin' and 'space' to figure out what to do with each token/size pair it consumes as it goes. It's trying to consume the entire buffered window, but can't output anything until the size is >= 0 (sizes are set to negative while they're pending calculation).
So SCAN takes input and buffers tokens and pending calculations, while PRINT gobbles up completed calculations and tokens from the buffer. The theory is that the two can never get more than 3N tokens apart, because once there's "obviously" too much data to fit on a line, in a size calculation, SCAN will write "infinity" to the size and let PRINT consume it.
In this implementation (following the paper, again) the SCAN process is the method called 'pretty_print', and the 'PRINT' process is the method called 'print'.
Fields
out |