]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/print/pprust.rs
Get rid of structural records in libsyntax and the last bit in librustc.
[rust.git] / src / libsyntax / print / pprust.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 use core::prelude::*;
12
13 use ast::{RegionTyParamBound, TraitTyParamBound, required, provided};
14 use ast;
15 use ast_util;
16 use ast_util::{operator_prec};
17 use attr;
18 use codemap::{CodeMap, BytePos};
19 use codemap;
20 use diagnostic;
21 use parse::classify::{expr_is_simple_block, expr_requires_semi_to_be_stmt};
22 use parse::classify::{stmt_ends_with_semi};
23 use parse::token::ident_interner;
24 use parse::{comments, lexer, token};
25 use parse;
26 use print::pp::{break_offset, word, Printer, space, zerobreak, hardbreak};
27 use print::pp::{breaks, consistent, inconsistent, eof};
28 use print::pp;
29 use print::pprust;
30
31 use core::char;
32 use core::dvec::DVec;
33 use core::io;
34 use core::option;
35 use core::str::{push_str, push_char};
36 use core::str;
37 use core::u64;
38 use core::vec;
39
40 // The @ps is stored here to prevent recursive type.
41 pub enum ann_node {
42     node_block(@ps, ast::blk),
43     node_item(@ps, @ast::item),
44     node_expr(@ps, @ast::expr),
45     node_pat(@ps, @ast::pat),
46 }
47 pub struct pp_ann {
48     pre: fn@(ann_node),
49     post: fn@(ann_node)
50 }
51
52 pub fn no_ann() -> pp_ann {
53     fn ignore(_node: ann_node) { }
54     return pp_ann {pre: ignore, post: ignore};
55 }
56
57 pub struct CurrentCommentAndLiteral {
58     cur_cmnt: uint,
59     cur_lit: uint,
60 }
61
62 pub struct ps {
63     s: @mut pp::Printer,
64     cm: Option<@CodeMap>,
65     intr: @token::ident_interner,
66     comments: Option<~[comments::cmnt]>,
67     literals: Option<~[comments::lit]>,
68     cur_cmnt_and_lit: @mut CurrentCommentAndLiteral,
69     boxes: DVec<pp::breaks>,
70     ann: pp_ann
71 }
72
73 pub fn ibox(s: @ps, u: uint) {
74     s.boxes.push(pp::inconsistent);
75     pp::ibox(s.s, u);
76 }
77
78 pub fn end(s: @ps) {
79     s.boxes.pop();
80     pp::end(s.s);
81 }
82
83 pub fn rust_printer(writer: io::Writer, intr: @ident_interner) -> @ps {
84     return @ps {
85         s: pp::mk_printer(writer, default_columns),
86         cm: None::<@CodeMap>,
87         intr: intr,
88         comments: None::<~[comments::cmnt]>,
89         literals: None::<~[comments::lit]>,
90         cur_cmnt_and_lit: @mut CurrentCommentAndLiteral {
91             cur_cmnt: 0,
92             cur_lit: 0
93         },
94         boxes: DVec(),
95         ann: no_ann()
96     };
97 }
98
99 pub const indent_unit: uint = 4u;
100 pub const match_indent_unit: uint = 2u;
101
102 pub const default_columns: uint = 78u;
103
104 // Requires you to pass an input filename and reader so that
105 // it can scan the input text for comments and literals to
106 // copy forward.
107 pub fn print_crate(cm: @CodeMap, intr: @ident_interner,
108                    span_diagnostic: diagnostic::span_handler,
109                    crate: @ast::crate, filename: ~str, in: io::Reader,
110                    out: io::Writer, ann: pp_ann, is_expanded: bool) {
111     let (cmnts, lits) =
112         comments::gather_comments_and_literals(span_diagnostic,
113                                                filename, in);
114     let s = @ps {
115         s: pp::mk_printer(out, default_columns),
116         cm: Some(cm),
117         intr: intr,
118         comments: Some(cmnts),
119         // If the code is post expansion, don't use the table of
120         // literals, since it doesn't correspond with the literals
121         // in the AST anymore.
122         literals: if is_expanded { None } else { Some(lits) },
123         cur_cmnt_and_lit: @mut CurrentCommentAndLiteral {
124             cur_cmnt: 0,
125             cur_lit: 0
126         },
127         boxes: DVec(),
128         ann: ann
129     };
130     print_crate_(s, crate);
131 }
132
133 pub fn print_crate_(s: @ps, &&crate: @ast::crate) {
134     print_mod(s, crate.node.module, crate.node.attrs);
135     print_remaining_comments(s);
136     eof(s.s);
137 }
138
139 pub fn ty_to_str(ty: @ast::Ty, intr: @ident_interner) -> ~str {
140     to_str(ty, print_type, intr)
141 }
142
143 pub fn pat_to_str(pat: @ast::pat, intr: @ident_interner) -> ~str {
144     to_str(pat, print_irrefutable_pat, intr)
145 }
146
147 pub fn expr_to_str(e: @ast::expr, intr: @ident_interner) -> ~str {
148     to_str(e, print_expr, intr)
149 }
150
151 pub fn tt_to_str(tt: ast::token_tree, intr: @ident_interner) -> ~str {
152     to_str(tt, print_tt, intr)
153 }
154
155 pub fn tts_to_str(tts: &[ast::token_tree], intr: @ident_interner) -> ~str {
156     to_str(tts, print_tts, intr)
157 }
158
159 pub fn stmt_to_str(s: ast::stmt, intr: @ident_interner) -> ~str {
160     to_str(s, print_stmt, intr)
161 }
162
163 pub fn item_to_str(i: @ast::item, intr: @ident_interner) -> ~str {
164     to_str(i, print_item, intr)
165 }
166
167 pub fn typarams_to_str(tps: ~[ast::ty_param], intr: @ident_interner) -> ~str {
168     to_str(tps, print_type_params, intr)
169 }
170
171 pub fn path_to_str(&&p: @ast::path, intr: @ident_interner) -> ~str {
172     to_str(p, |a,b| print_path(a, b, false), intr)
173 }
174
175 pub fn fun_to_str(decl: ast::fn_decl, name: ast::ident,
176                   params: ~[ast::ty_param], intr: @ident_interner) -> ~str {
177     do io::with_str_writer |wr| {
178         let s = rust_printer(wr, intr);
179         print_fn(s, decl, None, name, params, None, ast::inherited);
180         end(s); // Close the head box
181         end(s); // Close the outer box
182         eof(s.s);
183     }
184 }
185
186 pub fn block_to_str(blk: ast::blk, intr: @ident_interner) -> ~str {
187     do io::with_str_writer |wr| {
188         let s = rust_printer(wr, intr);
189         // containing cbox, will be closed by print-block at }
190         cbox(s, indent_unit);
191         // head-ibox, will be closed by print-block after {
192         ibox(s, 0u);
193         print_block(s, blk);
194         eof(s.s);
195     }
196 }
197
198 pub fn meta_item_to_str(mi: @ast::meta_item, intr: @ident_interner) -> ~str {
199     to_str(mi, print_meta_item, intr)
200 }
201
202 pub fn attribute_to_str(attr: ast::attribute, intr: @ident_interner) -> ~str {
203     to_str(attr, print_attribute, intr)
204 }
205
206 pub fn variant_to_str(var: ast::variant, intr: @ident_interner) -> ~str {
207     to_str(var, print_variant, intr)
208 }
209
210 pub fn cbox(s: @ps, u: uint) {
211     s.boxes.push(pp::consistent);
212     pp::cbox(s.s, u);
213 }
214
215 pub fn box(s: @ps, u: uint, b: pp::breaks) {
216     s.boxes.push(b);
217     pp::box(s.s, u, b);
218 }
219
220 pub fn nbsp(s: @ps) { word(s.s, ~" "); }
221
222 pub fn word_nbsp(s: @ps, w: ~str) { word(s.s, w); nbsp(s); }
223
224 pub fn word_space(s: @ps, w: ~str) { word(s.s, w); space(s.s); }
225
226 pub fn popen(s: @ps) { word(s.s, ~"("); }
227
228 pub fn pclose(s: @ps) { word(s.s, ~")"); }
229
230 pub fn head(s: @ps, w: ~str) {
231     // outer-box is consistent
232     cbox(s, indent_unit);
233     // head-box is inconsistent
234     ibox(s, str::len(w) + 1);
235     // keyword that starts the head
236     if !w.is_empty() {
237         word_nbsp(s, w);
238     }
239 }
240
241 pub fn bopen(s: @ps) {
242     word(s.s, ~"{");
243     end(s); // close the head-box
244 }
245
246 pub fn bclose_(s: @ps, span: codemap::span, indented: uint) {
247     bclose_maybe_open(s, span, indented, true);
248 }
249 pub fn bclose_maybe_open (s: @ps, span: codemap::span, indented: uint,
250                           close_box: bool) {
251     maybe_print_comment(s, span.hi);
252     break_offset_if_not_bol(s, 1u, -(indented as int));
253     word(s.s, ~"}");
254     if close_box {
255         end(s); // close the outer-box
256     }
257 }
258 pub fn bclose(s: @ps, span: codemap::span) { bclose_(s, span, indent_unit); }
259
260 pub fn is_begin(s: @ps) -> bool {
261     match s.s.last_token() { pp::BEGIN(_) => true, _ => false }
262 }
263
264 pub fn is_end(s: @ps) -> bool {
265     match s.s.last_token() { pp::END => true, _ => false }
266 }
267
268 pub fn is_bol(s: @ps) -> bool {
269     return s.s.last_token().is_eof() || s.s.last_token().is_hardbreak_tok();
270 }
271
272 pub fn in_cbox(s: @ps) -> bool {
273     let len = s.boxes.len();
274     if len == 0u { return false; }
275     return s.boxes[len - 1u] == pp::consistent;
276 }
277
278 pub fn hardbreak_if_not_bol(s: @ps) { if !is_bol(s) { hardbreak(s.s); } }
279 pub fn space_if_not_bol(s: @ps) { if !is_bol(s) { space(s.s); } }
280 pub fn break_offset_if_not_bol(s: @ps, n: uint, off: int) {
281     if !is_bol(s) {
282         break_offset(s.s, n, off);
283     } else {
284         if off != 0 && s.s.last_token().is_hardbreak_tok() {
285             // We do something pretty sketchy here: tuck the nonzero
286             // offset-adjustment we were going to deposit along with the
287             // break into the previous hardbreak.
288             s.s.replace_last_token(pp::hardbreak_tok_offset(off));
289         }
290     }
291 }
292
293 // Synthesizes a comment that was not textually present in the original source
294 // file.
295 pub fn synth_comment(s: @ps, text: ~str) {
296     word(s.s, ~"/*");
297     space(s.s);
298     word(s.s, text);
299     space(s.s);
300     word(s.s, ~"*/");
301 }
302
303 pub fn commasep<IN>(s: @ps, b: breaks, elts: ~[IN], op: fn(@ps, IN)) {
304     box(s, 0u, b);
305     let mut first = true;
306     for elts.each |elt| {
307         if first { first = false; } else { word_space(s, ~","); }
308         op(s, *elt);
309     }
310     end(s);
311 }
312
313
314 pub fn commasep_cmnt<IN>(s: @ps, b: breaks, elts: ~[IN], op: fn(@ps, IN),
315                          get_span: fn(IN) -> codemap::span) {
316     box(s, 0u, b);
317     let len = vec::len::<IN>(elts);
318     let mut i = 0u;
319     for elts.each |elt| {
320         maybe_print_comment(s, get_span(*elt).hi);
321         op(s, *elt);
322         i += 1u;
323         if i < len {
324             word(s.s, ~",");
325             maybe_print_trailing_comment(s, get_span(*elt),
326                                          Some(get_span(elts[i]).hi));
327             space_if_not_bol(s);
328         }
329     }
330     end(s);
331 }
332
333 pub fn commasep_exprs(s: @ps, b: breaks, exprs: ~[@ast::expr]) {
334     fn expr_span(&&expr: @ast::expr) -> codemap::span { return expr.span; }
335     commasep_cmnt(s, b, exprs, print_expr, expr_span);
336 }
337
338 pub fn print_mod(s: @ps, _mod: ast::_mod, attrs: ~[ast::attribute]) {
339     print_inner_attributes(s, attrs);
340     for _mod.view_items.each |vitem| {
341         print_view_item(s, *vitem);
342     }
343     for _mod.items.each |item| { print_item(s, *item); }
344 }
345
346 pub fn print_foreign_mod(s: @ps, nmod: ast::foreign_mod,
347                          attrs: ~[ast::attribute]) {
348     print_inner_attributes(s, attrs);
349     for nmod.view_items.each |vitem| {
350         print_view_item(s, *vitem);
351     }
352     for nmod.items.each |item| { print_foreign_item(s, *item); }
353 }
354
355 pub fn print_region(s: @ps, prefix: ~str, region: @ast::region, sep: ~str) {
356     word(s.s, prefix);
357     match region.node {
358         ast::re_anon => {
359             return;
360         }
361         ast::re_static => {
362             word_space(s, ~"static")
363         }
364         ast::re_self => {
365             word_space(s, ~"self")
366         }
367         ast::re_named(name) => {
368             print_ident(s, name);
369         }
370     }
371     word(s.s, sep);
372 }
373
374 pub fn print_type(s: @ps, &&ty: @ast::Ty) {
375     print_type_ex(s, ty, false);
376 }
377
378 pub fn print_type_ex(s: @ps, &&ty: @ast::Ty, print_colons: bool) {
379     maybe_print_comment(s, ty.span.lo);
380     ibox(s, 0u);
381     match ty.node {
382       ast::ty_nil => word(s.s, ~"()"),
383       ast::ty_bot => word(s.s, ~"!"),
384       ast::ty_box(mt) => { word(s.s, ~"@"); print_mt(s, mt); }
385       ast::ty_uniq(mt) => { word(s.s, ~"~"); print_mt(s, mt); }
386       ast::ty_vec(mt) => {
387         word(s.s, ~"[");
388         match mt.mutbl {
389           ast::m_mutbl => word_space(s, ~"mut"),
390           ast::m_const => word_space(s, ~"const"),
391           ast::m_imm => ()
392         }
393         print_type(s, mt.ty);
394         word(s.s, ~"]");
395       }
396       ast::ty_ptr(mt) => { word(s.s, ~"*"); print_mt(s, mt); }
397       ast::ty_rptr(region, mt) => {
398           print_region(s, ~"&", region, ~"/");
399           print_mt(s, mt);
400       }
401       ast::ty_rec(ref fields) => {
402         word(s.s, ~"{");
403         fn print_field(s: @ps, f: ast::ty_field) {
404             cbox(s, indent_unit);
405             print_mutability(s, f.node.mt.mutbl);
406             print_ident(s, f.node.ident);
407             word_space(s, ~":");
408             print_type(s, f.node.mt.ty);
409             end(s);
410         }
411         fn get_span(f: ast::ty_field) -> codemap::span { return f.span; }
412         commasep_cmnt(s, consistent, (*fields), print_field, get_span);
413         word(s.s, ~",}");
414       }
415       ast::ty_tup(elts) => {
416         popen(s);
417         commasep(s, inconsistent, elts, print_type);
418         if elts.len() == 1 {
419             word(s.s, ~",");
420         }
421         pclose(s);
422       }
423       ast::ty_bare_fn(f) => {
424           print_ty_fn(s, Some(f.abi), None, None,
425                       f.purity, ast::Many, f.decl, None,
426                       None, None);
427       }
428       ast::ty_closure(f) => {
429           print_ty_fn(s, None, Some(f.sigil), f.region,
430                       f.purity, f.onceness, f.decl, None,
431                       None, None);
432       }
433       ast::ty_path(path, _) => print_path(s, path, print_colons),
434       ast::ty_fixed_length_vec(mt, v) => {
435         word(s.s, ~"[");
436         match mt.mutbl {
437             ast::m_mutbl => word_space(s, ~"mut"),
438             ast::m_const => word_space(s, ~"const"),
439             ast::m_imm => ()
440         }
441         print_type(s, mt.ty);
442         word(s.s, ~" * ");
443         word(s.s, fmt!("%u", v));
444         word(s.s, ~"]");
445       }
446       ast::ty_mac(_) => {
447           fail!(~"print_type doesn't know how to print a ty_mac");
448       }
449       ast::ty_infer => {
450           fail!(~"print_type shouldn't see a ty_infer");
451       }
452
453     }
454     end(s);
455 }
456
457 pub fn print_foreign_item(s: @ps, item: @ast::foreign_item) {
458     hardbreak_if_not_bol(s);
459     maybe_print_comment(s, item.span.lo);
460     print_outer_attributes(s, item.attrs);
461     match item.node {
462       ast::foreign_item_fn(decl, purity, typarams) => {
463         print_fn(s, decl, Some(purity), item.ident, typarams, None,
464                  ast::inherited);
465         end(s); // end head-ibox
466         word(s.s, ~";");
467         end(s); // end the outer fn box
468       }
469       ast::foreign_item_const(t) => {
470         head(s, ~"const");
471         print_ident(s, item.ident);
472         word_space(s, ~":");
473         print_type(s, t);
474         word(s.s, ~";");
475         end(s); // end the head-ibox
476         end(s); // end the outer cbox
477       }
478     }
479 }
480
481 pub fn print_item(s: @ps, &&item: @ast::item) {
482     hardbreak_if_not_bol(s);
483     maybe_print_comment(s, item.span.lo);
484     print_outer_attributes(s, item.attrs);
485     let ann_node = node_item(s, item);
486     (s.ann.pre)(ann_node);
487     match item.node {
488       ast::item_const(ty, expr) => {
489         head(s, visibility_qualified(item.vis, ~"const"));
490         print_ident(s, item.ident);
491         word_space(s, ~":");
492         print_type(s, ty);
493         space(s.s);
494         end(s); // end the head-ibox
495
496         word_space(s, ~"=");
497         print_expr(s, expr);
498         word(s.s, ~";");
499         end(s); // end the outer cbox
500
501       }
502       ast::item_fn(ref decl, purity, ref typarams, ref body) => {
503         print_fn(
504             s,
505             /* FIXME (#2543) */ copy *decl,
506             Some(purity),
507             item.ident,
508             /* FIXME (#2543) */ copy *typarams,
509             None,
510             item.vis
511         );
512         word(s.s, ~" ");
513         print_block_with_attrs(s, (*body), item.attrs);
514       }
515       ast::item_mod(_mod) => {
516         head(s, visibility_qualified(item.vis, ~"mod"));
517         print_ident(s, item.ident);
518         nbsp(s);
519         bopen(s);
520         print_mod(s, _mod, item.attrs);
521         bclose(s, item.span);
522       }
523       ast::item_foreign_mod(nmod) => {
524         head(s, visibility_qualified(item.vis, ~"extern"));
525         print_string(s, *s.intr.get(nmod.abi));
526         nbsp(s);
527         match nmod.sort {
528             ast::named => {
529                 word_nbsp(s, ~"mod");
530                 print_ident(s, item.ident);
531                 nbsp(s);
532             }
533             ast::anonymous => {}
534         }
535         bopen(s);
536         print_foreign_mod(s, nmod, item.attrs);
537         bclose(s, item.span);
538       }
539       ast::item_ty(ty, params) => {
540         ibox(s, indent_unit);
541         ibox(s, 0u);
542         word_nbsp(s, visibility_qualified(item.vis, ~"type"));
543         print_ident(s, item.ident);
544         print_type_params(s, params);
545         end(s); // end the inner ibox
546
547         space(s.s);
548         word_space(s, ~"=");
549         print_type(s, ty);
550         word(s.s, ~";");
551         end(s); // end the outer ibox
552       }
553       ast::item_enum(ref enum_definition, ref params) => {
554         print_enum_def(
555             s,
556             *enum_definition,
557             /* FIXME (#2543) */ copy *params,
558             item.ident,
559             item.span,
560             item.vis
561         );
562       }
563       ast::item_struct(struct_def, tps) => {
564           head(s, visibility_qualified(item.vis, ~"struct"));
565           print_struct(s, struct_def, tps, item.ident, item.span);
566       }
567
568       ast::item_impl(tps, opt_trait, ty, methods) => {
569         head(s, visibility_qualified(item.vis, ~"impl"));
570         if !tps.is_empty() {
571             print_type_params(s, tps);
572             space(s.s);
573         }
574
575         match opt_trait {
576             Some(t) => {
577                 print_path(s, t.path, false);
578                 space(s.s);
579                 word_space(s, ~"for");
580             }
581             None => ()
582         };
583
584         print_type(s, ty);
585         space(s.s);
586
587         if methods.len() == 0 {
588             word(s.s, ~";");
589         } else {
590             bopen(s);
591             for methods.each |meth| {
592                print_method(s, *meth);
593             }
594             bclose(s, item.span);
595         }
596       }
597       ast::item_trait(ref tps, ref traits, ref methods) => {
598         head(s, visibility_qualified(item.vis, ~"trait"));
599         print_ident(s, item.ident);
600         print_type_params(s, /* FIXME (#2543) */ copy *tps);
601         if traits.len() != 0u {
602             word(s.s, ~":");
603             for traits.each |trait_| {
604                 nbsp(s);
605                 print_path(s, trait_.path, false);
606             }
607         }
608         word(s.s, ~" ");
609         bopen(s);
610         for (*methods).each |meth| {
611             print_trait_method(s, *meth);
612         }
613         bclose(s, item.span);
614       }
615       ast::item_mac(codemap::spanned { node: ast::mac_invoc_tt(pth, ref tts),
616                                    _}) => {
617         print_visibility(s, item.vis);
618         print_path(s, pth, false);
619         word(s.s, ~"! ");
620         print_ident(s, item.ident);
621         cbox(s, indent_unit);
622         popen(s);
623         print_tts(s, *tts);
624         pclose(s);
625         end(s);
626       }
627     }
628     (s.ann.post)(ann_node);
629 }
630
631 pub fn print_enum_def(s: @ps, enum_definition: ast::enum_def,
632                       params: ~[ast::ty_param], ident: ast::ident,
633                       span: codemap::span, visibility: ast::visibility) {
634     let mut newtype =
635         vec::len(enum_definition.variants) == 1u &&
636         ident == enum_definition.variants[0].node.name;
637     if newtype {
638         match enum_definition.variants[0].node.kind {
639             ast::tuple_variant_kind(ref args) if args.len() == 1 => {}
640             _ => newtype = false
641         }
642     }
643     if newtype {
644         ibox(s, indent_unit);
645         word_space(s, visibility_qualified(visibility, ~"enum"));
646     } else {
647         head(s, visibility_qualified(visibility, ~"enum"));
648     }
649
650     print_ident(s, ident);
651     print_type_params(s, params);
652     space(s.s);
653     if newtype {
654         word_space(s, ~"=");
655         match enum_definition.variants[0].node.kind {
656             ast::tuple_variant_kind(args) => print_type(s, args[0].ty),
657             _ => fail!(~"newtype syntax with struct?")
658         }
659         word(s.s, ~";");
660         end(s);
661     } else {
662         print_variants(s, enum_definition.variants, span);
663     }
664 }
665
666 pub fn print_variants(s: @ps,
667                       variants: ~[ast::variant],
668                       span: codemap::span) {
669     bopen(s);
670     for variants.each |v| {
671         space_if_not_bol(s);
672         maybe_print_comment(s, v.span.lo);
673         print_outer_attributes(s, v.node.attrs);
674         ibox(s, indent_unit);
675         print_variant(s, *v);
676         word(s.s, ~",");
677         end(s);
678         maybe_print_trailing_comment(s, v.span, None);
679     }
680     bclose(s, span);
681 }
682
683 pub fn visibility_to_str(vis: ast::visibility) -> ~str {
684     match vis {
685         ast::private => ~"priv",
686         ast::public => ~"pub",
687         ast::inherited => ~""
688     }
689 }
690
691 pub fn visibility_qualified(vis: ast::visibility, s: ~str) -> ~str {
692     match vis {
693         ast::private | ast::public =>
694         visibility_to_str(vis) + " " + s,
695         ast::inherited => s
696     }
697 }
698
699 pub fn print_visibility(s: @ps, vis: ast::visibility) {
700     match vis {
701         ast::private | ast::public =>
702         word_nbsp(s, visibility_to_str(vis)),
703         ast::inherited => ()
704     }
705 }
706
707 pub fn print_struct(s: @ps,
708                     struct_def: @ast::struct_def,
709                     tps: ~[ast::ty_param],
710                     ident: ast::ident,
711                     span: codemap::span) {
712     print_ident(s, ident);
713     nbsp(s);
714     print_type_params(s, tps);
715     if ast_util::struct_def_is_tuple_like(struct_def) {
716         popen(s);
717         let mut first = true;
718         for struct_def.fields.each |field| {
719             if first {
720                 first = false;
721             } else {
722                 word_space(s, ~",");
723             }
724
725             match field.node.kind {
726                 ast::named_field(*) => fail!(~"unexpected named field"),
727                 ast::unnamed_field => {
728                     maybe_print_comment(s, field.span.lo);
729                     print_type(s, field.node.ty);
730                 }
731             }
732         }
733         pclose(s);
734         word(s.s, ~";");
735         end(s); // close the outer-box
736     } else {
737         bopen(s);
738         hardbreak_if_not_bol(s);
739         do struct_def.dtor.iter |dtor| {
740           hardbreak_if_not_bol(s);
741           maybe_print_comment(s, dtor.span.lo);
742           print_outer_attributes(s, dtor.node.attrs);
743           head(s, ~"drop");
744           print_block(s, dtor.node.body);
745         }
746
747         for struct_def.fields.each |field| {
748             match field.node.kind {
749                 ast::unnamed_field => fail!(~"unexpected unnamed field"),
750                 ast::named_field(ident, mutability, visibility) => {
751                     hardbreak_if_not_bol(s);
752                     maybe_print_comment(s, field.span.lo);
753                     print_visibility(s, visibility);
754                     if mutability == ast::struct_mutable {
755                         word_nbsp(s, ~"mut");
756                     }
757                     print_ident(s, ident);
758                     word_nbsp(s, ~":");
759                     print_type(s, field.node.ty);
760                     word(s.s, ~",");
761                 }
762             }
763         }
764
765         bclose(s, span);
766     }
767 }
768
769 /// This doesn't deserve to be called "pretty" printing, but it should be
770 /// meaning-preserving. A quick hack that might help would be to look at the
771 /// spans embedded in the TTs to decide where to put spaces and newlines.
772 /// But it'd be better to parse these according to the grammar of the
773 /// appropriate macro, transcribe back into the grammar we just parsed from,
774 /// and then pretty-print the resulting AST nodes (so, e.g., we print
775 /// expression arguments as expressions). It can be done! I think.
776 pub fn print_tt(s: @ps, tt: ast::token_tree) {
777     match tt {
778       ast::tt_delim(ref tts) => print_tts(s, *tts),
779       ast::tt_tok(_, ref tk) => {
780           word(s.s, parse::token::to_str(s.intr, (*tk)));
781       }
782       ast::tt_seq(_, ref tts, ref sep, zerok) => {
783         word(s.s, ~"$(");
784         for (*tts).each() |tt_elt| { print_tt(s, *tt_elt); }
785         word(s.s, ~")");
786         match (*sep) {
787           Some(ref tk) => word(s.s, parse::token::to_str(s.intr, (*tk))),
788           None => ()
789         }
790         word(s.s, if zerok { ~"*" } else { ~"+" });
791       }
792       ast::tt_nonterminal(_, name) => {
793         word(s.s, ~"$");
794         print_ident(s, name);
795       }
796     }
797 }
798
799 pub fn print_tts(s: @ps, &&tts: &[ast::token_tree]) {
800     ibox(s, 0);
801     for tts.eachi |i, tt| {
802         if i != 0 {
803             space(s.s);
804         }
805         print_tt(s, *tt);
806     }
807     end(s);
808 }
809
810 pub fn print_variant(s: @ps, v: ast::variant) {
811     print_visibility(s, v.node.vis);
812     match v.node.kind {
813         ast::tuple_variant_kind(args) => {
814             print_ident(s, v.node.name);
815             if !args.is_empty() {
816                 popen(s);
817                 fn print_variant_arg(s: @ps, arg: ast::variant_arg) {
818                     print_type(s, arg.ty);
819                 }
820                 commasep(s, consistent, args, print_variant_arg);
821                 pclose(s);
822             }
823         }
824         ast::struct_variant_kind(struct_def) => {
825             head(s, ~"");
826             print_struct(s, struct_def, ~[], v.node.name, v.span);
827         }
828         ast::enum_variant_kind(ref enum_definition) => {
829             print_variants(s, (*enum_definition).variants, v.span);
830         }
831     }
832     match v.node.disr_expr {
833       Some(d) => {
834         space(s.s);
835         word_space(s, ~"=");
836         print_expr(s, d);
837       }
838       _ => ()
839     }
840 }
841
842 pub fn print_ty_method(s: @ps, m: ast::ty_method) {
843     hardbreak_if_not_bol(s);
844     maybe_print_comment(s, m.span.lo);
845     print_outer_attributes(s, m.attrs);
846     print_ty_fn(s, None, None, None, m.purity, ast::Many,
847                 m.decl, Some(m.ident), Some(m.tps),
848                 Some(m.self_ty.node));
849     word(s.s, ~";");
850 }
851
852 pub fn print_trait_method(s: @ps, m: ast::trait_method) {
853     match m {
854       required(ref ty_m) => print_ty_method(s, (*ty_m)),
855       provided(m)    => print_method(s, m)
856     }
857 }
858
859 pub fn print_method(s: @ps, meth: @ast::method) {
860     hardbreak_if_not_bol(s);
861     maybe_print_comment(s, meth.span.lo);
862     print_outer_attributes(s, meth.attrs);
863     print_fn(s, meth.decl, Some(meth.purity),
864              meth.ident, meth.tps, Some(meth.self_ty.node),
865              meth.vis);
866     word(s.s, ~" ");
867     print_block_with_attrs(s, meth.body, meth.attrs);
868 }
869
870 pub fn print_outer_attributes(s: @ps, attrs: ~[ast::attribute]) {
871     let mut count = 0;
872     for attrs.each |attr| {
873         match attr.node.style {
874           ast::attr_outer => { print_attribute(s, *attr); count += 1; }
875           _ => {/* fallthrough */ }
876         }
877     }
878     if count > 0 { hardbreak_if_not_bol(s); }
879 }
880
881 pub fn print_inner_attributes(s: @ps, attrs: ~[ast::attribute]) {
882     let mut count = 0;
883     for attrs.each |attr| {
884         match attr.node.style {
885           ast::attr_inner => {
886             print_attribute(s, *attr);
887             if !attr.node.is_sugared_doc {
888                 word(s.s, ~";");
889             }
890             count += 1;
891           }
892           _ => {/* fallthrough */ }
893         }
894     }
895     if count > 0 { hardbreak_if_not_bol(s); }
896 }
897
898 pub fn print_attribute(s: @ps, attr: ast::attribute) {
899     hardbreak_if_not_bol(s);
900     maybe_print_comment(s, attr.span.lo);
901     if attr.node.is_sugared_doc {
902         let meta = attr::attr_meta(attr);
903         let comment = attr::get_meta_item_value_str(meta).get();
904         word(s.s, *comment);
905     } else {
906         word(s.s, ~"#[");
907         print_meta_item(s, @attr.node.value);
908         word(s.s, ~"]");
909     }
910 }
911
912
913 pub fn print_stmt(s: @ps, st: ast::stmt) {
914     maybe_print_comment(s, st.span.lo);
915     match st.node {
916       ast::stmt_decl(decl, _) => {
917         print_decl(s, decl);
918       }
919       ast::stmt_expr(expr, _) => {
920         space_if_not_bol(s);
921         print_expr(s, expr);
922       }
923       ast::stmt_semi(expr, _) => {
924         space_if_not_bol(s);
925         print_expr(s, expr);
926         word(s.s, ~";");
927       }
928       ast::stmt_mac(ref mac, semi) => {
929         space_if_not_bol(s);
930         print_mac(s, (*mac));
931         if semi { word(s.s, ~";"); }
932       }
933     }
934     if parse::classify::stmt_ends_with_semi(st) { word(s.s, ~";"); }
935     maybe_print_trailing_comment(s, st.span, None);
936 }
937
938 pub fn print_block(s: @ps, blk: ast::blk) {
939     print_possibly_embedded_block(s, blk, block_normal, indent_unit);
940 }
941
942 pub fn print_block_unclosed(s: @ps, blk: ast::blk) {
943     print_possibly_embedded_block_(s, blk, block_normal, indent_unit, ~[],
944                                  false);
945 }
946
947 pub fn print_block_unclosed_indent(s: @ps, blk: ast::blk, indented: uint) {
948     print_possibly_embedded_block_(s, blk, block_normal, indented, ~[],
949                                    false);
950 }
951
952 pub fn print_block_with_attrs(s: @ps,
953                               blk: ast::blk,
954                               attrs: ~[ast::attribute]) {
955     print_possibly_embedded_block_(s, blk, block_normal, indent_unit, attrs,
956                                   true);
957 }
958
959 pub enum embed_type { block_block_fn, block_normal, }
960
961 pub fn print_possibly_embedded_block(s: @ps,
962                                      blk: ast::blk,
963                                      embedded: embed_type,
964                                      indented: uint) {
965     print_possibly_embedded_block_(
966         s, blk, embedded, indented, ~[], true);
967 }
968
969 pub fn print_possibly_embedded_block_(s: @ps,
970                                       blk: ast::blk,
971                                       embedded: embed_type,
972                                       indented: uint,
973                                       attrs: ~[ast::attribute],
974                                       close_box: bool) {
975     match blk.node.rules {
976       ast::unsafe_blk => word_space(s, ~"unsafe"),
977       ast::default_blk => ()
978     }
979     maybe_print_comment(s, blk.span.lo);
980     let ann_node = node_block(s, blk);
981     (s.ann.pre)(ann_node);
982     match embedded {
983       block_block_fn => end(s),
984       block_normal => bopen(s)
985     }
986
987     print_inner_attributes(s, attrs);
988
989     for blk.node.view_items.each |vi| { print_view_item(s, *vi); }
990     for blk.node.stmts.each |st| {
991         print_stmt(s, **st);
992     }
993     match blk.node.expr {
994       Some(expr) => {
995         space_if_not_bol(s);
996         print_expr(s, expr);
997         maybe_print_trailing_comment(s, expr.span, Some(blk.span.hi));
998       }
999       _ => ()
1000     }
1001     bclose_maybe_open(s, blk.span, indented, close_box);
1002     (s.ann.post)(ann_node);
1003 }
1004
1005 pub fn print_if(s: @ps, test: @ast::expr, blk: ast::blk,
1006                 elseopt: Option<@ast::expr>, chk: bool) {
1007     head(s, ~"if");
1008     if chk { word_nbsp(s, ~"check"); }
1009     print_expr(s, test);
1010     space(s.s);
1011     print_block(s, blk);
1012     fn do_else(s: @ps, els: Option<@ast::expr>) {
1013         match els {
1014           Some(_else) => {
1015             match _else.node {
1016               // "another else-if"
1017               ast::expr_if(i, ref t, e) => {
1018                 cbox(s, indent_unit - 1u);
1019                 ibox(s, 0u);
1020                 word(s.s, ~" else if ");
1021                 print_expr(s, i);
1022                 space(s.s);
1023                 print_block(s, (*t));
1024                 do_else(s, e);
1025               }
1026               // "final else"
1027               ast::expr_block(ref b) => {
1028                 cbox(s, indent_unit - 1u);
1029                 ibox(s, 0u);
1030                 word(s.s, ~" else ");
1031                 print_block(s, (*b));
1032               }
1033               // BLEAH, constraints would be great here
1034               _ => {
1035                   fail!(~"print_if saw if with weird alternative");
1036               }
1037             }
1038           }
1039           _ => {/* fall through */ }
1040         }
1041     }
1042     do_else(s, elseopt);
1043 }
1044
1045 pub fn print_mac(s: @ps, m: ast::mac) {
1046     match m.node {
1047       ast::mac_invoc_tt(pth, ref tts) => {
1048         print_path(s, pth, false);
1049         word(s.s, ~"!");
1050         popen(s);
1051         print_tts(s, *tts);
1052         pclose(s);
1053       }
1054     }
1055 }
1056
1057 pub fn print_vstore(s: @ps, t: ast::vstore) {
1058     match t {
1059         ast::vstore_fixed(Some(i)) => word(s.s, fmt!("%u", i)),
1060         ast::vstore_fixed(None) => word(s.s, ~"_"),
1061         ast::vstore_uniq => word(s.s, ~"~"),
1062         ast::vstore_box => word(s.s, ~"@"),
1063         ast::vstore_slice(r) => print_region(s, ~"&", r, ~"/")
1064     }
1065 }
1066
1067 pub fn print_expr_vstore(s: @ps, t: ast::expr_vstore) {
1068     match t {
1069       ast::expr_vstore_fixed(Some(i)) => word(s.s, fmt!("%u", i)),
1070       ast::expr_vstore_fixed(None) => word(s.s, ~"_"),
1071       ast::expr_vstore_uniq => word(s.s, ~"~"),
1072       ast::expr_vstore_box => word(s.s, ~"@"),
1073       ast::expr_vstore_mut_box => {
1074         word(s.s, ~"@");
1075         word(s.s, ~"mut");
1076       }
1077       ast::expr_vstore_slice => word(s.s, ~"&"),
1078       ast::expr_vstore_mut_slice => {
1079         word(s.s, ~"&");
1080         word(s.s, ~"mut");
1081       }
1082     }
1083 }
1084
1085 pub fn print_call_pre(s: @ps,
1086                       sugar: ast::CallSugar,
1087                       base_args: &mut ~[@ast::expr])
1088                    -> Option<@ast::expr> {
1089     match sugar {
1090         ast::DoSugar => {
1091             head(s, ~"do");
1092             Some(base_args.pop())
1093         }
1094         ast::ForSugar => {
1095             head(s, ~"for");
1096             Some(base_args.pop())
1097         }
1098         ast::NoSugar => None
1099     }
1100 }
1101
1102 pub fn print_call_post(s: @ps,
1103                        sugar: ast::CallSugar,
1104                        blk: &Option<@ast::expr>,
1105                        base_args: &mut ~[@ast::expr]) {
1106     if sugar == ast::NoSugar || !base_args.is_empty() {
1107         popen(s);
1108         commasep_exprs(s, inconsistent, *base_args);
1109         pclose(s);
1110     }
1111     if sugar != ast::NoSugar {
1112         nbsp(s);
1113         match blk.get().node {
1114           // need to handle closures specifically
1115           ast::expr_do_body(e) | ast::expr_loop_body(e) => {
1116             end(s); // we close our head box; closure
1117                     // will create it's own.
1118             print_expr(s, e);
1119             end(s); // close outer box, as closures don't
1120           }
1121           _ => {
1122             // not sure if this can happen.
1123             print_expr(s, blk.get());
1124           }
1125         }
1126     }
1127 }
1128
1129 pub fn print_expr(s: @ps, &&expr: @ast::expr) {
1130     fn print_field(s: @ps, field: ast::field) {
1131         ibox(s, indent_unit);
1132         if field.node.mutbl == ast::m_mutbl { word_nbsp(s, ~"mut"); }
1133         print_ident(s, field.node.ident);
1134         word_space(s, ~":");
1135         print_expr(s, field.node.expr);
1136         end(s);
1137     }
1138     fn get_span(field: ast::field) -> codemap::span { return field.span; }
1139
1140     maybe_print_comment(s, expr.span.lo);
1141     ibox(s, indent_unit);
1142     let ann_node = node_expr(s, expr);
1143     (s.ann.pre)(ann_node);
1144     match expr.node {
1145         ast::expr_vstore(e, v) => match v {
1146             ast::expr_vstore_fixed(_) => {
1147                 print_expr(s, e);
1148                 word(s.s, ~"/");
1149                 print_expr_vstore(s, v);
1150             }
1151             _ => {
1152                 print_expr_vstore(s, v);
1153                 print_expr(s, e);
1154             }
1155         },
1156       ast::expr_vec(exprs, mutbl) => {
1157         ibox(s, indent_unit);
1158         word(s.s, ~"[");
1159         if mutbl == ast::m_mutbl {
1160             word(s.s, ~"mut");
1161             if vec::len(exprs) > 0u { nbsp(s); }
1162         }
1163         commasep_exprs(s, inconsistent, exprs);
1164         word(s.s, ~"]");
1165         end(s);
1166       }
1167
1168       ast::expr_repeat(element, count, mutbl) => {
1169         ibox(s, indent_unit);
1170         word(s.s, ~"[");
1171         if mutbl == ast::m_mutbl {
1172             word(s.s, ~"mut");
1173             nbsp(s);
1174         }
1175         print_expr(s, element);
1176         word(s.s, ~",");
1177         word(s.s, ~"..");
1178         print_expr(s, count);
1179         word(s.s, ~"]");
1180         end(s);
1181       }
1182
1183       ast::expr_rec(ref fields, wth) => {
1184         word(s.s, ~"{");
1185         commasep_cmnt(s, consistent, (*fields), print_field, get_span);
1186         match wth {
1187           Some(expr) => {
1188             ibox(s, indent_unit);
1189             word(s.s, ~",");
1190             space(s.s);
1191             word(s.s, ~"..");
1192             print_expr(s, expr);
1193             end(s);
1194           }
1195           _ => word(s.s, ~",")
1196         }
1197         word(s.s, ~"}");
1198       }
1199       ast::expr_struct(path, ref fields, wth) => {
1200         print_path(s, path, true);
1201         word(s.s, ~"{");
1202         commasep_cmnt(s, consistent, (*fields), print_field, get_span);
1203         match wth {
1204             Some(expr) => {
1205                 ibox(s, indent_unit);
1206                 word(s.s, ~",");
1207                 space(s.s);
1208                 word(s.s, ~"..");
1209                 print_expr(s, expr);
1210                 end(s);
1211             }
1212             _ => word(s.s, ~",")
1213         }
1214         word(s.s, ~"}");
1215       }
1216       ast::expr_tup(exprs) => {
1217         popen(s);
1218         commasep_exprs(s, inconsistent, exprs);
1219         if exprs.len() == 1 {
1220             word(s.s, ~",");
1221         }
1222         pclose(s);
1223       }
1224       ast::expr_call(func, args, sugar) => {
1225         let mut base_args = copy args;
1226         let blk = print_call_pre(s, sugar, &mut base_args);
1227         print_expr(s, func);
1228         print_call_post(s, sugar, &blk, &mut base_args);
1229       }
1230       ast::expr_method_call(func, ident, tys, args, sugar) => {
1231         let mut base_args = copy args;
1232         let blk = print_call_pre(s, sugar, &mut base_args);
1233         print_expr(s, func);
1234         word(s.s, ~".");
1235         print_ident(s, ident);
1236         if vec::len(tys) > 0u {
1237             word(s.s, ~"::<");
1238             commasep(s, inconsistent, tys, print_type);
1239             word(s.s, ~">");
1240         }
1241         print_call_post(s, sugar, &blk, &mut base_args);
1242       }
1243       ast::expr_binary(op, lhs, rhs) => {
1244         print_expr(s, lhs);
1245         space(s.s);
1246         word_space(s, ast_util::binop_to_str(op));
1247         print_expr(s, rhs);
1248       }
1249       ast::expr_unary(op, expr) => {
1250         word(s.s, ast_util::unop_to_str(op));
1251         print_expr(s, expr);
1252       }
1253       ast::expr_addr_of(m, expr) => {
1254         word(s.s, ~"&");
1255         print_mutability(s, m);
1256         print_expr(s, expr);
1257       }
1258       ast::expr_lit(lit) => print_literal(s, lit),
1259       ast::expr_cast(expr, ty) => {
1260         print_expr(s, expr);
1261         space(s.s);
1262         word_space(s, ~"as");
1263         print_type_ex(s, ty, true);
1264       }
1265       ast::expr_if(test, ref blk, elseopt) => {
1266         print_if(s, test, (*blk), elseopt, false);
1267       }
1268       ast::expr_while(test, ref blk) => {
1269         head(s, ~"while");
1270         print_expr(s, test);
1271         space(s.s);
1272         print_block(s, (*blk));
1273       }
1274       ast::expr_loop(ref blk, opt_ident) => {
1275         head(s, ~"loop");
1276         space(s.s);
1277         opt_ident.iter(|ident| {
1278             print_ident(s, *ident);
1279             word_space(s, ~":");
1280         });
1281         print_block(s, (*blk));
1282       }
1283       ast::expr_match(expr, ref arms) => {
1284         cbox(s, match_indent_unit);
1285         ibox(s, 4);
1286         word_nbsp(s, ~"match");
1287         print_expr(s, expr);
1288         space(s.s);
1289         bopen(s);
1290         let len = (*arms).len();
1291         for (*arms).eachi |i, arm| {
1292             space(s.s);
1293             cbox(s, match_indent_unit);
1294             ibox(s, 0u);
1295             let mut first = true;
1296             for arm.pats.each |p| {
1297                 if first {
1298                     first = false;
1299                 } else { space(s.s); word_space(s, ~"|"); }
1300                 print_refutable_pat(s, *p);
1301             }
1302             space(s.s);
1303             match arm.guard {
1304               Some(e) => {
1305                 word_space(s, ~"if");
1306                 print_expr(s, e);
1307                 space(s.s);
1308               }
1309               None => ()
1310             }
1311             word_space(s, ~"=>");
1312
1313             // Extract the expression from the extra block the parser adds
1314             // in the case of foo => expr
1315             if arm.body.node.view_items.is_empty() &&
1316                 arm.body.node.stmts.is_empty() &&
1317                 arm.body.node.rules == ast::default_blk &&
1318                 arm.body.node.expr.is_some()
1319             {
1320                 match arm.body.node.expr {
1321                     Some(expr) => {
1322                         match expr.node {
1323                             ast::expr_block(ref blk) => {
1324                                 // the block will close the pattern's ibox
1325                                 print_block_unclosed_indent(
1326                                     s, (*blk), match_indent_unit);
1327                             }
1328                             _ => {
1329                                 end(s); // close the ibox for the pattern
1330                                 print_expr(s, expr);
1331                             }
1332                         }
1333                         if !expr_is_simple_block(expr)
1334                             && i < len - 1 {
1335                             word(s.s, ~",");
1336                         }
1337                         end(s); // close enclosing cbox
1338                     }
1339                     None => fail!()
1340                 }
1341             } else {
1342                 // the block will close the pattern's ibox
1343                 print_block_unclosed_indent(s, arm.body, match_indent_unit);
1344             }
1345         }
1346         bclose_(s, expr.span, match_indent_unit);
1347       }
1348       ast::expr_fn(sigil, ref decl, ref body, _) => {
1349         // containing cbox, will be closed by print-block at }
1350         cbox(s, indent_unit);
1351         // head-box, will be closed by print-block at start
1352         ibox(s, 0u);
1353         print_fn_header_info(s, None, None, ast::Many,
1354                              Some(sigil), ast::inherited);
1355         print_fn_args_and_ret(s, /* FIXME (#2543) */ copy *decl, None);
1356         space(s.s);
1357         print_block(s, (*body));
1358       }
1359       ast::expr_fn_block(ref decl, ref body) => {
1360         // in do/for blocks we don't want to show an empty
1361         // argument list, but at this point we don't know which
1362         // we are inside.
1363         //
1364         // if !decl.inputs.is_empty() {
1365         print_fn_block_args(s, /* FIXME (#2543) */ copy *decl);
1366         space(s.s);
1367         // }
1368         assert (*body).node.stmts.is_empty();
1369         assert (*body).node.expr.is_some();
1370         // we extract the block, so as not to create another set of boxes
1371         match (*body).node.expr.get().node {
1372             ast::expr_block(ref blk) => {
1373                 print_block_unclosed(s, (*blk));
1374             }
1375             _ => {
1376                 // this is a bare expression
1377                 print_expr(s, (*body).node.expr.get());
1378                 end(s); // need to close a box
1379             }
1380         }
1381         // a box will be closed by print_expr, but we didn't want an overall
1382         // wrapper so we closed the corresponding opening. so create an
1383         // empty box to satisfy the close.
1384         ibox(s, 0);
1385       }
1386       ast::expr_loop_body(body) => {
1387         print_expr(s, body);
1388       }
1389       ast::expr_do_body(body) => {
1390         print_expr(s, body);
1391       }
1392       ast::expr_block(ref blk) => {
1393         // containing cbox, will be closed by print-block at }
1394         cbox(s, indent_unit);
1395         // head-box, will be closed by print-block after {
1396         ibox(s, 0u);
1397         print_block(s, (*blk));
1398       }
1399       ast::expr_copy(e) => { word_space(s, ~"copy"); print_expr(s, e); }
1400       ast::expr_assign(lhs, rhs) => {
1401         print_expr(s, lhs);
1402         space(s.s);
1403         word_space(s, ~"=");
1404         print_expr(s, rhs);
1405       }
1406       ast::expr_swap(lhs, rhs) => {
1407         print_expr(s, lhs);
1408         space(s.s);
1409         word_space(s, ~"<->");
1410         print_expr(s, rhs);
1411       }
1412       ast::expr_assign_op(op, lhs, rhs) => {
1413         print_expr(s, lhs);
1414         space(s.s);
1415         word(s.s, ast_util::binop_to_str(op));
1416         word_space(s, ~"=");
1417         print_expr(s, rhs);
1418       }
1419       ast::expr_field(expr, id, tys) => {
1420         print_expr(s, expr);
1421         word(s.s, ~".");
1422         print_ident(s, id);
1423         if vec::len(tys) > 0u {
1424             word(s.s, ~"::<");
1425             commasep(s, inconsistent, tys, print_type);
1426             word(s.s, ~">");
1427         }
1428       }
1429       ast::expr_index(expr, index) => {
1430         print_expr(s, expr);
1431         word(s.s, ~"[");
1432         print_expr(s, index);
1433         word(s.s, ~"]");
1434       }
1435       ast::expr_path(path) => print_path(s, path, true),
1436       ast::expr_break(opt_ident) => {
1437         word(s.s, ~"break");
1438         space(s.s);
1439         opt_ident.iter(|ident| {print_ident(s, *ident); space(s.s)});
1440       }
1441       ast::expr_again(opt_ident) => {
1442         word(s.s, ~"loop");
1443         space(s.s);
1444         opt_ident.iter(|ident| {print_ident(s, *ident); space(s.s)});
1445       }
1446       ast::expr_ret(result) => {
1447         word(s.s, ~"return");
1448         match result {
1449           Some(expr) => { word(s.s, ~" "); print_expr(s, expr); }
1450           _ => ()
1451         }
1452       }
1453       ast::expr_log(lvl, lexp, expr) => {
1454         match lvl {
1455           ast::debug => { word_nbsp(s, ~"log"); print_expr(s, expr); }
1456           ast::error => { word_nbsp(s, ~"log_err"); print_expr(s, expr); }
1457           ast::log_other => {
1458             word_nbsp(s, ~"log");
1459             popen(s);
1460             print_expr(s, lexp);
1461             word(s.s, ~",");
1462             space_if_not_bol(s);
1463             print_expr(s, expr);
1464             pclose(s);
1465           }
1466         }
1467       }
1468       ast::expr_assert(expr) => {
1469         word_nbsp(s, ~"assert");
1470         print_expr(s, expr);
1471       }
1472       ast::expr_mac(ref m) => print_mac(s, (*m)),
1473       ast::expr_paren(e) => {
1474           popen(s);
1475           print_expr(s, e);
1476           pclose(s);
1477       }
1478     }
1479     (s.ann.post)(ann_node);
1480     end(s);
1481 }
1482
1483 pub fn print_local_decl(s: @ps, loc: @ast::local) {
1484     print_irrefutable_pat(s, loc.node.pat);
1485     match loc.node.ty.node {
1486       ast::ty_infer => (),
1487       _ => { word_space(s, ~":"); print_type(s, loc.node.ty); }
1488     }
1489 }
1490
1491 pub fn print_decl(s: @ps, decl: @ast::decl) {
1492     maybe_print_comment(s, decl.span.lo);
1493     match decl.node {
1494       ast::decl_local(locs) => {
1495         space_if_not_bol(s);
1496         ibox(s, indent_unit);
1497         word_nbsp(s, ~"let");
1498
1499         // if any are mut, all are mut
1500         if vec::any(locs, |l| l.node.is_mutbl) {
1501             assert vec::all(locs, |l| l.node.is_mutbl);
1502             word_nbsp(s, ~"mut");
1503         }
1504
1505         fn print_local(s: @ps, &&loc: @ast::local) {
1506             ibox(s, indent_unit);
1507             print_local_decl(s, loc);
1508             end(s);
1509             match loc.node.init {
1510               Some(init) => {
1511                 nbsp(s);
1512                 word_space(s, ~"=");
1513                 print_expr(s, init);
1514               }
1515               _ => ()
1516             }
1517         }
1518         commasep(s, consistent, locs, print_local);
1519         end(s);
1520       }
1521       ast::decl_item(item) => print_item(s, item)
1522     }
1523 }
1524
1525 pub fn print_ident(s: @ps, ident: ast::ident) {
1526     word(s.s, *s.intr.get(ident));
1527 }
1528
1529 pub fn print_for_decl(s: @ps, loc: @ast::local, coll: @ast::expr) {
1530     print_local_decl(s, loc);
1531     space(s.s);
1532     word_space(s, ~"in");
1533     print_expr(s, coll);
1534 }
1535
1536 pub fn print_path(s: @ps, &&path: @ast::path, colons_before_params: bool) {
1537     maybe_print_comment(s, path.span.lo);
1538     if path.global { word(s.s, ~"::"); }
1539     let mut first = true;
1540     for path.idents.each |id| {
1541         if first { first = false; } else { word(s.s, ~"::"); }
1542         print_ident(s, *id);
1543     }
1544     if path.rp.is_some() || !path.types.is_empty() {
1545         if colons_before_params { word(s.s, ~"::"); }
1546
1547         match path.rp {
1548           None => { /* ok */ }
1549           Some(r) => {
1550             word(s.s, ~"/");
1551             print_region(s, ~"&", r, ~"");
1552           }
1553         }
1554
1555         if !path.types.is_empty() {
1556             word(s.s, ~"<");
1557             commasep(s, inconsistent, path.types, print_type);
1558             word(s.s, ~">");
1559         }
1560     }
1561 }
1562
1563 pub fn print_irrefutable_pat(s: @ps, &&pat: @ast::pat) {
1564     print_pat(s, pat, false)
1565 }
1566
1567 pub fn print_refutable_pat(s: @ps, &&pat: @ast::pat) {
1568     print_pat(s, pat, true)
1569 }
1570
1571 pub fn print_pat(s: @ps, &&pat: @ast::pat, refutable: bool) {
1572     maybe_print_comment(s, pat.span.lo);
1573     let ann_node = node_pat(s, pat);
1574     (s.ann.pre)(ann_node);
1575     /* Pat isn't normalized, but the beauty of it
1576      is that it doesn't matter */
1577     match pat.node {
1578       ast::pat_wild => word(s.s, ~"_"),
1579       ast::pat_ident(binding_mode, path, sub) => {
1580           if refutable {
1581               match binding_mode {
1582                   ast::bind_by_ref(mutbl) => {
1583                       word_nbsp(s, ~"ref");
1584                       print_mutability(s, mutbl);
1585                   }
1586                   ast::bind_by_copy => {
1587                       word_nbsp(s, ~"copy");
1588                   }
1589                   ast::bind_infer => {}
1590               }
1591           }
1592           print_path(s, path, true);
1593           match sub {
1594               Some(p) => {
1595                   word(s.s, ~"@");
1596                   print_pat(s, p, refutable);
1597               }
1598               None => ()
1599           }
1600       }
1601       ast::pat_enum(path, args_) => {
1602         print_path(s, path, true);
1603         match args_ {
1604           None => word(s.s, ~"(*)"),
1605           Some(args) => {
1606             if !args.is_empty() {
1607               popen(s);
1608               commasep(s, inconsistent, args,
1609                        |s, p| print_pat(s, p, refutable));
1610               pclose(s);
1611             } else { }
1612           }
1613         }
1614       }
1615       ast::pat_rec(fields, etc) => {
1616         word(s.s, ~"{");
1617         fn print_field(s: @ps, f: ast::field_pat, refutable: bool) {
1618             cbox(s, indent_unit);
1619             print_ident(s, f.ident);
1620             word_space(s, ~":");
1621             print_pat(s, f.pat, refutable);
1622             end(s);
1623         }
1624         fn get_span(f: ast::field_pat) -> codemap::span { return f.pat.span; }
1625         commasep_cmnt(s, consistent, fields,
1626                       |s, f| print_field(s, f, refutable),
1627                       get_span);
1628         if etc {
1629             if vec::len(fields) != 0u { word_space(s, ~","); }
1630             word(s.s, ~"_");
1631         }
1632         word(s.s, ~"}");
1633       }
1634       ast::pat_struct(path, fields, etc) => {
1635         print_path(s, path, true);
1636         word(s.s, ~"{");
1637         fn print_field(s: @ps, f: ast::field_pat, refutable: bool) {
1638             cbox(s, indent_unit);
1639             print_ident(s, f.ident);
1640             word_space(s, ~":");
1641             print_pat(s, f.pat, refutable);
1642             end(s);
1643         }
1644         fn get_span(f: ast::field_pat) -> codemap::span { return f.pat.span; }
1645         commasep_cmnt(s, consistent, fields,
1646                       |s, f| print_field(s,f,refutable),
1647                       get_span);
1648         if etc {
1649             if vec::len(fields) != 0u { word_space(s, ~","); }
1650             word(s.s, ~"_");
1651         }
1652         word(s.s, ~"}");
1653       }
1654       ast::pat_tup(elts) => {
1655         popen(s);
1656         commasep(s, inconsistent, elts, |s, p| print_pat(s, p, refutable));
1657         if elts.len() == 1 {
1658             word(s.s, ~",");
1659         }
1660         pclose(s);
1661       }
1662       ast::pat_box(inner) => {
1663           word(s.s, ~"@");
1664           print_pat(s, inner, refutable);
1665       }
1666       ast::pat_uniq(inner) => {
1667           word(s.s, ~"~");
1668           print_pat(s, inner, refutable);
1669       }
1670       ast::pat_region(inner) => {
1671           word(s.s, ~"&");
1672           print_pat(s, inner, refutable);
1673       }
1674       ast::pat_lit(e) => print_expr(s, e),
1675       ast::pat_range(begin, end) => {
1676         print_expr(s, begin);
1677         space(s.s);
1678         word(s.s, ~"..");
1679         print_expr(s, end);
1680       }
1681       ast::pat_vec(elts, tail) => {
1682         word(s.s, ~"[");
1683         commasep(s, inconsistent, elts, |s, p| print_pat(s, p, refutable));
1684         do option::iter(&tail) |tail| {
1685             if vec::len(elts) != 0u { word_space(s, ~","); }
1686             word(s.s, ~"..");
1687             print_pat(s, *tail, refutable);
1688         }
1689         word(s.s, ~"]");
1690       }
1691     }
1692     (s.ann.post)(ann_node);
1693 }
1694
1695 // Returns whether it printed anything
1696 pub fn print_self_ty(s: @ps, self_ty: ast::self_ty_) -> bool {
1697     match self_ty {
1698       ast::sty_static | ast::sty_by_ref => { return false; }
1699       ast::sty_value => { word(s.s, ~"self"); }
1700       ast::sty_region(m) => {
1701         word(s.s, ~"&"); print_mutability(s, m); word(s.s, ~"self");
1702       }
1703       ast::sty_box(m) => {
1704         word(s.s, ~"@"); print_mutability(s, m); word(s.s, ~"self");
1705       }
1706       ast::sty_uniq(m) => {
1707         word(s.s, ~"~"); print_mutability(s, m); word(s.s, ~"self");
1708       }
1709     }
1710     return true;
1711 }
1712
1713 pub fn print_fn(s: @ps,
1714                 decl: ast::fn_decl,
1715                 purity: Option<ast::purity>,
1716                 name: ast::ident,
1717                 typarams: ~[ast::ty_param],
1718                 opt_self_ty: Option<ast::self_ty_>,
1719                 vis: ast::visibility) {
1720     head(s, ~"");
1721     print_fn_header_info(s, opt_self_ty, purity, ast::Many, None, vis);
1722     nbsp(s);
1723     print_ident(s, name);
1724     print_type_params(s, typarams);
1725     print_fn_args_and_ret(s, decl, opt_self_ty);
1726 }
1727
1728 pub fn print_fn_args(s: @ps, decl: ast::fn_decl,
1729                  opt_self_ty: Option<ast::self_ty_>) {
1730     // It is unfortunate to duplicate the commasep logic, but we we want the
1731     // self type and the args all in the same box.
1732     box(s, 0u, inconsistent);
1733     let mut first = true;
1734     for opt_self_ty.each |self_ty| {
1735         first = !print_self_ty(s, *self_ty);
1736     }
1737
1738     for decl.inputs.each |arg| {
1739         if first { first = false; } else { word_space(s, ~","); }
1740         print_arg(s, *arg);
1741     }
1742
1743     end(s);
1744 }
1745
1746 pub fn print_fn_args_and_ret(s: @ps, decl: ast::fn_decl,
1747                              opt_self_ty: Option<ast::self_ty_>) {
1748     popen(s);
1749     print_fn_args(s, decl, opt_self_ty);
1750     pclose(s);
1751
1752     maybe_print_comment(s, decl.output.span.lo);
1753     match decl.output.node {
1754         ast::ty_nil => {}
1755         _ => {
1756             space_if_not_bol(s);
1757             word_space(s, ~"->");
1758             print_type(s, decl.output);
1759         }
1760     }
1761 }
1762
1763 pub fn print_fn_block_args(s: @ps, decl: ast::fn_decl) {
1764     word(s.s, ~"|");
1765     print_fn_args(s, decl, None);
1766     word(s.s, ~"|");
1767
1768     match decl.output.node {
1769         ast::ty_infer => {}
1770         _ => {
1771             space_if_not_bol(s);
1772             word_space(s, ~"->");
1773             print_type(s, decl.output);
1774         }
1775     }
1776
1777     maybe_print_comment(s, decl.output.span.lo);
1778 }
1779
1780 pub fn mode_to_str(m: ast::mode) -> ~str {
1781     match m {
1782       ast::expl(ast::by_ref) => ~"&&",
1783       ast::expl(ast::by_copy) => ~"+",
1784       ast::expl(ast::by_val) => ~"++",
1785       ast::infer(_) => ~""
1786     }
1787 }
1788
1789 pub fn print_arg_mode(s: @ps, m: ast::mode) {
1790     let ms = mode_to_str(m);
1791     if ms != ~"" { word(s.s, ms); }
1792 }
1793
1794 pub fn print_bounds(s: @ps, bounds: @~[ast::ty_param_bound]) {
1795     if !bounds.is_empty() {
1796         word(s.s, ~":");
1797         let mut first = true;
1798         for vec::each(*bounds) |&bound| {
1799             nbsp(s);
1800             if first {
1801                 first = false;
1802             } else {
1803                 word_space(s, ~"+");
1804             }
1805
1806             match bound {
1807                 TraitTyParamBound(ty) => print_type(s, ty),
1808                 RegionTyParamBound => word(s.s, ~"&static"),
1809             }
1810         }
1811     }
1812 }
1813
1814 pub fn print_type_params(s: @ps, &&params: ~[ast::ty_param]) {
1815     if vec::len(params) > 0u {
1816         word(s.s, ~"<");
1817         fn printParam(s: @ps, param: ast::ty_param) {
1818             print_ident(s, param.ident);
1819             print_bounds(s, param.bounds);
1820         }
1821         commasep(s, inconsistent, params, printParam);
1822         word(s.s, ~">");
1823     }
1824 }
1825
1826 pub fn print_meta_item(s: @ps, &&item: @ast::meta_item) {
1827     ibox(s, indent_unit);
1828     match item.node {
1829       ast::meta_word(name) => word(s.s, *name),
1830       ast::meta_name_value(name, value) => {
1831         word_space(s, *name);
1832         word_space(s, ~"=");
1833         print_literal(s, @value);
1834       }
1835       ast::meta_list(name, ref items) => {
1836         word(s.s, *name);
1837         popen(s);
1838         commasep(
1839             s,
1840             consistent,
1841             /* FIXME (#2543) */ copy *items,
1842             print_meta_item
1843         );
1844         pclose(s);
1845       }
1846     }
1847     end(s);
1848 }
1849
1850 pub fn print_view_path(s: @ps, &&vp: @ast::view_path) {
1851     match vp.node {
1852       ast::view_path_simple(ident, path, namespace, _) => {
1853         if namespace == ast::module_ns {
1854             word_space(s, ~"mod");
1855         }
1856         if path.idents[vec::len(path.idents)-1u] != ident {
1857             print_ident(s, ident);
1858             space(s.s);
1859             word_space(s, ~"=");
1860         }
1861         print_path(s, path, false);
1862       }
1863
1864       ast::view_path_glob(path, _) => {
1865         print_path(s, path, false);
1866         word(s.s, ~"::*");
1867       }
1868
1869       ast::view_path_list(path, ref idents, _) => {
1870         print_path(s, path, false);
1871         word(s.s, ~"::{");
1872         do commasep(s, inconsistent, (*idents)) |s, w| {
1873             print_ident(s, w.node.name);
1874         }
1875         word(s.s, ~"}");
1876       }
1877     }
1878 }
1879
1880 pub fn print_view_paths(s: @ps, vps: ~[@ast::view_path]) {
1881     commasep(s, inconsistent, vps, print_view_path);
1882 }
1883
1884 pub fn print_view_item(s: @ps, item: @ast::view_item) {
1885     hardbreak_if_not_bol(s);
1886     maybe_print_comment(s, item.span.lo);
1887     print_outer_attributes(s, item.attrs);
1888     print_visibility(s, item.vis);
1889     match item.node {
1890         ast::view_item_extern_mod(id, mta, _) => {
1891             head(s, ~"extern mod");
1892             print_ident(s, id);
1893             if !mta.is_empty() {
1894                 popen(s);
1895                 commasep(s, consistent, mta, print_meta_item);
1896                 pclose(s);
1897             }
1898         }
1899
1900         ast::view_item_use(vps) => {
1901             head(s, ~"use");
1902             print_view_paths(s, vps);
1903         }
1904     }
1905     word(s.s, ~";");
1906     end(s); // end inner head-block
1907     end(s); // end outer head-block
1908 }
1909
1910 pub fn print_mutability(s: @ps, mutbl: ast::mutability) {
1911     match mutbl {
1912       ast::m_mutbl => word_nbsp(s, ~"mut"),
1913       ast::m_const => word_nbsp(s, ~"const"),
1914       ast::m_imm => {/* nothing */ }
1915     }
1916 }
1917
1918 pub fn print_mt(s: @ps, mt: ast::mt) {
1919     print_mutability(s, mt.mutbl);
1920     print_type(s, mt.ty);
1921 }
1922
1923 pub fn print_arg(s: @ps, input: ast::arg) {
1924     ibox(s, indent_unit);
1925     print_arg_mode(s, input.mode);
1926     if input.is_mutbl {
1927         word_space(s, ~"mut");
1928     }
1929     match input.ty.node {
1930       ast::ty_infer => print_irrefutable_pat(s, input.pat),
1931       _ => {
1932         match input.pat.node {
1933             ast::pat_ident(_, path, _) if
1934                 path.idents.len() == 1 &&
1935                 path.idents[0] == parse::token::special_idents::invalid => {
1936                 // Do nothing.
1937             }
1938             _ => {
1939                 print_irrefutable_pat(s, input.pat);
1940                 word(s.s, ~":");
1941                 space(s.s);
1942             }
1943         }
1944         print_type(s, input.ty);
1945       }
1946     }
1947     end(s);
1948 }
1949
1950 pub fn print_ty_fn(s: @ps,
1951                    opt_abi: Option<ast::Abi>,
1952                    opt_sigil: Option<ast::Sigil>,
1953                    opt_region: Option<@ast::region>,
1954                    purity: ast::purity,
1955                    onceness: ast::Onceness,
1956                    decl: ast::fn_decl, id: Option<ast::ident>,
1957                    tps: Option<~[ast::ty_param]>,
1958                    opt_self_ty: Option<ast::self_ty_>) {
1959     ibox(s, indent_unit);
1960
1961     // Duplicates the logic in `print_fn_header_info()`.  This is because that
1962     // function prints the sigil in the wrong place.  That should be fixed.
1963     print_self_ty_if_static(s, opt_self_ty);
1964     print_opt_abi(s, opt_abi);
1965     print_opt_sigil(s, opt_sigil);
1966     for opt_region.each |r| { print_region(s, ~"", *r, ~"/"); }
1967     print_purity(s, purity);
1968     print_onceness(s, onceness);
1969     word(s.s, ~"fn");
1970     match id { Some(id) => { word(s.s, ~" "); print_ident(s, id); } _ => () }
1971     match tps { Some(tps) => print_type_params(s, tps), _ => () }
1972     zerobreak(s.s);
1973
1974     popen(s);
1975     // It is unfortunate to duplicate the commasep logic, but we we want the
1976     // self type and the args all in the same box.
1977     box(s, 0u, inconsistent);
1978     let mut first = true;
1979     for opt_self_ty.each |self_ty| {
1980         first = !print_self_ty(s, *self_ty);
1981     }
1982     for decl.inputs.each |arg| {
1983         if first { first = false; } else { word_space(s, ~","); }
1984         print_arg(s, *arg);
1985     }
1986     end(s);
1987     pclose(s);
1988
1989     maybe_print_comment(s, decl.output.span.lo);
1990
1991     match decl.output.node {
1992         ast::ty_nil => {}
1993         _ => {
1994             space_if_not_bol(s);
1995             ibox(s, indent_unit);
1996             word_space(s, ~"->");
1997             if decl.cf == ast::noreturn { word_nbsp(s, ~"!"); }
1998             else { print_type(s, decl.output); }
1999             end(s);
2000         }
2001     }
2002
2003     end(s);
2004 }
2005
2006 pub fn maybe_print_trailing_comment(s: @ps, span: codemap::span,
2007                                     next_pos: Option<BytePos>) {
2008     let mut cm;
2009     match s.cm { Some(ccm) => cm = ccm, _ => return }
2010     match next_comment(s) {
2011       Some(ref cmnt) => {
2012         if (*cmnt).style != comments::trailing { return; }
2013         let span_line = cm.lookup_char_pos(span.hi);
2014         let comment_line = cm.lookup_char_pos((*cmnt).pos);
2015         let mut next = (*cmnt).pos + BytePos(1u);
2016         match next_pos { None => (), Some(p) => next = p }
2017         if span.hi < (*cmnt).pos && (*cmnt).pos < next &&
2018                span_line.line == comment_line.line {
2019             print_comment(s, (*cmnt));
2020             s.cur_cmnt_and_lit.cur_cmnt += 1u;
2021         }
2022       }
2023       _ => ()
2024     }
2025 }
2026
2027 pub fn print_remaining_comments(s: @ps) {
2028     // If there aren't any remaining comments, then we need to manually
2029     // make sure there is a line break at the end.
2030     if next_comment(s).is_none() { hardbreak(s.s); }
2031     loop {
2032         match next_comment(s) {
2033           Some(ref cmnt) => {
2034             print_comment(s, (*cmnt));
2035             s.cur_cmnt_and_lit.cur_cmnt += 1u;
2036           }
2037           _ => break
2038         }
2039     }
2040 }
2041
2042 pub fn print_literal(s: @ps, &&lit: @ast::lit) {
2043     maybe_print_comment(s, lit.span.lo);
2044     match next_lit(s, lit.span.lo) {
2045       Some(ref ltrl) => {
2046         word(s.s, (*ltrl).lit);
2047         return;
2048       }
2049       _ => ()
2050     }
2051     match lit.node {
2052       ast::lit_str(st) => print_string(s, *st),
2053       ast::lit_int(ch, ast::ty_char) => {
2054         word(s.s, ~"'" + char::escape_default(ch as char) + ~"'");
2055       }
2056       ast::lit_int(i, t) => {
2057         if i < 0_i64 {
2058             word(s.s,
2059                  ~"-" + u64::to_str_radix(-i as u64, 10u)
2060                  + ast_util::int_ty_to_str(t));
2061         } else {
2062             word(s.s,
2063                  u64::to_str_radix(i as u64, 10u)
2064                  + ast_util::int_ty_to_str(t));
2065         }
2066       }
2067       ast::lit_uint(u, t) => {
2068         word(s.s,
2069              u64::to_str_radix(u, 10u)
2070              + ast_util::uint_ty_to_str(t));
2071       }
2072       ast::lit_int_unsuffixed(i) => {
2073         if i < 0_i64 {
2074             word(s.s, ~"-" + u64::to_str_radix(-i as u64, 10u));
2075         } else {
2076             word(s.s, u64::to_str_radix(i as u64, 10u));
2077         }
2078       }
2079       ast::lit_float(f, t) => {
2080         word(s.s, *f + ast_util::float_ty_to_str(t));
2081       }
2082       ast::lit_float_unsuffixed(f) => word(s.s, *f),
2083       ast::lit_nil => word(s.s, ~"()"),
2084       ast::lit_bool(val) => {
2085         if val { word(s.s, ~"true"); } else { word(s.s, ~"false"); }
2086       }
2087     }
2088 }
2089
2090 pub fn lit_to_str(l: @ast::lit) -> ~str {
2091     return to_str(l, print_literal, parse::token::mk_fake_ident_interner());
2092 }
2093
2094 pub fn next_lit(s: @ps, pos: BytePos) -> Option<comments::lit> {
2095     match s.literals {
2096       Some(ref lits) => {
2097         while s.cur_cmnt_and_lit.cur_lit < vec::len((*lits)) {
2098             let ltrl = (*lits)[s.cur_cmnt_and_lit.cur_lit];
2099             if ltrl.pos > pos { return None; }
2100             s.cur_cmnt_and_lit.cur_lit += 1u;
2101             if ltrl.pos == pos { return Some(ltrl); }
2102         }
2103         return None;
2104       }
2105       _ => return None
2106     }
2107 }
2108
2109 pub fn maybe_print_comment(s: @ps, pos: BytePos) {
2110     loop {
2111         match next_comment(s) {
2112           Some(ref cmnt) => {
2113             if (*cmnt).pos < pos {
2114                 print_comment(s, (*cmnt));
2115                 s.cur_cmnt_and_lit.cur_cmnt += 1u;
2116             } else { break; }
2117           }
2118           _ => break
2119         }
2120     }
2121 }
2122
2123 pub fn print_comment(s: @ps, cmnt: comments::cmnt) {
2124     match cmnt.style {
2125       comments::mixed => {
2126         assert (vec::len(cmnt.lines) == 1u);
2127         zerobreak(s.s);
2128         word(s.s, cmnt.lines[0]);
2129         zerobreak(s.s);
2130       }
2131       comments::isolated => {
2132         pprust::hardbreak_if_not_bol(s);
2133         for cmnt.lines.each |line| {
2134             // Don't print empty lines because they will end up as trailing
2135             // whitespace
2136             if !line.is_empty() { word(s.s, *line); }
2137             hardbreak(s.s);
2138         }
2139       }
2140       comments::trailing => {
2141         word(s.s, ~" ");
2142         if vec::len(cmnt.lines) == 1u {
2143             word(s.s, cmnt.lines[0]);
2144             hardbreak(s.s);
2145         } else {
2146             ibox(s, 0u);
2147             for cmnt.lines.each |line| {
2148                 if !line.is_empty() { word(s.s, *line); }
2149                 hardbreak(s.s);
2150             }
2151             end(s);
2152         }
2153       }
2154       comments::blank_line => {
2155         // We need to do at least one, possibly two hardbreaks.
2156         let is_semi =
2157             match s.s.last_token() {
2158               pp::STRING(s, _) => *s == ~";",
2159               _ => false
2160             };
2161         if is_semi || is_begin(s) || is_end(s) { hardbreak(s.s); }
2162         hardbreak(s.s);
2163       }
2164     }
2165 }
2166
2167 pub fn print_string(s: @ps, st: ~str) {
2168     word(s.s, ~"\"");
2169     word(s.s, str::escape_default(st));
2170     word(s.s, ~"\"");
2171 }
2172
2173 pub fn to_str<T>(t: T, f: fn@(@ps, T), intr: @ident_interner) -> ~str {
2174     do io::with_str_writer |wr| {
2175         let s = rust_printer(wr, intr);
2176         f(s, t);
2177         eof(s.s);
2178     }
2179 }
2180
2181 pub fn next_comment(s: @ps) -> Option<comments::cmnt> {
2182     match s.comments {
2183       Some(ref cmnts) => {
2184         if s.cur_cmnt_and_lit.cur_cmnt < vec::len((*cmnts)) {
2185             return Some((*cmnts)[s.cur_cmnt_and_lit.cur_cmnt]);
2186         } else { return None::<comments::cmnt>; }
2187       }
2188       _ => return None::<comments::cmnt>
2189     }
2190 }
2191
2192 pub fn print_self_ty_if_static(s: @ps,
2193                                opt_self_ty: Option<ast::self_ty_>) {
2194     match opt_self_ty {
2195         Some(ast::sty_static) => { word(s.s, ~"static "); }
2196         _ => {}
2197     }
2198 }
2199
2200 pub fn print_opt_purity(s: @ps, opt_purity: Option<ast::purity>) {
2201     match opt_purity {
2202         Some(ast::impure_fn) => { }
2203         Some(purity) => {
2204             word_nbsp(s, purity_to_str(purity));
2205         }
2206         None => {}
2207     }
2208 }
2209
2210 pub fn print_opt_abi(s: @ps, opt_abi: Option<ast::Abi>) {
2211     match opt_abi {
2212         Some(ast::RustAbi) => { word_nbsp(s, ~"extern"); }
2213         None => {}
2214     };
2215 }
2216
2217 pub fn print_opt_sigil(s: @ps, opt_sigil: Option<ast::Sigil>) {
2218     match opt_sigil {
2219         Some(ast::BorrowedSigil) => { word(s.s, ~"&"); }
2220         Some(ast::OwnedSigil) => { word(s.s, ~"~"); }
2221         Some(ast::ManagedSigil) => { word(s.s, ~"@"); }
2222         None => {}
2223     };
2224 }
2225
2226 pub fn print_fn_header_info(s: @ps,
2227                             opt_sty: Option<ast::self_ty_>,
2228                             opt_purity: Option<ast::purity>,
2229                             onceness: ast::Onceness,
2230                             opt_sigil: Option<ast::Sigil>,
2231                             vis: ast::visibility) {
2232     print_self_ty_if_static(s, opt_sty);
2233     word(s.s, visibility_qualified(vis, ~""));
2234     print_opt_purity(s, opt_purity);
2235     print_onceness(s, onceness);
2236     word(s.s, ~"fn");
2237     print_opt_sigil(s, opt_sigil);
2238 }
2239
2240 pub fn opt_sigil_to_str(opt_p: Option<ast::Sigil>) -> ~str {
2241     match opt_p {
2242       None => ~"fn",
2243       Some(p) => fmt!("fn%s", p.to_str())
2244     }
2245 }
2246
2247 pub pure fn purity_to_str(p: ast::purity) -> ~str {
2248     match p {
2249       ast::impure_fn => ~"impure",
2250       ast::unsafe_fn => ~"unsafe",
2251       ast::pure_fn => ~"pure",
2252       ast::extern_fn => ~"extern"
2253     }
2254 }
2255
2256 pub pure fn onceness_to_str(o: ast::Onceness) -> ~str {
2257     match o {
2258         ast::Once => ~"once",
2259         ast::Many => ~"many"
2260     }
2261 }
2262
2263 pub fn print_purity(s: @ps, p: ast::purity) {
2264     match p {
2265       ast::impure_fn => (),
2266       _ => word_nbsp(s, purity_to_str(p))
2267     }
2268 }
2269
2270 pub fn print_onceness(s: @ps, o: ast::Onceness) {
2271     match o {
2272         ast::Once => { word_nbsp(s, ~"once"); }
2273         ast::Many => {}
2274     }
2275 }
2276
2277 #[cfg(test)]
2278 pub mod test {
2279     use ast;
2280     use ast_util;
2281     use parse;
2282     use super::*;
2283     //use util;
2284     use util::testing::check_equal;
2285
2286     fn string_check<T:Eq> (given : &T, expected: &T) {
2287         if !(given == expected) {
2288             fail!(fmt!("given %?, expected %?",given,expected));
2289         }
2290     }
2291
2292     #[test]
2293     fn test_fun_to_str() {
2294         let mock_interner = parse::token::mk_fake_ident_interner();
2295         let abba_ident = mock_interner.intern(@~"abba");
2296
2297         let decl: ast::fn_decl = ast::fn_decl {
2298             inputs: ~[],
2299             output: @ast::Ty {id: 0,
2300                               node: ast::ty_nil,
2301                               span: codemap::dummy_sp()},
2302             cf: ast::return_val
2303         };
2304         check_equal (&fun_to_str(decl, abba_ident, ~[],mock_interner),
2305                      &~"fn abba()");
2306     }
2307
2308     #[test]
2309     fn test_variant_to_str() {
2310         let mock_interner = parse::token::mk_fake_ident_interner();
2311         let ident = mock_interner.intern(@~"principal_skinner");
2312
2313         let var = codemap::respan(codemap::dummy_sp(), ast::variant_ {
2314             name: ident,
2315             attrs: ~[],
2316             // making this up as I go.... ?
2317             kind: ast::tuple_variant_kind(~[]),
2318             id: 0,
2319             disr_expr: None,
2320             vis: ast::public,
2321         });
2322
2323         let varstr = variant_to_str(var,mock_interner);
2324         check_equal(&varstr,&~"pub principal_skinner");
2325     }
2326 }
2327
2328 //
2329 // Local Variables:
2330 // mode: rust
2331 // fill-column: 78;
2332 // indent-tabs-mode: nil
2333 // c-basic-offset: 4
2334 // buffer-file-coding-system: utf-8-unix
2335 // End:
2336 //