Skip to content

Commit

Permalink
Rollup merge of #93102 - dtolnay:ringbuffer, r=lcnr
Browse files Browse the repository at this point in the history
Pretty printer algorithm revamp step 3

This PR follows #93065 as a third chunk of minor modernizations backported from https://github.com/dtolnay/prettyplease into rustc_ast_pretty.

I've broken this up into atomic commits that hopefully are sensible in isolation. At every commit, the pretty printer is compilable and has runtime behavior that is identical to before and after the PR. None of the refactoring so far changes behavior.

This PR is the last chunk of non-behavior-changing cleanup. After this the **next PR** will begin backporting behavior changes from `prettyplease`, starting with block indentation:

```rust
macro_rules! print_expr {
    ($expr:expr) => {
        println!("{}", stringify!($expr));
    };
}

fn main() {
    print_expr!(Struct { x: 0, y: 0 });
    print_expr!(Structtttttttttttttttttttttttttttttttttttttttttttttttttt { xxxxxxxxx: 0, yyyyyyyyy: 0 });
}
```

Output currently on master (nowhere near modern Rust style):

```console
Struct{x: 0, y: 0,}
Structtttttttttttttttttttttttttttttttttttttttttttttttttt{xxxxxxxxx: 0,
                                                         yyyyyyyyy: 0,}
```

After the upcoming PR for block indentation (based on dtolnay/prettyplease@401d60c):

```console
Struct { x: 0, y: 0, }
Structtttttttttttttttttttttttttttttttttttttttttttttttttt {
    xxxxxxxxx: 0,
    yyyyyyyyy: 0,
}
```

And the PR after that, for intelligent trailing commas (based on dtolnay/prettyplease@e2a0297):

```console
Struct { x: 0, y: 0 }
Structtttttttttttttttttttttttttttttttttttttttttttttttttt {
    xxxxxxxxx: 0,
    yyyyyyyyy: 0,
}
```
  • Loading branch information
matthiaskrgr authored Jan 20, 2022
2 parents 51fd48f + 21c1571 commit d4ec464
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 133 deletions.
205 changes: 77 additions & 128 deletions compiler/rustc_ast_pretty/src/pp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ mod ring;
use ring::RingBuffer;
use std::borrow::Cow;
use std::collections::VecDeque;
use std::fmt;
use std::iter;

/// How to break. Described in more detail in the module docs.
#[derive(Clone, Copy, PartialEq)]
Expand Down Expand Up @@ -175,27 +175,10 @@ impl Token {
}
}

impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Token::String(ref s) => write!(f, "STR({},{})", s, s.len()),
Token::Break(_) => f.write_str("BREAK"),
Token::Begin(_) => f.write_str("BEGIN"),
Token::End => f.write_str("END"),
}
}
}

#[derive(Copy, Clone)]
enum PrintStackBreak {
enum PrintFrame {
Fits,
Broken(Breaks),
}

#[derive(Copy, Clone)]
struct PrintStackElem {
offset: isize,
pbreak: PrintStackBreak,
Broken { offset: isize, breaks: Breaks },
}

const SIZE_INFINITY: isize = 0xffff;
Expand All @@ -220,7 +203,7 @@ pub struct Printer {
/// advancing.
scan_stack: VecDeque<usize>,
/// Stack of blocks-in-progress being flushed by print
print_stack: Vec<PrintStackElem>,
print_stack: Vec<PrintFrame>,
/// Buffered indentation to avoid writing trailing whitespace
pending_indentation: isize,
/// The token most recently popped from the left boundary of the
Expand Down Expand Up @@ -260,8 +243,8 @@ impl Printer {
}

/// Be very careful with this!
pub fn replace_last_token_still_buffered(&mut self, t: Token) {
self.buf.last_mut().unwrap().token = t;
pub fn replace_last_token_still_buffered(&mut self, token: Token) {
self.buf.last_mut().unwrap().token = token;
}

fn scan_eof(&mut self) {
Expand All @@ -271,53 +254,53 @@ impl Printer {
}
}

fn scan_begin(&mut self, b: BeginToken) {
fn scan_begin(&mut self, token: BeginToken) {
if self.scan_stack.is_empty() {
self.left_total = 1;
self.right_total = 1;
self.buf.clear();
}
let right = self.buf.push(BufEntry { token: Token::Begin(b), size: -self.right_total });
self.scan_stack.push_front(right);
let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total });
self.scan_stack.push_back(right);
}

fn scan_end(&mut self) {
if self.scan_stack.is_empty() {
self.print_end();
} else {
let right = self.buf.push(BufEntry { token: Token::End, size: -1 });
self.scan_stack.push_front(right);
self.scan_stack.push_back(right);
}
}

fn scan_break(&mut self, b: BreakToken) {
fn scan_break(&mut self, token: BreakToken) {
if self.scan_stack.is_empty() {
self.left_total = 1;
self.right_total = 1;
self.buf.clear();
} else {
self.check_stack(0);
}
let right = self.buf.push(BufEntry { token: Token::Break(b), size: -self.right_total });
self.scan_stack.push_front(right);
self.right_total += b.blank_space;
let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total });
self.scan_stack.push_back(right);
self.right_total += token.blank_space;
}

fn scan_string(&mut self, s: Cow<'static, str>) {
fn scan_string(&mut self, string: Cow<'static, str>) {
if self.scan_stack.is_empty() {
self.print_string(&s);
self.print_string(&string);
} else {
let len = s.len() as isize;
self.buf.push(BufEntry { token: Token::String(s), size: len });
let len = string.len() as isize;
self.buf.push(BufEntry { token: Token::String(string), size: len });
self.right_total += len;
self.check_stream();
}
}

fn check_stream(&mut self) {
while self.right_total - self.left_total > self.space {
if *self.scan_stack.back().unwrap() == self.buf.index_of_first() {
self.scan_stack.pop_back().unwrap();
if *self.scan_stack.front().unwrap() == self.buf.index_of_first() {
self.scan_stack.pop_front().unwrap();
self.buf.first_mut().unwrap().size = SIZE_INFINITY;
}
self.advance_left();
Expand All @@ -328,152 +311,118 @@ impl Printer {
}

fn advance_left(&mut self) {
let mut left_size = self.buf.first().unwrap().size;

while left_size >= 0 {
let left = self.buf.first().unwrap().token.clone();
while self.buf.first().unwrap().size >= 0 {
let left = self.buf.pop_first().unwrap();

let len = match left {
Token::Break(b) => b.blank_space,
Token::String(ref s) => {
let len = s.len() as isize;
assert_eq!(len, left_size);
len
match &left.token {
Token::String(string) => {
self.left_total += string.len() as isize;
self.print_string(string);
}
_ => 0,
};

self.print(left, left_size);
Token::Break(token) => {
self.left_total += token.blank_space;
self.print_break(*token, left.size);
}
Token::Begin(token) => self.print_begin(*token, left.size),
Token::End => self.print_end(),
}

self.left_total += len;
self.last_printed = Some(left.token);

self.buf.advance_left();
if self.buf.is_empty() {
break;
}

left_size = self.buf.first().unwrap().size;
}
}

fn check_stack(&mut self, mut k: usize) {
while let Some(&x) = self.scan_stack.front() {
let mut entry = &mut self.buf[x];
fn check_stack(&mut self, mut depth: usize) {
while let Some(&index) = self.scan_stack.back() {
let mut entry = &mut self.buf[index];
match entry.token {
Token::Begin(_) => {
if k == 0 {
if depth == 0 {
break;
}
self.scan_stack.pop_front().unwrap();
self.scan_stack.pop_back().unwrap();
entry.size += self.right_total;
k -= 1;
depth -= 1;
}
Token::End => {
// paper says + not =, but that makes no sense.
self.scan_stack.pop_front().unwrap();
self.scan_stack.pop_back().unwrap();
entry.size = 1;
k += 1;
depth += 1;
}
_ => {
self.scan_stack.pop_front().unwrap();
self.scan_stack.pop_back().unwrap();
entry.size += self.right_total;
if k == 0 {
if depth == 0 {
break;
}
}
}
}
}

fn print_newline(&mut self, amount: isize) {
self.out.push('\n');
self.pending_indentation = 0;
self.indent(amount);
}

fn indent(&mut self, amount: isize) {
self.pending_indentation += amount;
fn get_top(&self) -> PrintFrame {
*self
.print_stack
.last()
.unwrap_or(&PrintFrame::Broken { offset: 0, breaks: Breaks::Inconsistent })
}

fn get_top(&self) -> PrintStackElem {
*self.print_stack.last().unwrap_or({
&PrintStackElem { offset: 0, pbreak: PrintStackBreak::Broken(Breaks::Inconsistent) }
})
}

fn print_begin(&mut self, b: BeginToken, l: isize) {
if l > self.space {
let col = self.margin - self.space + b.offset;
self.print_stack
.push(PrintStackElem { offset: col, pbreak: PrintStackBreak::Broken(b.breaks) });
fn print_begin(&mut self, token: BeginToken, size: isize) {
if size > self.space {
let col = self.margin - self.space + token.offset;
self.print_stack.push(PrintFrame::Broken { offset: col, breaks: token.breaks });
} else {
self.print_stack.push(PrintStackElem { offset: 0, pbreak: PrintStackBreak::Fits });
self.print_stack.push(PrintFrame::Fits);
}
}

fn print_end(&mut self) {
self.print_stack.pop().unwrap();
}

fn print_break(&mut self, b: BreakToken, l: isize) {
let top = self.get_top();
match top.pbreak {
PrintStackBreak::Fits => {
self.space -= b.blank_space;
self.indent(b.blank_space);
}
PrintStackBreak::Broken(Breaks::Consistent) => {
self.print_newline(top.offset + b.offset);
self.space = self.margin - (top.offset + b.offset);
}
PrintStackBreak::Broken(Breaks::Inconsistent) => {
if l > self.space {
self.print_newline(top.offset + b.offset);
self.space = self.margin - (top.offset + b.offset);
} else {
self.indent(b.blank_space);
self.space -= b.blank_space;
fn print_break(&mut self, token: BreakToken, size: isize) {
let break_offset =
match self.get_top() {
PrintFrame::Fits => None,
PrintFrame::Broken { offset, breaks: Breaks::Consistent } => Some(offset),
PrintFrame::Broken { offset, breaks: Breaks::Inconsistent } => {
if size > self.space { Some(offset) } else { None }
}
}
};
if let Some(offset) = break_offset {
self.out.push('\n');
self.pending_indentation = offset + token.offset;
self.space = self.margin - (offset + token.offset);
} else {
self.pending_indentation += token.blank_space;
self.space -= token.blank_space;
}
}

fn print_string(&mut self, s: &str) {
let len = s.len() as isize;
// assert!(len <= space);
self.space -= len;

fn print_string(&mut self, string: &str) {
// Write the pending indent. A more concise way of doing this would be:
//
// write!(self.out, "{: >n$}", "", n = self.pending_indentation as usize)?;
//
// But that is significantly slower. This code is sufficiently hot, and indents can get
// sufficiently large, that the difference is significant on some workloads.
self.out.reserve(self.pending_indentation as usize);
self.out.extend(std::iter::repeat(' ').take(self.pending_indentation as usize));
self.out.extend(iter::repeat(' ').take(self.pending_indentation as usize));
self.pending_indentation = 0;
self.out.push_str(s);
}

fn print(&mut self, token: Token, l: isize) {
match &token {
Token::Begin(b) => self.print_begin(*b, l),
Token::End => self.print_end(),
Token::Break(b) => self.print_break(*b, l),
Token::String(s) => {
let len = s.len() as isize;
assert_eq!(len, l);
self.print_string(s);
}
}
self.last_printed = Some(token);
self.out.push_str(string);
self.space -= string.len() as isize;
}

// Convenience functions to talk to the printer.

/// "raw box"
pub fn rbox(&mut self, indent: usize, b: Breaks) {
self.scan_begin(BeginToken { offset: indent as isize, breaks: b })
pub fn rbox(&mut self, indent: usize, breaks: Breaks) {
self.scan_begin(BeginToken { offset: indent as isize, breaks })
}

/// Inconsistent breaking box
Expand All @@ -500,8 +449,8 @@ impl Printer {
}

pub fn word<S: Into<Cow<'static, str>>>(&mut self, wrd: S) {
let s = wrd.into();
self.scan_string(s)
let string = wrd.into();
self.scan_string(string)
}

fn spaces(&mut self, n: usize) {
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_ast_pretty/src/pp/ring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ impl<T> RingBuffer<T> {
index
}

pub fn advance_left(&mut self) {
self.data.pop_front().unwrap();
self.offset += 1;
}

pub fn clear(&mut self) {
self.data.clear();
}
Expand All @@ -53,6 +48,12 @@ impl<T> RingBuffer<T> {
self.data.front_mut()
}

pub fn pop_first(&mut self) -> Option<T> {
let first = self.data.pop_front()?;
self.offset += 1;
Some(first)
}

pub fn last(&self) -> Option<&T> {
self.data.back()
}
Expand Down

0 comments on commit d4ec464

Please sign in to comment.