use std::io;
use std::string;
#[derive(Clone, Copy, PartialEq)]
pub enum Breaks {
Consistent,
Inconsistent,
}
#[derive(Clone, Copy)]
pub struct BreakToken {
offset: isize,
blank_space: isize
}
#[derive(Clone, Copy)]
pub struct BeginToken {
offset: isize,
breaks: Breaks
}
#[derive(Clone)]
pub enum Token {
String(String, isize),
Break(BreakToken),
Begin(BeginToken),
End,
Eof,
}
impl Token {
pub fn is_eof(&self) -> bool {
match *self {
Token::Eof => true,
_ => false,
}
}
pub fn is_hardbreak_tok(&self) -> bool {
match *self {
Token::Break(BreakToken {
offset: 0,
blank_space: bs
}) if bs == SIZE_INFINITY =>
true,
_ =>
false
}
}
}
pub fn tok_str(token: &Token) -> String {
match *token {
Token::String(ref s, len) => format!("STR({},{})", s, len),
Token::Break(_) => "BREAK".to_string(),
Token::Begin(_) => "BEGIN".to_string(),
Token::End => "END".to_string(),
Token::Eof => "EOF".to_string()
}
}
pub fn buf_str(toks: &[Token],
szs: &[isize],
left: usize,
right: usize,
lim: usize)
-> String {
let n = toks.len();
assert_eq!(n, szs.len());
let mut i = left;
let mut l = lim;
let mut s = string::String::from("[");
while i != right && l != 0 {
l -= 1;
if i != left {
s.push_str(", ");
}
s.push_str(&format!("{}={}",
szs[i],
tok_str(&toks[i])));
i += 1;
i %= n;
}
s.push(']');
s
}
#[derive(Copy, Clone)]
pub enum PrintStackBreak {
Fits,
Broken(Breaks),
}
#[derive(Copy, Clone)]
pub struct PrintStackElem {
offset: isize,
pbreak: PrintStackBreak
}
const SIZE_INFINITY: isize = 0xffff;
pub fn mk_printer<'a>(out: Box<io::Write+'a>, linewidth: usize) -> Printer<'a> {
let n: usize = 3 * linewidth;
debug!("mk_printer {}", linewidth);
let token = vec![Token::Eof; n];
let size = vec![0; n];
let scan_stack = vec![0; n];
Printer {
out: out,
buf_len: n,
margin: linewidth as isize,
space: linewidth as isize,
left: 0,
right: 0,
token: token,
size: size,
left_total: 0,
right_total: 0,
scan_stack: scan_stack,
scan_stack_empty: true,
top: 0,
bottom: 0,
print_stack: Vec::new(),
pending_indentation: 0
}
}
pub struct Printer<'a> {
pub out: Box<io::Write+'a>,
buf_len: usize,
margin: isize,
space: isize,
left: usize,
right: usize,
token: Vec<Token> ,
size: Vec<isize> ,
left_total: isize,
right_total: isize,
scan_stack: Vec<usize> ,
scan_stack_empty: bool,
top: usize,
bottom: usize,
print_stack: Vec<PrintStackElem> ,
pending_indentation: isize,
}
impl<'a> Printer<'a> {
pub fn last_token(&mut self) -> Token {
self.token[self.right].clone()
}
pub fn replace_last_token(&mut self, t: Token) {
self.token[self.right] = t;
}
pub fn pretty_print(&mut self, token: Token) -> io::Result<()> {
debug!("pp Vec<{},{}>", self.left, self.right);
match token {
Token::Eof => {
if !self.scan_stack_empty {
self.check_stack(0);
self.advance_left()?;
}
self.indent(0);
Ok(())
}
Token::Begin(b) => {
if self.scan_stack_empty {
self.left_total = 1;
self.right_total = 1;
self.left = 0;
self.right = 0;
} else { self.advance_right(); }
debug!("pp Begin({})/buffer Vec<{},{}>",
b.offset, self.left, self.right);
self.token[self.right] = token;
self.size[self.right] = -self.right_total;
let right = self.right;
self.scan_push(right);
Ok(())
}
Token::End => {
if self.scan_stack_empty {
debug!("pp End/print Vec<{},{}>", self.left, self.right);
self.print(token, 0)
} else {
debug!("pp End/buffer Vec<{},{}>", self.left, self.right);
self.advance_right();
self.token[self.right] = token;
self.size[self.right] = -1;
let right = self.right;
self.scan_push(right);
Ok(())
}
}
Token::Break(b) => {
if self.scan_stack_empty {
self.left_total = 1;
self.right_total = 1;
self.left = 0;
self.right = 0;
} else { self.advance_right(); }
debug!("pp Break({})/buffer Vec<{},{}>",
b.offset, self.left, self.right);
self.check_stack(0);
let right = self.right;
self.scan_push(right);
self.token[self.right] = token;
self.size[self.right] = -self.right_total;
self.right_total += b.blank_space;
Ok(())
}
Token::String(s, len) => {
if self.scan_stack_empty {
debug!("pp String('{}')/print Vec<{},{}>",
s, self.left, self.right);
self.print(Token::String(s, len), len)
} else {
debug!("pp String('{}')/buffer Vec<{},{}>",
s, self.left, self.right);
self.advance_right();
self.token[self.right] = Token::String(s, len);
self.size[self.right] = len;
self.right_total += len;
self.check_stream()
}
}
}
}
pub fn check_stream(&mut self) -> io::Result<()> {
debug!("check_stream Vec<{}, {}> with left_total={}, right_total={}",
self.left, self.right, self.left_total, self.right_total);
if self.right_total - self.left_total > self.space {
debug!("scan window is {}, longer than space on line ({})",
self.right_total - self.left_total, self.space);
if !self.scan_stack_empty {
if self.left == self.scan_stack[self.bottom] {
debug!("setting {} to infinity and popping", self.left);
let scanned = self.scan_pop_bottom();
self.size[scanned] = SIZE_INFINITY;
}
}
self.advance_left()?;
if self.left != self.right {
self.check_stream()?;
}
}
Ok(())
}
pub fn scan_push(&mut self, x: usize) {
debug!("scan_push {}", x);
if self.scan_stack_empty {
self.scan_stack_empty = false;
} else {
self.top += 1;
self.top %= self.buf_len;
assert!((self.top != self.bottom));
}
self.scan_stack[self.top] = x;
}
pub fn scan_pop(&mut self) -> usize {
assert!((!self.scan_stack_empty));
let x = self.scan_stack[self.top];
if self.top == self.bottom {
self.scan_stack_empty = true;
} else {
self.top += self.buf_len - 1; self.top %= self.buf_len;
}
return x;
}
pub fn scan_top(&mut self) -> usize {
assert!((!self.scan_stack_empty));
return self.scan_stack[self.top];
}
pub fn scan_pop_bottom(&mut self) -> usize {
assert!((!self.scan_stack_empty));
let x = self.scan_stack[self.bottom];
if self.top == self.bottom {
self.scan_stack_empty = true;
} else {
self.bottom += 1; self.bottom %= self.buf_len;
}
return x;
}
pub fn advance_right(&mut self) {
self.right += 1;
self.right %= self.buf_len;
assert!((self.right != self.left));
}
pub fn advance_left(&mut self) -> io::Result<()> {
debug!("advance_left Vec<{},{}>, sizeof({})={}", self.left, self.right,
self.left, self.size[self.left]);
let mut left_size = self.size[self.left];
while left_size >= 0 {
let left = self.token[self.left].clone();
let len = match left {
Token::Break(b) => b.blank_space,
Token::String(_, len) => {
assert_eq!(len, left_size);
len
}
_ => 0
};
self.print(left, left_size)?;
self.left_total += len;
if self.left == self.right {
break;
}
self.left += 1;
self.left %= self.buf_len;
left_size = self.size[self.left];
}
Ok(())
}
pub fn check_stack(&mut self, k: isize) {
if !self.scan_stack_empty {
let x = self.scan_top();
match self.token[x] {
Token::Begin(_) => {
if k > 0 {
let popped = self.scan_pop();
self.size[popped] = self.size[x] + self.right_total;
self.check_stack(k - 1);
}
}
Token::End => {
let popped = self.scan_pop();
self.size[popped] = 1;
self.check_stack(k + 1);
}
_ => {
let popped = self.scan_pop();
self.size[popped] = self.size[x] + self.right_total;
if k > 0 {
self.check_stack(k);
}
}
}
}
}
pub fn print_newline(&mut self, amount: isize) -> io::Result<()> {
debug!("NEWLINE {}", amount);
let ret = write!(self.out, "\n");
self.pending_indentation = 0;
self.indent(amount);
return ret;
}
pub fn indent(&mut self, amount: isize) {
debug!("INDENT {}", amount);
self.pending_indentation += amount;
}
pub fn get_top(&mut self) -> PrintStackElem {
let print_stack = &mut self.print_stack;
let n = print_stack.len();
if n != 0 {
(*print_stack)[n - 1]
} else {
PrintStackElem {
offset: 0,
pbreak: PrintStackBreak::Broken(Breaks::Inconsistent)
}
}
}
pub fn print_str(&mut self, s: &str) -> io::Result<()> {
while self.pending_indentation > 0 {
write!(self.out, " ")?;
self.pending_indentation -= 1;
}
write!(self.out, "{}", s)
}
pub fn print(&mut self, token: Token, l: isize) -> io::Result<()> {
debug!("print {} {} (remaining line space={})", tok_str(&token), l,
self.space);
debug!("{}", buf_str(&self.token,
&self.size,
self.left,
self.right,
6));
match token {
Token::Begin(b) => {
if l > self.space {
let col = self.margin - self.space + b.offset;
debug!("print Begin -> push broken block at col {}", col);
self.print_stack.push(PrintStackElem {
offset: col,
pbreak: PrintStackBreak::Broken(b.breaks)
});
} else {
debug!("print Begin -> push fitting block");
self.print_stack.push(PrintStackElem {
offset: 0,
pbreak: PrintStackBreak::Fits
});
}
Ok(())
}
Token::End => {
debug!("print End -> pop End");
let print_stack = &mut self.print_stack;
assert!((!print_stack.is_empty()));
print_stack.pop().unwrap();
Ok(())
}
Token::Break(b) => {
let top = self.get_top();
match top.pbreak {
PrintStackBreak::Fits => {
debug!("print Break({}) in fitting block", b.blank_space);
self.space -= b.blank_space;
self.indent(b.blank_space);
Ok(())
}
PrintStackBreak::Broken(Breaks::Consistent) => {
debug!("print Break({}+{}) in consistent block",
top.offset, b.offset);
let ret = self.print_newline(top.offset + b.offset);
self.space = self.margin - (top.offset + b.offset);
ret
}
PrintStackBreak::Broken(Breaks::Inconsistent) => {
if l > self.space {
debug!("print Break({}+{}) w/ newline in inconsistent",
top.offset, b.offset);
let ret = self.print_newline(top.offset + b.offset);
self.space = self.margin - (top.offset + b.offset);
ret
} else {
debug!("print Break({}) w/o newline in inconsistent",
b.blank_space);
self.indent(b.blank_space);
self.space -= b.blank_space;
Ok(())
}
}
}
}
Token::String(s, len) => {
debug!("print String({})", s);
assert_eq!(l, len);
self.space -= len;
self.print_str(&s[..])
}
Token::Eof => {
panic!();
}
}
}
}
pub fn rbox(p: &mut Printer, indent: usize, b: Breaks) -> io::Result<()> {
p.pretty_print(Token::Begin(BeginToken {
offset: indent as isize,
breaks: b
}))
}
pub fn ibox(p: &mut Printer, indent: usize) -> io::Result<()> {
rbox(p, indent, Breaks::Inconsistent)
}
pub fn cbox(p: &mut Printer, indent: usize) -> io::Result<()> {
rbox(p, indent, Breaks::Consistent)
}
pub fn break_offset(p: &mut Printer, n: usize, off: isize) -> io::Result<()> {
p.pretty_print(Token::Break(BreakToken {
offset: off,
blank_space: n as isize
}))
}
pub fn end(p: &mut Printer) -> io::Result<()> {
p.pretty_print(Token::End)
}
pub fn eof(p: &mut Printer) -> io::Result<()> {
p.pretty_print(Token::Eof)
}
pub fn word(p: &mut Printer, wrd: &str) -> io::Result<()> {
p.pretty_print(Token::String( wrd.to_string(), wrd.len() as isize))
}
pub fn huge_word(p: &mut Printer, wrd: &str) -> io::Result<()> {
p.pretty_print(Token::String( wrd.to_string(), SIZE_INFINITY))
}
pub fn zero_word(p: &mut Printer, wrd: &str) -> io::Result<()> {
p.pretty_print(Token::String( wrd.to_string(), 0))
}
pub fn spaces(p: &mut Printer, n: usize) -> io::Result<()> {
break_offset(p, n, 0)
}
pub fn zerobreak(p: &mut Printer) -> io::Result<()> {
spaces(p, 0)
}
pub fn space(p: &mut Printer) -> io::Result<()> {
spaces(p, 1)
}
pub fn hardbreak(p: &mut Printer) -> io::Result<()> {
spaces(p, SIZE_INFINITY as usize)
}
pub fn hardbreak_tok_offset(off: isize) -> Token {
Token::Break(BreakToken {offset: off, blank_space: SIZE_INFINITY})
}
pub fn hardbreak_tok() -> Token {
hardbreak_tok_offset(0)
}