]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/print/pp.rs
Fix whitespace in `pp.rs`.
[rust.git] / src / libsyntax / print / pp.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! This pretty-printer is a direct reimplementation of Philip Karlton's
12 //! Mesa pretty-printer, as described in appendix A of
13 //!
14 //! ````text
15 //! STAN-CS-79-770: "Pretty Printing", by Derek C. Oppen.
16 //! Stanford Department of Computer Science, 1979.
17 //! ````
18 //!
19 //! The algorithm's aim is to break a stream into as few lines as possible
20 //! while respecting the indentation-consistency requirements of the enclosing
21 //! block, and avoiding breaking at silly places on block boundaries, for
22 //! example, between "x" and ")" in "x)".
23 //!
24 //! I am implementing this algorithm because it comes with 20 pages of
25 //! documentation explaining its theory, and because it addresses the set of
26 //! concerns I've seen other pretty-printers fall down on. Weirdly. Even though
27 //! it's 32 years old. What can I say?
28 //!
29 //! Despite some redundancies and quirks in the way it's implemented in that
30 //! paper, I've opted to keep the implementation here as similar as I can,
31 //! changing only what was blatantly wrong, a typo, or sufficiently
32 //! non-idiomatic rust that it really stuck out.
33 //!
34 //! In particular you'll see a certain amount of churn related to INTEGER vs.
35 //! CARDINAL in the Mesa implementation. Mesa apparently interconverts the two
36 //! somewhat readily? In any case, I've used usize for indices-in-buffers and
37 //! ints for character-sizes-and-indentation-offsets. This respects the need
38 //! for ints to "go negative" while carrying a pending-calculation balance, and
39 //! helps differentiate all the numbers flying around internally (slightly).
40 //!
41 //! I also inverted the indentation arithmetic used in the print stack, since
42 //! the Mesa implementation (somewhat randomly) stores the offset on the print
43 //! stack in terms of margin-col rather than col itself. I store col.
44 //!
45 //! I also implemented a small change in the String token, in that I store an
46 //! explicit length for the string. For most tokens this is just the length of
47 //! the accompanying string. But it's necessary to permit it to differ, for
48 //! encoding things that are supposed to "go on their own line" -- certain
49 //! classes of comment and blank-line -- where relying on adjacent
50 //! hardbreak-like Break tokens with long blankness indication doesn't actually
51 //! work. To see why, consider when there is a "thing that should be on its own
52 //! line" between two long blocks, say functions. If you put a hardbreak after
53 //! each function (or before each) and the breaking algorithm decides to break
54 //! there anyways (because the functions themselves are long) you wind up with
55 //! extra blank lines. If you don't put hardbreaks you can wind up with the
56 //! "thing which should be on its own line" not getting its own line in the
57 //! rare case of "really small functions" or such. This re-occurs with comments
58 //! and explicit blank lines. So in those cases we use a string with a payload
59 //! we want isolated to a line and an explicit length that's huge, surrounded
60 //! by two zero-length breaks. The algorithm will try its best to fit it on a
61 //! line (which it can't) and so naturally place the content on its own line to
62 //! avoid combining it with other lines and making matters even worse.
63 //!
64 //! # Explanation
65 //!
66 //! In case you do not have the paper, here is an explanation of what's going
67 //! on.
68 //!
69 //! There is a stream of input tokens flowing through this printer.
70 //!
71 //! The printer buffers up to 3N tokens inside itself, where N is linewidth.
72 //! Yes, linewidth is chars and tokens are multi-char, but in the worst
73 //! case every token worth buffering is 1 char long, so it's ok.
74 //!
75 //! Tokens are String, Break, and Begin/End to delimit blocks.
76 //!
77 //! Begin tokens can carry an offset, saying "how far to indent when you break
78 //! inside here", as well as a flag indicating "consistent" or "inconsistent"
79 //! breaking. Consistent breaking means that after the first break, no attempt
80 //! will be made to flow subsequent breaks together onto lines. Inconsistent
81 //! is the opposite. Inconsistent breaking example would be, say:
82 //!
83 //! ```
84 //! foo(hello, there, good, friends)
85 //! ```
86 //!
87 //! breaking inconsistently to become
88 //!
89 //! ```
90 //! foo(hello, there
91 //!     good, friends);
92 //! ```
93 //!
94 //! whereas a consistent breaking would yield:
95 //!
96 //! ```
97 //! foo(hello,
98 //!     there
99 //!     good,
100 //!     friends);
101 //! ```
102 //!
103 //! That is, in the consistent-break blocks we value vertical alignment
104 //! more than the ability to cram stuff onto a line. But in all cases if it
105 //! can make a block a one-liner, it'll do so.
106 //!
107 //! Carrying on with high-level logic:
108 //!
109 //! The buffered tokens go through a ring-buffer, 'tokens'. The 'left' and
110 //! 'right' indices denote the active portion of the ring buffer as well as
111 //! describing hypothetical points-in-the-infinite-stream at most 3N tokens
112 //! apart (i.e. "not wrapped to ring-buffer boundaries"). The paper will switch
113 //! between using 'left' and 'right' terms to denote the wrapped-to-ring-buffer
114 //! and point-in-infinite-stream senses freely.
115 //!
116 //! There is a parallel ring buffer, `size`, that holds the calculated size of
117 //! each token. Why calculated? Because for Begin/End pairs, the "size"
118 //! includes everything between the pair. That is, the "size" of Begin is
119 //! actually the sum of the sizes of everything between Begin and the paired
120 //! End that follows. Since that is arbitrarily far in the future, `size` is
121 //! being rewritten regularly while the printer runs; in fact most of the
122 //! machinery is here to work out `size` entries on the fly (and give up when
123 //! they're so obviously over-long that "infinity" is a good enough
124 //! approximation for purposes of line breaking).
125 //!
126 //! The "input side" of the printer is managed as an abstract process called
127 //! SCAN, which uses `scan_stack`, to manage calculating `size`. SCAN is, in
128 //! other words, the process of calculating 'size' entries.
129 //!
130 //! The "output side" of the printer is managed by an abstract process called
131 //! PRINT, which uses `print_stack`, `margin` and `space` to figure out what to
132 //! do with each token/size pair it consumes as it goes. It's trying to consume
133 //! the entire buffered window, but can't output anything until the size is >=
134 //! 0 (sizes are set to negative while they're pending calculation).
135 //!
136 //! So SCAN takes input and buffers tokens and pending calculations, while
137 //! PRINT gobbles up completed calculations and tokens from the buffer. The
138 //! theory is that the two can never get more than 3N tokens apart, because
139 //! once there's "obviously" too much data to fit on a line, in a size
140 //! calculation, SCAN will write "infinity" to the size and let PRINT consume
141 //! it.
142 //!
143 //! In this implementation (following the paper, again) the SCAN process is
144 //! the method called `Printer::pretty_print`, and the 'PRINT' process is the method
145 //! called `Printer::print`.
146
147 use std::collections::VecDeque;
148 use std::fmt;
149 use std::io;
150
151 /// How to break. Described in more detail in the module docs.
152 #[derive(Clone, Copy, PartialEq)]
153 pub enum Breaks {
154     Consistent,
155     Inconsistent,
156 }
157
158 #[derive(Clone, Copy)]
159 pub struct BreakToken {
160     offset: isize,
161     blank_space: isize
162 }
163
164 #[derive(Clone, Copy)]
165 pub struct BeginToken {
166     offset: isize,
167     breaks: Breaks
168 }
169
170 #[derive(Clone)]
171 pub enum Token {
172     String(String, isize),
173     Break(BreakToken),
174     Begin(BeginToken),
175     End,
176     Eof,
177 }
178
179 impl Token {
180     pub fn is_eof(&self) -> bool {
181         match *self {
182             Token::Eof => true,
183             _ => false,
184         }
185     }
186
187     pub fn is_hardbreak_tok(&self) -> bool {
188         match *self {
189             Token::Break(BreakToken {
190                 offset: 0,
191                 blank_space: bs
192             }) if bs == SIZE_INFINITY =>
193                 true,
194             _ =>
195                 false
196         }
197     }
198 }
199
200 impl fmt::Display for Token {
201     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202         match *self {
203             Token::String(ref s, len) => write!(f, "STR({},{})", s, len),
204             Token::Break(_) => f.write_str("BREAK"),
205             Token::Begin(_) => f.write_str("BEGIN"),
206             Token::End => f.write_str("END"),
207             Token::Eof => f.write_str("EOF"),
208         }
209     }
210 }
211
212 fn buf_str(buf: &[BufEntry], left: usize, right: usize, lim: usize) -> String {
213     let n = buf.len();
214     let mut i = left;
215     let mut l = lim;
216     let mut s = String::from("[");
217     while i != right && l != 0 {
218         l -= 1;
219         if i != left {
220             s.push_str(", ");
221         }
222         s.push_str(&format!("{}={}", buf[i].size, &buf[i].token));
223         i += 1;
224         i %= n;
225     }
226     s.push(']');
227     s
228 }
229
230 #[derive(Copy, Clone)]
231 pub enum PrintStackBreak {
232     Fits,
233     Broken(Breaks),
234 }
235
236 #[derive(Copy, Clone)]
237 pub struct PrintStackElem {
238     offset: isize,
239     pbreak: PrintStackBreak
240 }
241
242 const SIZE_INFINITY: isize = 0xffff;
243
244 pub fn mk_printer<'a>(out: Box<dyn io::Write+'a>, linewidth: usize) -> Printer<'a> {
245     // Yes 55, it makes the ring buffers big enough to never fall behind.
246     let n: usize = 55 * linewidth;
247     debug!("mk_printer {}", linewidth);
248     Printer {
249         out,
250         buf_max_len: n,
251         margin: linewidth as isize,
252         space: linewidth as isize,
253         left: 0,
254         right: 0,
255         // Initialize a single entry; advance_right() will extend it on demand
256         // up to `buf_max_len` elements.
257         buf: vec![BufEntry::default()],
258         left_total: 0,
259         right_total: 0,
260         scan_stack: VecDeque::new(),
261         print_stack: Vec::new(),
262         pending_indentation: 0
263     }
264 }
265
266 pub struct Printer<'a> {
267     out: Box<dyn io::Write+'a>,
268     buf_max_len: usize,
269     /// Width of lines we're constrained to
270     margin: isize,
271     /// Number of spaces left on line
272     space: isize,
273     /// Index of left side of input stream
274     left: usize,
275     /// Index of right side of input stream
276     right: usize,
277     /// Ring-buffer of tokens and calculated sizes
278     buf: Vec<BufEntry>,
279     /// Running size of stream "...left"
280     left_total: isize,
281     /// Running size of stream "...right"
282     right_total: isize,
283     /// Pseudo-stack, really a ring too. Holds the
284     /// primary-ring-buffers index of the Begin that started the
285     /// current block, possibly with the most recent Break after that
286     /// Begin (if there is any) on top of it. Stuff is flushed off the
287     /// bottom as it becomes irrelevant due to the primary ring-buffer
288     /// advancing.
289     scan_stack: VecDeque<usize>,
290     /// Stack of blocks-in-progress being flushed by print
291     print_stack: Vec<PrintStackElem> ,
292     /// Buffered indentation to avoid writing trailing whitespace
293     pending_indentation: isize,
294 }
295
296 #[derive(Clone)]
297 struct BufEntry {
298     token: Token,
299     size: isize,
300 }
301
302 impl Default for BufEntry {
303     fn default() -> Self {
304         BufEntry { token: Token::Eof, size: 0 }
305     }
306 }
307
308 impl<'a> Printer<'a> {
309     pub fn last_token(&mut self) -> Token {
310         self.buf[self.right].token.clone()
311     }
312     /// be very careful with this!
313     pub fn replace_last_token(&mut self, t: Token) {
314         self.buf[self.right].token = t;
315     }
316     pub fn pretty_print(&mut self, token: Token) -> io::Result<()> {
317         debug!("pp Vec<{},{}>", self.left, self.right);
318         match token {
319             Token::Eof => {
320                 if !self.scan_stack.is_empty() {
321                     self.check_stack(0);
322                     self.advance_left()?;
323                 }
324                 self.indent(0);
325                 Ok(())
326             }
327             Token::Begin(b) => {
328                 if self.scan_stack.is_empty() {
329                     self.left_total = 1;
330                     self.right_total = 1;
331                     self.left = 0;
332                     self.right = 0;
333                 } else {
334                     self.advance_right();
335                 }
336                 debug!("pp Begin({})/buffer Vec<{},{}>",
337                        b.offset, self.left, self.right);
338                 self.buf[self.right] = BufEntry { token: token, size: -self.right_total };
339                 let right = self.right;
340                 self.scan_push(right);
341                 Ok(())
342             }
343             Token::End => {
344                 if self.scan_stack.is_empty() {
345                     debug!("pp End/print Vec<{},{}>", self.left, self.right);
346                     self.print(token, 0)
347                 } else {
348                     debug!("pp End/buffer Vec<{},{}>", self.left, self.right);
349                     self.advance_right();
350                     self.buf[self.right] = BufEntry { token: token, size: -1 };
351                     let right = self.right;
352                     self.scan_push(right);
353                     Ok(())
354                 }
355             }
356             Token::Break(b) => {
357                 if self.scan_stack.is_empty() {
358                     self.left_total = 1;
359                     self.right_total = 1;
360                     self.left = 0;
361                     self.right = 0;
362                 } else {
363                     self.advance_right();
364                 }
365                 debug!("pp Break({})/buffer Vec<{},{}>",
366                        b.offset, self.left, self.right);
367                 self.check_stack(0);
368                 let right = self.right;
369                 self.scan_push(right);
370                 self.buf[self.right] = BufEntry { token: token, size: -self.right_total };
371                 self.right_total += b.blank_space;
372                 Ok(())
373             }
374             Token::String(s, len) => {
375                 if self.scan_stack.is_empty() {
376                     debug!("pp String('{}')/print Vec<{},{}>",
377                            s, self.left, self.right);
378                     self.print(Token::String(s, len), len)
379                 } else {
380                     debug!("pp String('{}')/buffer Vec<{},{}>",
381                            s, self.left, self.right);
382                     self.advance_right();
383                     self.buf[self.right] = BufEntry { token: Token::String(s, len), size: len };
384                     self.right_total += len;
385                     self.check_stream()
386                 }
387             }
388         }
389     }
390     pub fn check_stream(&mut self) -> io::Result<()> {
391         debug!("check_stream Vec<{}, {}> with left_total={}, right_total={}",
392                self.left, self.right, self.left_total, self.right_total);
393         if self.right_total - self.left_total > self.space {
394             debug!("scan window is {}, longer than space on line ({})",
395                    self.right_total - self.left_total, self.space);
396             if Some(&self.left) == self.scan_stack.back() {
397                 debug!("setting {} to infinity and popping", self.left);
398                 let scanned = self.scan_pop_bottom();
399                 self.buf[scanned].size = SIZE_INFINITY;
400             }
401             self.advance_left()?;
402             if self.left != self.right {
403                 self.check_stream()?;
404             }
405         }
406         Ok(())
407     }
408     pub fn scan_push(&mut self, x: usize) {
409         debug!("scan_push {}", x);
410         self.scan_stack.push_front(x);
411     }
412     pub fn scan_pop(&mut self) -> usize {
413         self.scan_stack.pop_front().unwrap()
414     }
415     pub fn scan_top(&mut self) -> usize {
416         *self.scan_stack.front().unwrap()
417     }
418     pub fn scan_pop_bottom(&mut self) -> usize {
419         self.scan_stack.pop_back().unwrap()
420     }
421     pub fn advance_right(&mut self) {
422         self.right += 1;
423         self.right %= self.buf_max_len;
424         // Extend the buf if necessary.
425         if self.right == self.buf.len() {
426             self.buf.push(BufEntry::default());
427         }
428         assert_ne!(self.right, self.left);
429     }
430     pub fn advance_left(&mut self) -> io::Result<()> {
431         debug!("advance_left Vec<{},{}>, sizeof({})={}", self.left, self.right,
432                self.left, self.buf[self.left].size);
433
434         let mut left_size = self.buf[self.left].size;
435
436         while left_size >= 0 {
437             let left = self.buf[self.left].token.clone();
438
439             let len = match left {
440                 Token::Break(b) => b.blank_space,
441                 Token::String(_, len) => {
442                     assert_eq!(len, left_size);
443                     len
444                 }
445                 _ => 0
446             };
447
448             self.print(left, left_size)?;
449
450             self.left_total += len;
451
452             if self.left == self.right {
453                 break;
454             }
455
456             self.left += 1;
457             self.left %= self.buf_max_len;
458
459             left_size = self.buf[self.left].size;
460         }
461
462         Ok(())
463     }
464     pub fn check_stack(&mut self, k: isize) {
465         if !self.scan_stack.is_empty() {
466             let x = self.scan_top();
467             match self.buf[x].token {
468                 Token::Begin(_) => {
469                     if k > 0 {
470                         let popped = self.scan_pop();
471                         self.buf[popped].size = self.buf[x].size + self.right_total;
472                         self.check_stack(k - 1);
473                     }
474                 }
475                 Token::End => {
476                     // paper says + not =, but that makes no sense.
477                     let popped = self.scan_pop();
478                     self.buf[popped].size = 1;
479                     self.check_stack(k + 1);
480                 }
481                 _ => {
482                     let popped = self.scan_pop();
483                     self.buf[popped].size = self.buf[x].size + self.right_total;
484                     if k > 0 {
485                         self.check_stack(k);
486                     }
487                 }
488             }
489         }
490     }
491     pub fn print_newline(&mut self, amount: isize) -> io::Result<()> {
492         debug!("NEWLINE {}", amount);
493         let ret = write!(self.out, "\n");
494         self.pending_indentation = 0;
495         self.indent(amount);
496         ret
497     }
498     pub fn indent(&mut self, amount: isize) {
499         debug!("INDENT {}", amount);
500         self.pending_indentation += amount;
501     }
502     pub fn get_top(&mut self) -> PrintStackElem {
503         match self.print_stack.last() {
504             Some(el) => *el,
505             None => PrintStackElem {
506                 offset: 0,
507                 pbreak: PrintStackBreak::Broken(Breaks::Inconsistent)
508             }
509         }
510     }
511     pub fn print_str(&mut self, s: &str) -> io::Result<()> {
512         while self.pending_indentation > 0 {
513             write!(self.out, " ")?;
514             self.pending_indentation -= 1;
515         }
516         write!(self.out, "{}", s)
517     }
518     pub fn print(&mut self, token: Token, l: isize) -> io::Result<()> {
519         debug!("print {} {} (remaining line space={})", token, l,
520                self.space);
521         debug!("{}", buf_str(&self.buf,
522                              self.left,
523                              self.right,
524                              6));
525         match token {
526             Token::Begin(b) => {
527                 if l > self.space {
528                     let col = self.margin - self.space + b.offset;
529                     debug!("print Begin -> push broken block at col {}", col);
530                     self.print_stack.push(PrintStackElem {
531                         offset: col,
532                         pbreak: PrintStackBreak::Broken(b.breaks)
533                     });
534                 } else {
535                     debug!("print Begin -> push fitting block");
536                     self.print_stack.push(PrintStackElem {
537                         offset: 0,
538                         pbreak: PrintStackBreak::Fits
539                     });
540                 }
541                 Ok(())
542             }
543             Token::End => {
544                 debug!("print End -> pop End");
545                 let print_stack = &mut self.print_stack;
546                 assert!(!print_stack.is_empty());
547                 print_stack.pop().unwrap();
548                 Ok(())
549             }
550             Token::Break(b) => {
551                 let top = self.get_top();
552                 match top.pbreak {
553                     PrintStackBreak::Fits => {
554                         debug!("print Break({}) in fitting block", b.blank_space);
555                         self.space -= b.blank_space;
556                         self.indent(b.blank_space);
557                         Ok(())
558                     }
559                     PrintStackBreak::Broken(Breaks::Consistent) => {
560                         debug!("print Break({}+{}) in consistent block",
561                                top.offset, b.offset);
562                         let ret = self.print_newline(top.offset + b.offset);
563                         self.space = self.margin - (top.offset + b.offset);
564                         ret
565                     }
566                     PrintStackBreak::Broken(Breaks::Inconsistent) => {
567                         if l > self.space {
568                             debug!("print Break({}+{}) w/ newline in inconsistent",
569                                    top.offset, b.offset);
570                             let ret = self.print_newline(top.offset + b.offset);
571                             self.space = self.margin - (top.offset + b.offset);
572                             ret
573                         } else {
574                             debug!("print Break({}) w/o newline in inconsistent",
575                                    b.blank_space);
576                             self.indent(b.blank_space);
577                             self.space -= b.blank_space;
578                             Ok(())
579                         }
580                     }
581                 }
582             }
583             Token::String(ref s, len) => {
584                 debug!("print String({})", s);
585                 assert_eq!(l, len);
586                 // assert!(l <= space);
587                 self.space -= len;
588                 self.print_str(s)
589             }
590             Token::Eof => {
591                 // Eof should never get here.
592                 panic!();
593             }
594         }
595     }
596
597     // Convenience functions to talk to the printer.
598
599     /// "raw box"
600     pub fn rbox(&mut self, indent: usize, b: Breaks) -> io::Result<()> {
601         self.pretty_print(Token::Begin(BeginToken {
602             offset: indent as isize,
603             breaks: b
604         }))
605     }
606
607     /// Inconsistent breaking box
608     pub fn ibox(&mut self, indent: usize) -> io::Result<()> {
609         self.rbox(indent, Breaks::Inconsistent)
610     }
611
612     /// Consistent breaking box
613     pub fn cbox(&mut self, indent: usize) -> io::Result<()> {
614         self.rbox(indent, Breaks::Consistent)
615     }
616
617     pub fn break_offset(&mut self, n: usize, off: isize) -> io::Result<()> {
618         self.pretty_print(Token::Break(BreakToken {
619             offset: off,
620             blank_space: n as isize
621         }))
622     }
623
624     pub fn end(&mut self) -> io::Result<()> {
625         self.pretty_print(Token::End)
626     }
627
628     pub fn eof(&mut self) -> io::Result<()> {
629         self.pretty_print(Token::Eof)
630     }
631
632     pub fn word(&mut self, wrd: &str) -> io::Result<()> {
633         self.pretty_print(Token::String(wrd.to_string(), wrd.len() as isize))
634     }
635
636     pub fn huge_word(&mut self, wrd: &str) -> io::Result<()> {
637         self.pretty_print(Token::String(wrd.to_string(), SIZE_INFINITY))
638     }
639
640     pub fn zero_word(&mut self, wrd: &str) -> io::Result<()> {
641         self.pretty_print(Token::String(wrd.to_string(), 0))
642     }
643
644     fn spaces(&mut self, n: usize) -> io::Result<()> {
645         self.break_offset(n, 0)
646     }
647
648     pub fn zerobreak(&mut self) -> io::Result<()> {
649         self.spaces(0)
650     }
651
652     pub fn space(&mut self) -> io::Result<()> {
653         self.spaces(1)
654     }
655
656     pub fn hardbreak(&mut self) -> io::Result<()> {
657         self.spaces(SIZE_INFINITY as usize)
658     }
659
660     pub fn hardbreak_tok_offset(off: isize) -> Token {
661         Token::Break(BreakToken {offset: off, blank_space: SIZE_INFINITY})
662     }
663
664     pub fn hardbreak_tok() -> Token {
665         Self::hardbreak_tok_offset(0)
666     }
667 }