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