]> git.lizzy.rs Git - rust.git/blob - src/comp/syntax/print/pprust.rs
Remove proto_sugar and 'lambda' as keyword, commit to fn@.
[rust.git] / src / comp / syntax / print / pprust.rs
1
2 import core::{vec, int, str, uint, option};
3 import std::io;
4 import parse::lexer;
5 import syntax::codemap::codemap;
6 import ast;
7 import ast_util;
8 import option::{some, none};
9 import pp::{break_offset, word,
10             space, zerobreak, hardbreak, breaks, consistent,
11             inconsistent, eof};
12
13 // The ps is stored here to prevent recursive type.
14 // FIXME use a nominal tag instead
15 tag ann_node {
16     node_block(ps, ast::blk);
17     node_item(ps, @ast::item);
18     node_expr(ps, @ast::expr);
19     node_pat(ps, @ast::pat);
20 }
21 type pp_ann = {pre: fn@(ann_node), post: fn@(ann_node)};
22
23 fn no_ann() -> pp_ann {
24     fn ignore(_node: ann_node) { }
25     ret {pre: ignore, post: ignore};
26 }
27
28 type ps =
29     @{s: pp::printer,
30       cm: option::t<codemap>,
31       comments: option::t<[lexer::cmnt]>,
32       literals: option::t<[lexer::lit]>,
33       mutable cur_cmnt: uint,
34       mutable cur_lit: uint,
35       mutable boxes: [pp::breaks],
36       ann: pp_ann};
37
38 fn ibox(s: ps, u: uint) { s.boxes += [pp::inconsistent]; pp::ibox(s.s, u); }
39
40 fn end(s: ps) { vec::pop(s.boxes); pp::end(s.s); }
41
42 fn rust_printer(writer: io::writer) -> ps {
43     let boxes: [pp::breaks] = [];
44     ret @{s: pp::mk_printer(writer, default_columns),
45           cm: none::<codemap>,
46           comments: none::<[lexer::cmnt]>,
47           literals: none::<[lexer::lit]>,
48           mutable cur_cmnt: 0u,
49           mutable cur_lit: 0u,
50           mutable boxes: boxes,
51           ann: no_ann()};
52 }
53
54 const indent_unit: uint = 4u;
55 const alt_indent_unit: uint = 2u;
56
57 const default_columns: uint = 78u;
58
59 // Requires you to pass an input filename and reader so that
60 // it can scan the input text for comments and literals to
61 // copy forward.
62 fn print_crate(cm: codemap, crate: @ast::crate, filename: str, in: io::reader,
63                out: io::writer, ann: pp_ann) {
64     let boxes: [pp::breaks] = [];
65     let r = lexer::gather_comments_and_literals(cm, filename, in);
66     let s =
67         @{s: pp::mk_printer(out, default_columns),
68           cm: some(cm),
69           comments: some(r.cmnts),
70           literals: some(r.lits),
71           mutable cur_cmnt: 0u,
72           mutable cur_lit: 0u,
73           mutable boxes: boxes,
74           ann: ann};
75     print_mod(s, crate.node.module, crate.node.attrs);
76     print_remaining_comments(s);
77     eof(s.s);
78 }
79
80 fn ty_to_str(ty: @ast::ty) -> str { be to_str(ty, print_type); }
81
82 fn pat_to_str(pat: @ast::pat) -> str { be to_str(pat, print_pat); }
83
84 fn expr_to_str(e: @ast::expr) -> str { be to_str(e, print_expr); }
85
86 fn stmt_to_str(s: ast::stmt) -> str { be to_str(s, print_stmt); }
87
88 fn item_to_str(i: @ast::item) -> str { be to_str(i, print_item); }
89
90 fn path_to_str(&&p: @ast::path) -> str {
91     be to_str(p, bind print_path(_, _, false));
92 }
93
94 fn fun_to_str(decl: ast::fn_decl, name: ast::ident,
95               params: [ast::ty_param]) -> str {
96     let writer = io::string_writer();
97     let s = rust_printer(writer.get_writer());
98     print_fn(s, decl, name, params);
99     eof(s.s);
100     ret writer.get_str();
101 }
102
103 fn block_to_str(blk: ast::blk) -> str {
104     let writer = io::string_writer();
105     let s = rust_printer(writer.get_writer());
106     // containing cbox, will be closed by print-block at }
107
108     cbox(s, indent_unit);
109     // head-ibox, will be closed by print-block after {
110
111     ibox(s, 0u);
112     print_block(s, blk);
113     eof(s.s);
114     ret writer.get_str();
115 }
116
117 fn meta_item_to_str(mi: ast::meta_item) -> str {
118     ret to_str(@mi, print_meta_item);
119 }
120
121 fn attribute_to_str(attr: ast::attribute) -> str {
122     be to_str(attr, print_attribute);
123 }
124
125 fn cbox(s: ps, u: uint) { s.boxes += [pp::consistent]; pp::cbox(s.s, u); }
126
127 fn box(s: ps, u: uint, b: pp::breaks) { s.boxes += [b]; pp::box(s.s, u, b); }
128
129 fn nbsp(s: ps) { word(s.s, " "); }
130
131 fn word_nbsp(s: ps, w: str) { word(s.s, w); nbsp(s); }
132
133 fn word_space(s: ps, w: str) { word(s.s, w); space(s.s); }
134
135 fn popen(s: ps) { word(s.s, "("); }
136
137 fn pclose(s: ps) { word(s.s, ")"); }
138
139 fn head(s: ps, w: str) {
140     // outer-box is consistent
141     cbox(s, indent_unit);
142     // head-box is inconsistent
143     ibox(s, str::char_len(w) + 1u);
144     // keyword that starts the head
145     word_nbsp(s, w);
146 }
147
148 fn bopen(s: ps) {
149     word(s.s, "{");
150     end(s); // close the head-box
151 }
152
153 fn bclose_(s: ps, span: codemap::span, indented: uint) {
154     maybe_print_comment(s, span.hi);
155     break_offset_if_not_bol(s, 1u, -(indented as int));
156     word(s.s, "}");
157     end(s); // close the outer-box
158 }
159 fn bclose(s: ps, span: codemap::span) { bclose_(s, span, indent_unit); }
160
161 fn is_begin(s: ps) -> bool {
162     alt s.s.last_token() { pp::BEGIN(_) { true } _ { false } }
163 }
164
165 fn is_end(s: ps) -> bool {
166     alt s.s.last_token() { pp::END. { true } _ { false } }
167 }
168
169 fn is_bol(s: ps) -> bool {
170     ret s.s.last_token() == pp::EOF ||
171             s.s.last_token() == pp::hardbreak_tok();
172 }
173
174 fn hardbreak_if_not_bol(s: ps) { if !is_bol(s) { hardbreak(s.s); } }
175 fn space_if_not_bol(s: ps) { if !is_bol(s) { space(s.s); } }
176 fn break_offset_if_not_bol(s: ps, n: uint, off: int) {
177     if !is_bol(s) {
178         break_offset(s.s, n, off);
179     } else {
180         if off != 0 && s.s.last_token() == pp::hardbreak_tok() {
181             // We do something pretty sketchy here: tuck the nonzero
182             // offset-adjustment we were going to deposit along with the
183             // break into the previous hardbreak.
184             s.s.replace_last_token(pp::hardbreak_tok_offset(off));
185         }
186     }
187 }
188
189 // Synthesizes a comment that was not textually present in the original source
190 // file.
191 fn synth_comment(s: ps, text: str) {
192     word(s.s, "/*");
193     space(s.s);
194     word(s.s, text);
195     space(s.s);
196     word(s.s, "*/");
197 }
198
199 fn commasep<IN>(s: ps, b: breaks, elts: [IN], op: fn(ps, IN)) {
200     box(s, 0u, b);
201     let first = true;
202     for elt: IN in elts {
203         if first { first = false; } else { word_space(s, ","); }
204         op(s, elt);
205     }
206     end(s);
207 }
208
209
210 fn commasep_cmnt<IN>(s: ps, b: breaks, elts: [IN], op: fn(ps, IN),
211                      get_span: fn(IN) -> codemap::span) {
212     box(s, 0u, b);
213     let len = vec::len::<IN>(elts);
214     let i = 0u;
215     for elt: IN in elts {
216         maybe_print_comment(s, get_span(elt).hi);
217         op(s, elt);
218         i += 1u;
219         if i < len {
220             word(s.s, ",");
221             maybe_print_trailing_comment(s, get_span(elt),
222                                          some(get_span(elts[i]).hi));
223             space_if_not_bol(s);
224         }
225     }
226     end(s);
227 }
228
229 fn commasep_exprs(s: ps, b: breaks, exprs: [@ast::expr]) {
230     fn expr_span(&&expr: @ast::expr) -> codemap::span { ret expr.span; }
231     commasep_cmnt(s, b, exprs, print_expr, expr_span);
232 }
233
234 fn print_mod(s: ps, _mod: ast::_mod, attrs: [ast::attribute]) {
235     print_inner_attributes(s, attrs);
236     for vitem: @ast::view_item in _mod.view_items {
237         print_view_item(s, vitem);
238     }
239     for item: @ast::item in _mod.items { print_item(s, item); }
240 }
241
242 fn print_native_mod(s: ps, nmod: ast::native_mod, attrs: [ast::attribute]) {
243     print_inner_attributes(s, attrs);
244     for vitem: @ast::view_item in nmod.view_items {
245         print_view_item(s, vitem);
246     }
247     for item: @ast::native_item in nmod.items { print_native_item(s, item); }
248 }
249
250 fn print_type(s: ps, &&ty: @ast::ty) {
251     maybe_print_comment(s, ty.span.lo);
252     ibox(s, 0u);
253     alt ty.node {
254       ast::ty_nil. { word(s.s, "()"); }
255       ast::ty_bool. { word(s.s, "bool"); }
256       ast::ty_bot. { word(s.s, "!"); }
257       ast::ty_int(ast::ty_i.) { word(s.s, "int"); }
258       ast::ty_int(ast::ty_char.) { word(s.s, "char"); }
259       ast::ty_int(t) { word(s.s, ast_util::int_ty_to_str(t)); }
260       ast::ty_uint(ast::ty_u.) { word(s.s, "uint"); }
261       ast::ty_uint(t) { word(s.s, ast_util::uint_ty_to_str(t)); }
262       ast::ty_float(ast::ty_f.) { word(s.s, "float"); }
263       ast::ty_float(t) { word(s.s, ast_util::float_ty_to_str(t)); }
264       ast::ty_str. { word(s.s, "str"); }
265       ast::ty_box(mt) { word(s.s, "@"); print_mt(s, mt); }
266       ast::ty_uniq(mt) { word(s.s, "~"); print_mt(s, mt); }
267       ast::ty_vec(mt) {
268         word(s.s, "[");
269         alt mt.mut {
270           ast::mut. { word_space(s, "mutable"); }
271           ast::maybe_mut. { word_space(s, "const"); }
272           ast::imm. { }
273         }
274         print_type(s, mt.ty);
275         word(s.s, "]");
276       }
277       ast::ty_ptr(mt) { word(s.s, "*"); print_mt(s, mt); }
278       ast::ty_task. { word(s.s, "task"); }
279       ast::ty_port(t) {
280         word(s.s, "port<");
281         print_type(s, t);
282         word(s.s, ">");
283       }
284       ast::ty_chan(t) {
285         word(s.s, "chan<");
286         print_type(s, t);
287         word(s.s, ">");
288       }
289       ast::ty_rec(fields) {
290         word(s.s, "{");
291         fn print_field(s: ps, f: ast::ty_field) {
292             cbox(s, indent_unit);
293             print_mutability(s, f.node.mt.mut);
294             word(s.s, f.node.ident);
295             word_space(s, ":");
296             print_type(s, f.node.mt.ty);
297             end(s);
298         }
299         fn get_span(f: ast::ty_field) -> codemap::span { ret f.span; }
300         commasep_cmnt(s, consistent, fields, print_field, get_span);
301         word(s.s, ",}");
302       }
303       ast::ty_tup(elts) {
304         popen(s);
305         commasep(s, inconsistent, elts, print_type);
306         pclose(s);
307       }
308       ast::ty_fn(proto, d) {
309         print_ty_fn(s, proto, d, none, none);
310       }
311       ast::ty_obj(methods) {
312         head(s, "obj");
313         bopen(s);
314         for m in methods { print_ty_method(s, m); }
315         bclose(s, ty.span);
316       }
317       ast::ty_path(path, _) { print_path(s, path, false); }
318       ast::ty_type. { word(s.s, "type"); }
319       ast::ty_constr(t, cs) {
320         print_type(s, t);
321         space(s.s);
322         word(s.s, ast_ty_constrs_str(cs));
323       }
324     }
325     end(s);
326 }
327
328 fn print_native_item(s: ps, item: @ast::native_item) {
329     hardbreak_if_not_bol(s);
330     maybe_print_comment(s, item.span.lo);
331     print_outer_attributes(s, item.attrs);
332     alt item.node {
333       ast::native_item_ty. {
334         ibox(s, indent_unit);
335         ibox(s, 0u);
336         word_nbsp(s, "type");
337         word(s.s, item.ident);
338         end(s); // end the inner ibox
339         word(s.s, ";");
340         end(s); // end the outer ibox
341
342       }
343       ast::native_item_fn(decl, typarams) {
344         print_fn(s, decl, item.ident, typarams);
345         end(s); // end head-ibox
346         word(s.s, ";");
347         end(s); // end the outer fn box
348       }
349     }
350 }
351
352 fn print_item(s: ps, &&item: @ast::item) {
353     hardbreak_if_not_bol(s);
354     maybe_print_comment(s, item.span.lo);
355     print_outer_attributes(s, item.attrs);
356     let ann_node = node_item(s, item);
357     s.ann.pre(ann_node);
358     alt item.node {
359       ast::item_const(ty, expr) {
360         head(s, "const");
361         word_space(s, item.ident + ":");
362         print_type(s, ty);
363         space(s.s);
364         end(s); // end the head-ibox
365
366         word_space(s, "=");
367         print_expr(s, expr);
368         word(s.s, ";");
369         end(s); // end the outer cbox
370
371       }
372       ast::item_fn(decl, typarams, body) {
373         print_fn(s, decl, item.ident, typarams);
374         word(s.s, " ");
375         print_block(s, body);
376       }
377       ast::item_mod(_mod) {
378         head(s, "mod");
379         word_nbsp(s, item.ident);
380         bopen(s);
381         print_mod(s, _mod, item.attrs);
382         bclose(s, item.span);
383       }
384       ast::item_native_mod(nmod) {
385         head(s, "native");
386         word_nbsp(s, "mod");
387         word_nbsp(s, item.ident);
388         bopen(s);
389         print_native_mod(s, nmod, item.attrs);
390         bclose(s, item.span);
391       }
392       ast::item_ty(ty, params) {
393         ibox(s, indent_unit);
394         ibox(s, 0u);
395         word_nbsp(s, "type");
396         word(s.s, item.ident);
397         print_type_params(s, params);
398         end(s); // end the inner ibox
399
400         space(s.s);
401         word_space(s, "=");
402         print_type(s, ty);
403         word(s.s, ";");
404         end(s); // end the outer ibox
405       }
406       ast::item_tag(variants, params) {
407         let newtype =
408             vec::len(variants) == 1u &&
409                 str::eq(item.ident, variants[0].node.name) &&
410                 vec::len(variants[0].node.args) == 1u;
411         if newtype {
412             ibox(s, indent_unit);
413             word_space(s, "tag");
414         } else { head(s, "tag"); }
415         word(s.s, item.ident);
416         print_type_params(s, params);
417         space(s.s);
418         if newtype {
419             word_space(s, "=");
420             print_type(s, variants[0].node.args[0].ty);
421             word(s.s, ";");
422             end(s);
423         } else {
424             bopen(s);
425             for v: ast::variant in variants {
426                 space_if_not_bol(s);
427                 maybe_print_comment(s, v.span.lo);
428                 word(s.s, v.node.name);
429                 if vec::len(v.node.args) > 0u {
430                     popen(s);
431                     fn print_variant_arg(s: ps, arg: ast::variant_arg) {
432                         print_type(s, arg.ty);
433                     }
434                     commasep(s, consistent, v.node.args, print_variant_arg);
435                     pclose(s);
436                 }
437                 word(s.s, ";");
438                 maybe_print_trailing_comment(s, v.span, none::<uint>);
439             }
440             bclose(s, item.span);
441         }
442       }
443       ast::item_obj(_obj, params, _) {
444         head(s, "obj");
445         word(s.s, item.ident);
446         print_type_params(s, params);
447         popen(s);
448         fn print_field(s: ps, field: ast::obj_field) {
449             ibox(s, indent_unit);
450             print_mutability(s, field.mut);
451             word_space(s, field.ident + ":");
452             print_type(s, field.ty);
453             end(s);
454         }
455         fn get_span(f: ast::obj_field) -> codemap::span { ret f.ty.span; }
456         commasep_cmnt(s, consistent, _obj.fields, print_field, get_span);
457         pclose(s);
458         space(s.s);
459         bopen(s);
460         for meth: @ast::method in _obj.methods {
461             hardbreak_if_not_bol(s);
462             maybe_print_comment(s, meth.span.lo);
463             print_fn(s, meth.decl, meth.ident, meth.tps);
464             word(s.s, " ");
465             print_block(s, meth.body);
466         }
467         bclose(s, item.span);
468       }
469       ast::item_impl(tps, ifce, ty, methods) {
470         head(s, "impl");
471         word(s.s, item.ident);
472         print_type_params(s, tps);
473         space(s.s);
474         alt ifce {
475           some(ty) {
476             word_nbsp(s, "of");
477             print_type(s, ty);
478             space(s.s);
479           }
480           _ {}
481         }
482         word_nbsp(s, "for");
483         print_type(s, ty);
484         space(s.s);
485         bopen(s);
486         for meth in methods {
487             hardbreak_if_not_bol(s);
488             maybe_print_comment(s, meth.span.lo);
489             print_fn(s, meth.decl, meth.ident, meth.tps);
490             word(s.s, " ");
491             print_block(s, meth.body);
492         }
493         bclose(s, item.span);
494       }
495       ast::item_iface(tps, methods) {
496         head(s, "iface");
497         word(s.s, item.ident);
498         print_type_params(s, tps);
499         bopen(s);
500         for meth in methods { print_ty_method(s, meth); }
501         bclose(s, item.span);
502       }
503       ast::item_res(decl, tps, body, dt_id, ct_id) {
504         head(s, "resource");
505         word(s.s, item.ident);
506         print_type_params(s, tps);
507         popen(s);
508         word_space(s, decl.inputs[0].ident + ":");
509         print_type(s, decl.inputs[0].ty);
510         pclose(s);
511         space(s.s);
512         print_block(s, body);
513       }
514     }
515     s.ann.post(ann_node);
516 }
517
518 fn print_ty_method(s: ps, m: ast::ty_method) {
519     hardbreak_if_not_bol(s);
520     cbox(s, indent_unit);
521     maybe_print_comment(s, m.span.lo);
522     print_ty_fn(s, ast::proto_bare, m.decl, some(m.ident), some(m.tps));
523     word(s.s, ";");
524     end(s);
525 }
526
527 fn print_outer_attributes(s: ps, attrs: [ast::attribute]) {
528     let count = 0;
529     for attr: ast::attribute in attrs {
530         alt attr.node.style {
531           ast::attr_outer. { print_attribute(s, attr); count += 1; }
532           _ {/* fallthrough */ }
533         }
534     }
535     if count > 0 { hardbreak_if_not_bol(s); }
536 }
537
538 fn print_inner_attributes(s: ps, attrs: [ast::attribute]) {
539     let count = 0;
540     for attr: ast::attribute in attrs {
541         alt attr.node.style {
542           ast::attr_inner. {
543             print_attribute(s, attr);
544             word(s.s, ";");
545             count += 1;
546           }
547           _ {/* fallthrough */ }
548         }
549     }
550     if count > 0 { hardbreak_if_not_bol(s); }
551 }
552
553 fn print_attribute(s: ps, attr: ast::attribute) {
554     hardbreak_if_not_bol(s);
555     maybe_print_comment(s, attr.span.lo);
556     word(s.s, "#[");
557     print_meta_item(s, @attr.node.value);
558     word(s.s, "]");
559 }
560
561
562 fn print_stmt(s: ps, st: ast::stmt) {
563     maybe_print_comment(s, st.span.lo);
564     alt st.node {
565       ast::stmt_decl(decl, _) {
566         print_decl(s, decl);
567       }
568       ast::stmt_expr(expr, _) {
569         space_if_not_bol(s);
570         print_expr(s, expr);
571       }
572       ast::stmt_semi(expr, _) {
573         space_if_not_bol(s);
574         print_expr(s, expr);
575         word(s.s, ";");
576       }
577     }
578     if parse::parser::stmt_ends_with_semi(st) { word(s.s, ";"); }
579     maybe_print_trailing_comment(s, st.span, none::<uint>);
580 }
581
582 fn print_block(s: ps, blk: ast::blk) {
583     print_possibly_embedded_block(s, blk, block_normal, indent_unit);
584 }
585
586 tag embed_type { block_macro; block_block_fn; block_normal; }
587
588 fn print_possibly_embedded_block(s: ps, blk: ast::blk, embedded: embed_type,
589                                  indented: uint) {
590     alt blk.node.rules {
591       ast::unchecked_blk. { word(s.s, "unchecked"); }
592       ast::unsafe_blk. { word(s.s, "unsafe"); }
593       ast::default_blk. { }
594     }
595
596     maybe_print_comment(s, blk.span.lo);
597     let ann_node = node_block(s, blk);
598     s.ann.pre(ann_node);
599     alt embedded {
600       block_macro. { word(s.s, "#{"); end(s); }
601       block_block_fn. { end(s); }
602       block_normal. { bopen(s); }
603     }
604
605     for vi in blk.node.view_items { print_view_item(s, vi); }
606     for st: @ast::stmt in blk.node.stmts {
607         print_stmt(s, *st);
608     }
609     alt blk.node.expr {
610       some(expr) {
611         space_if_not_bol(s);
612         print_expr(s, expr);
613         maybe_print_trailing_comment(s, expr.span, some(blk.span.hi));
614       }
615       _ { }
616     }
617     bclose_(s, blk.span, indented);
618     s.ann.post(ann_node);
619 }
620
621 // ret and fail, without arguments cannot appear is the discriminant of if,
622 // alt, do, & while unambiguously without being parenthesized
623 fn print_maybe_parens_discrim(s: ps, e: @ast::expr) {
624     let disambig = alt e.node {
625       ast::expr_ret(none.) | ast::expr_fail(none.) { true }
626       _ { false }
627     };
628     if disambig { popen(s); }
629     print_expr(s, e);
630     if disambig { pclose(s); }
631 }
632
633 fn print_if(s: ps, test: @ast::expr, blk: ast::blk,
634             elseopt: option::t<@ast::expr>, chk: bool) {
635     head(s, "if");
636     if chk { word_nbsp(s, "check"); }
637     print_maybe_parens_discrim(s, test);
638     space(s.s);
639     print_block(s, blk);
640     fn do_else(s: ps, els: option::t<@ast::expr>) {
641         alt els {
642           some(_else) {
643             alt _else.node {
644
645
646
647
648
649               // "another else-if"
650               ast::expr_if(i, t, e) {
651                 cbox(s, indent_unit - 1u);
652                 ibox(s, 0u);
653                 word(s.s, " else if ");
654                 print_maybe_parens_discrim(s, i);
655                 space(s.s);
656                 print_block(s, t);
657                 do_else(s, e);
658               }
659
660
661
662
663
664               // "final else"
665               ast::expr_block(b) {
666                 cbox(s, indent_unit - 1u);
667                 ibox(s, 0u);
668                 word(s.s, " else ");
669                 print_block(s, b);
670               }
671             }
672           }
673           _ {/* fall through */ }
674         }
675     }
676     do_else(s, elseopt);
677 }
678
679 fn print_mac(s: ps, m: ast::mac) {
680     alt m.node {
681       ast::mac_invoc(path, arg, body) {
682         word(s.s, "#");
683         print_path(s, path, false);
684         alt arg.node { ast::expr_vec(_, _) { } _ { word(s.s, " "); } }
685         print_expr(s, arg);
686         // FIXME: extension 'body'
687       }
688       ast::mac_embed_type(ty) {
689         word(s.s, "#<");
690         print_type(s, ty);
691         word(s.s, ">");
692       }
693       ast::mac_embed_block(blk) {
694         print_possibly_embedded_block(s, blk, block_normal, indent_unit);
695       }
696       ast::mac_ellipsis. { word(s.s, "..."); }
697     }
698 }
699
700 fn print_expr(s: ps, &&expr: @ast::expr) {
701     maybe_print_comment(s, expr.span.lo);
702     ibox(s, indent_unit);
703     let ann_node = node_expr(s, expr);
704     s.ann.pre(ann_node);
705     alt expr.node {
706       ast::expr_vec(exprs, mut) {
707         ibox(s, indent_unit);
708         word(s.s, "[");
709         if mut == ast::mut {
710             word(s.s, "mutable");
711             if vec::len(exprs) > 0u { nbsp(s); }
712         }
713         commasep_exprs(s, inconsistent, exprs);
714         word(s.s, "]");
715         end(s);
716       }
717       ast::expr_rec(fields, wth) {
718         fn print_field(s: ps, field: ast::field) {
719             ibox(s, indent_unit);
720             if field.node.mut == ast::mut { word_nbsp(s, "mutable"); }
721             word(s.s, field.node.ident);
722             word_space(s, ":");
723             print_expr(s, field.node.expr);
724             end(s);
725         }
726         fn get_span(field: ast::field) -> codemap::span { ret field.span; }
727         word(s.s, "{");
728         commasep_cmnt(s, consistent, fields, print_field, get_span);
729         alt wth {
730           some(expr) {
731             if vec::len(fields) > 0u { space(s.s); }
732             ibox(s, indent_unit);
733             word_space(s, "with");
734             print_expr(s, expr);
735             end(s);
736           }
737           _ { word(s.s, ","); }
738         }
739         word(s.s, "}");
740       }
741       ast::expr_tup(exprs) {
742         popen(s);
743         commasep_exprs(s, inconsistent, exprs);
744         pclose(s);
745       }
746       ast::expr_call(func, args, has_block) {
747         print_expr_parens_if_not_bot(s, func);
748         let base_args = args, blk = none;
749         if has_block { blk = some(vec::pop(base_args)); }
750         if !has_block || vec::len(base_args) > 0u {
751             popen(s);
752             commasep_exprs(s, inconsistent, base_args);
753             pclose(s);
754         }
755         if has_block {
756             nbsp(s);
757             print_expr(s, option::get(blk));
758         }
759       }
760       ast::expr_bind(func, args) {
761         fn print_opt(s: ps, expr: option::t<@ast::expr>) {
762             alt expr {
763               some(expr) { print_expr(s, expr); }
764               _ { word(s.s, "_"); }
765             }
766         }
767         word_nbsp(s, "bind");
768         print_expr(s, func);
769         popen(s);
770         commasep(s, inconsistent, args, print_opt);
771         pclose(s);
772       }
773       ast::expr_binary(op, lhs, rhs) {
774         let prec = operator_prec(op);
775         print_op_maybe_parens(s, lhs, prec);
776         space(s.s);
777         word_space(s, ast_util::binop_to_str(op));
778         print_op_maybe_parens(s, rhs, prec + 1);
779       }
780       ast::expr_unary(op, expr) {
781         word(s.s, ast_util::unop_to_str(op));
782         print_op_maybe_parens(s, expr, parse::parser::unop_prec);
783       }
784       ast::expr_lit(lit) { print_literal(s, lit); }
785       ast::expr_cast(expr, ty) {
786         print_op_maybe_parens(s, expr, parse::parser::as_prec);
787         space(s.s);
788         word_space(s, "as");
789         print_type(s, ty);
790       }
791       ast::expr_if(test, blk, elseopt) {
792         print_if(s, test, blk, elseopt, false);
793       }
794       ast::expr_if_check(test, blk, elseopt) {
795         print_if(s, test, blk, elseopt, true);
796       }
797       ast::expr_ternary(test, then, els) {
798         print_expr(s, test);
799         space(s.s);
800         word_space(s, "?");
801         print_expr(s, then);
802         space(s.s);
803         word_space(s, ":");
804         print_expr(s, els);
805       }
806       ast::expr_while(test, blk) {
807         head(s, "while");
808         print_maybe_parens_discrim(s, test);
809         space(s.s);
810         print_block(s, blk);
811       }
812       ast::expr_for(decl, expr, blk) {
813         head(s, "for");
814         print_for_decl(s, decl, expr);
815         space(s.s);
816         print_block(s, blk);
817       }
818       ast::expr_do_while(blk, expr) {
819         head(s, "do");
820         space(s.s);
821         print_block(s, blk);
822         space(s.s);
823         word_space(s, "while");
824         print_expr(s, expr);
825       }
826       ast::expr_alt(expr, arms) {
827         cbox(s, alt_indent_unit);
828         ibox(s, 4u);
829         word_nbsp(s, "alt");
830         print_maybe_parens_discrim(s, expr);
831         space(s.s);
832         bopen(s);
833         for arm: ast::arm in arms {
834             space(s.s);
835             cbox(s, alt_indent_unit);
836             ibox(s, 0u);
837             let first = true;
838             for p: @ast::pat in arm.pats {
839                 if first {
840                     first = false;
841                 } else { space(s.s); word_space(s, "|"); }
842                 print_pat(s, p);
843             }
844             space(s.s);
845             alt arm.guard {
846               some(e) { word_space(s, "if"); print_expr(s, e); space(s.s); }
847               none. { }
848             }
849             print_possibly_embedded_block(s, arm.body, block_normal,
850                                           alt_indent_unit);
851         }
852         bclose_(s, expr.span, alt_indent_unit);
853       }
854       ast::expr_fn(proto, decl, body, cap_clause) {
855         // containing cbox, will be closed by print-block at }
856         cbox(s, indent_unit);
857         // head-box, will be closed by print-block at start
858         ibox(s, 0u);
859         word(s.s, proto_to_str(proto));
860         print_cap_clause(s, *cap_clause);
861         print_fn_args_and_ret(s, decl);
862         space(s.s);
863         print_block(s, body);
864       }
865       ast::expr_fn_block(decl, body) {
866         // containing cbox, will be closed by print-block at }
867         cbox(s, indent_unit);
868         // head-box, will be closed by print-block at start
869         ibox(s, 0u);
870         word(s.s, "{");
871         print_fn_block_args(s, decl);
872         print_possibly_embedded_block(s, body, block_block_fn, indent_unit);
873       }
874       ast::expr_block(blk) {
875         // containing cbox, will be closed by print-block at }
876         cbox(s, indent_unit);
877         // head-box, will be closed by print-block after {
878         ibox(s, 0u);
879         print_block(s, blk);
880       }
881       ast::expr_copy(e) { word_space(s, "copy"); print_expr(s, e); }
882       ast::expr_move(lhs, rhs) {
883         print_expr(s, lhs);
884         space(s.s);
885         word_space(s, "<-");
886         print_expr(s, rhs);
887       }
888       ast::expr_assign(lhs, rhs) {
889         print_expr(s, lhs);
890         space(s.s);
891         word_space(s, "=");
892         print_expr(s, rhs);
893       }
894       ast::expr_swap(lhs, rhs) {
895         print_expr(s, lhs);
896         space(s.s);
897         word_space(s, "<->");
898         print_expr(s, rhs);
899       }
900       ast::expr_assign_op(op, lhs, rhs) {
901         print_expr(s, lhs);
902         space(s.s);
903         word(s.s, ast_util::binop_to_str(op));
904         word_space(s, "=");
905         print_expr(s, rhs);
906       }
907       ast::expr_field(expr, id, tys) {
908         // Deal with '10.x'
909         if ends_in_lit_int(expr) {
910             popen(s); print_expr(s, expr); pclose(s);
911         } else {
912             print_expr_parens_if_not_bot(s, expr);
913         }
914         word(s.s, ".");
915         word(s.s, id);
916         if vec::len(tys) > 0u {
917             word(s.s, "::<");
918             commasep(s, inconsistent, tys, print_type);
919             word(s.s, ">");
920         }
921       }
922       ast::expr_index(expr, index) {
923         print_expr_parens_if_not_bot(s, expr);
924         word(s.s, "[");
925         print_expr(s, index);
926         word(s.s, "]");
927       }
928       ast::expr_path(path) { print_path(s, path, true); }
929       ast::expr_fail(maybe_fail_val) {
930         word(s.s, "fail");
931         alt maybe_fail_val {
932           some(expr) { word(s.s, " "); print_expr(s, expr); }
933           _ { }
934         }
935       }
936       ast::expr_break. { word(s.s, "break"); }
937       ast::expr_cont. { word(s.s, "cont"); }
938       ast::expr_ret(result) {
939         word(s.s, "ret");
940         alt result {
941           some(expr) { word(s.s, " "); print_expr(s, expr); }
942           _ { }
943         }
944       }
945       ast::expr_be(result) { word_nbsp(s, "be"); print_expr(s, result); }
946       ast::expr_log(lvl, lexp, expr) {
947         alt lvl {
948           1 { word_nbsp(s, "log"); print_expr(s, expr); }
949           0 { word_nbsp(s, "log_err"); print_expr(s, expr); }
950           2 {
951             word_nbsp(s, "log");
952             popen(s);
953             print_expr(s, lexp);
954             word(s.s, ",");
955             space_if_not_bol(s);
956             print_expr(s, expr);
957             pclose(s);
958           }
959         }
960       }
961       ast::expr_check(m, expr) {
962         alt m {
963           ast::claimed_expr. { word_nbsp(s, "claim"); }
964           ast::checked_expr. { word_nbsp(s, "check"); }
965         }
966         popen(s);
967         print_expr(s, expr);
968         pclose(s);
969       }
970       ast::expr_assert(expr) {
971         word_nbsp(s, "assert");
972         popen(s);
973         print_expr(s, expr);
974         pclose(s);
975       }
976       ast::expr_mac(m) { print_mac(s, m); }
977       ast::expr_anon_obj(anon_obj) {
978         head(s, "obj");
979
980         // Fields
981         popen(s);
982         fn print_field(s: ps, field: ast::anon_obj_field) {
983             ibox(s, indent_unit);
984             print_mutability(s, field.mut);
985             word_space(s, field.ident + ":");
986             print_type(s, field.ty);
987             space(s.s);
988             word_space(s, "=");
989             print_expr(s, field.expr);
990             end(s);
991         }
992         fn get_span(f: ast::anon_obj_field) -> codemap::span {
993             ret f.ty.span;
994         }
995         alt anon_obj.fields {
996           none. { }
997           some(fields) {
998             commasep_cmnt(s, consistent, fields, print_field, get_span);
999           }
1000         }
1001         pclose(s);
1002         space(s.s);
1003         bopen(s);
1004
1005         // Methods
1006         for meth: @ast::method in anon_obj.methods {
1007             hardbreak_if_not_bol(s);
1008             maybe_print_comment(s, meth.span.lo);
1009             print_fn(s, meth.decl, meth.ident, meth.tps);
1010             word(s.s, " ");
1011             print_block(s, meth.body);
1012         }
1013
1014         // With object
1015         alt anon_obj.inner_obj {
1016           none. { }
1017           some(e) { space(s.s); word_space(s, "with"); print_expr(s, e); }
1018         }
1019         bclose(s, expr.span);
1020       }
1021     }
1022     s.ann.post(ann_node);
1023     end(s);
1024 }
1025
1026 fn print_expr_parens_if_not_bot(s: ps, ex: @ast::expr) {
1027     let parens = alt ex.node {
1028       ast::expr_fail(_) | ast::expr_ret(_) |
1029       ast::expr_binary(_, _, _) | ast::expr_unary(_, _) |
1030       ast::expr_ternary(_, _, _) | ast::expr_move(_, _) |
1031       ast::expr_copy(_) | ast::expr_assign(_, _) | ast::expr_be(_) |
1032       ast::expr_assign_op(_, _, _) | ast::expr_swap(_, _) |
1033       ast::expr_log(_, _, _) | ast::expr_assert(_) |
1034       ast::expr_call(_, _, true) |
1035       ast::expr_check(_, _) { true }
1036       _ { false }
1037     };
1038     if parens { popen(s); }
1039     print_expr(s, ex);
1040     if parens { pclose(s); }
1041 }
1042
1043 fn print_local_decl(s: ps, loc: @ast::local) {
1044     print_pat(s, loc.node.pat);
1045     alt loc.node.ty.node {
1046       ast::ty_infer. { }
1047       _ { word_space(s, ":"); print_type(s, loc.node.ty); }
1048     }
1049 }
1050
1051 fn print_decl(s: ps, decl: @ast::decl) {
1052     maybe_print_comment(s, decl.span.lo);
1053     alt decl.node {
1054       ast::decl_local(locs) {
1055         space_if_not_bol(s);
1056         ibox(s, indent_unit);
1057         word_nbsp(s, "let");
1058         fn print_local(s: ps, loc_st: (ast::let_style, @ast::local)) {
1059             let (st, loc) = loc_st;
1060             ibox(s, indent_unit);
1061             if st == ast::let_ref { word(s.s, "&"); }
1062             print_local_decl(s, loc);
1063             end(s);
1064             alt loc.node.init {
1065               some(init) {
1066                 nbsp(s);
1067                 alt init.op {
1068                   ast::init_assign. { word_space(s, "="); }
1069                   ast::init_move. { word_space(s, "<-"); }
1070                 }
1071                 print_expr(s, init.expr);
1072               }
1073               _ { }
1074             }
1075         }
1076         commasep(s, consistent, locs, print_local);
1077         end(s);
1078       }
1079       ast::decl_item(item) { print_item(s, item); }
1080     }
1081 }
1082
1083 fn print_ident(s: ps, ident: ast::ident) { word(s.s, ident); }
1084
1085 fn print_for_decl(s: ps, loc: @ast::local, coll: @ast::expr) {
1086     print_local_decl(s, loc);
1087     space(s.s);
1088     word_space(s, "in");
1089     print_expr(s, coll);
1090 }
1091
1092 fn print_path(s: ps, &&path: @ast::path, colons_before_params: bool) {
1093     maybe_print_comment(s, path.span.lo);
1094     if path.node.global { word(s.s, "::"); }
1095     let first = true;
1096     for id: ast::ident in path.node.idents {
1097         if first { first = false; } else { word(s.s, "::"); }
1098         word(s.s, id);
1099     }
1100     if vec::len(path.node.types) > 0u {
1101         if colons_before_params { word(s.s, "::"); }
1102         word(s.s, "<");
1103         commasep(s, inconsistent, path.node.types, print_type);
1104         word(s.s, ">");
1105     }
1106 }
1107
1108 fn print_pat(s: ps, &&pat: @ast::pat) {
1109     maybe_print_comment(s, pat.span.lo);
1110     let ann_node = node_pat(s, pat);
1111     s.ann.pre(ann_node);
1112     alt pat.node {
1113       ast::pat_wild. { word(s.s, "_"); }
1114       ast::pat_bind(id, sub) {
1115         word(s.s, id);
1116         alt sub {
1117           some(p) { word(s.s, "@"); print_pat(s, p); }
1118           _ {}
1119         }
1120       }
1121       ast::pat_tag(path, args) {
1122         print_path(s, path, true);
1123         if vec::len(args) > 0u {
1124             popen(s);
1125             commasep(s, inconsistent, args, print_pat);
1126             pclose(s);
1127         } else { word(s.s, "."); }
1128       }
1129       ast::pat_rec(fields, etc) {
1130         word(s.s, "{");
1131         fn print_field(s: ps, f: ast::field_pat) {
1132             cbox(s, indent_unit);
1133             word(s.s, f.ident);
1134             word_space(s, ":");
1135             print_pat(s, f.pat);
1136             end(s);
1137         }
1138         fn get_span(f: ast::field_pat) -> codemap::span { ret f.pat.span; }
1139         commasep_cmnt(s, consistent, fields, print_field, get_span);
1140         if etc {
1141             if vec::len(fields) != 0u { word_space(s, ","); }
1142             word(s.s, "_");
1143         }
1144         word(s.s, "}");
1145       }
1146       ast::pat_tup(elts) {
1147         popen(s);
1148         commasep(s, inconsistent, elts, print_pat);
1149         pclose(s);
1150       }
1151       ast::pat_box(inner) { word(s.s, "@"); print_pat(s, inner); }
1152       ast::pat_uniq(inner) { word(s.s, "~"); print_pat(s, inner); }
1153       ast::pat_lit(e) { print_expr(s, e); }
1154       ast::pat_range(begin, end) {
1155         print_expr(s, begin);
1156         space(s.s);
1157         word_space(s, "to");
1158         print_expr(s, end);
1159       }
1160     }
1161     s.ann.post(ann_node);
1162 }
1163
1164 fn print_fn(s: ps, decl: ast::fn_decl, name: ast::ident,
1165             typarams: [ast::ty_param]) {
1166     alt decl.purity {
1167       ast::impure_fn. { head(s, "fn"); }
1168       ast::unsafe_fn. { head(s, "unsafe fn"); }
1169       ast::pure_fn. { head(s, "pure fn"); }
1170     }
1171     word(s.s, name);
1172     print_type_params(s, typarams);
1173     print_fn_args_and_ret(s, decl);
1174 }
1175
1176 fn print_cap_clause(s: ps, cap_clause: ast::capture_clause) {
1177     fn print_cap_item(s: ps, &&cap_item: @ast::capture_item) {
1178         word(s.s, cap_item.name);
1179     }
1180
1181     let has_copies = vec::is_not_empty(cap_clause.copies);
1182     let has_moves = vec::is_not_empty(cap_clause.moves);
1183     if !has_copies && !has_moves { ret; }
1184
1185     word(s.s, "[");
1186
1187     if has_copies {
1188         word_nbsp(s, "copy");
1189         commasep(s, inconsistent, cap_clause.copies, print_cap_item);
1190         if has_moves {
1191             word_space(s, ";");
1192         }
1193     }
1194
1195     if has_moves {
1196         word_nbsp(s, "move");
1197         commasep(s, inconsistent, cap_clause.moves, print_cap_item);
1198     }
1199
1200     word(s.s, "]");
1201 }
1202
1203 fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl) {
1204     popen(s);
1205     fn print_arg(s: ps, x: ast::arg) {
1206         ibox(s, indent_unit);
1207         print_arg_mode(s, x.mode);
1208         word_space(s, x.ident + ":");
1209         print_type(s, x.ty);
1210         end(s);
1211     }
1212     commasep(s, inconsistent, decl.inputs, print_arg);
1213     pclose(s);
1214     word(s.s, ast_fn_constrs_str(decl, decl.constraints));
1215     maybe_print_comment(s, decl.output.span.lo);
1216     if decl.output.node != ast::ty_nil {
1217         space_if_not_bol(s);
1218         word_space(s, "->");
1219         print_type(s, decl.output);
1220     }
1221 }
1222
1223 fn print_fn_block_args(s: ps, decl: ast::fn_decl) {
1224     word(s.s, "|");
1225     fn print_arg(s: ps, x: ast::arg) {
1226         ibox(s, indent_unit);
1227         print_arg_mode(s, x.mode);
1228         word(s.s, x.ident);
1229         end(s);
1230     }
1231     commasep(s, inconsistent, decl.inputs, print_arg);
1232     word(s.s, "|");
1233     if decl.output.node != ast::ty_infer {
1234         space_if_not_bol(s);
1235         word_space(s, "->");
1236         print_type(s, decl.output);
1237     }
1238     maybe_print_comment(s, decl.output.span.lo);
1239 }
1240
1241 fn print_arg_mode(s: ps, m: ast::mode) {
1242     alt m {
1243       ast::by_mut_ref. { word(s.s, "&"); }
1244       ast::by_move. { word(s.s, "-"); }
1245       ast::by_ref. { word(s.s, "&&"); }
1246       ast::by_val. { word(s.s, "++"); }
1247       ast::by_copy. { word(s.s, "+"); }
1248       ast::mode_infer. {}
1249     }
1250 }
1251
1252 fn print_bounds(s: ps, bounds: @[ast::ty_param_bound]) {
1253     if vec::len(*bounds) > 0u {
1254         word(s.s, ":");
1255         for bound in *bounds {
1256             nbsp(s);
1257             alt bound {
1258               ast::bound_copy. { word(s.s, "copy"); }
1259               ast::bound_send. { word(s.s, "send"); }
1260               ast::bound_iface(t) { print_type(s, t); }
1261             }
1262         }
1263     }
1264 }
1265
1266 fn print_type_params(s: ps, params: [ast::ty_param]) {
1267     if vec::len(params) > 0u {
1268         word(s.s, "<");
1269         fn printParam(s: ps, param: ast::ty_param) {
1270             word(s.s, param.ident);
1271             print_bounds(s, param.bounds);
1272         }
1273         commasep(s, inconsistent, params, printParam);
1274         word(s.s, ">");
1275     }
1276 }
1277
1278 fn print_meta_item(s: ps, &&item: @ast::meta_item) {
1279     ibox(s, indent_unit);
1280     alt item.node {
1281       ast::meta_word(name) { word(s.s, name); }
1282       ast::meta_name_value(name, value) {
1283         word_space(s, name);
1284         word_space(s, "=");
1285         print_literal(s, @value);
1286       }
1287       ast::meta_list(name, items) {
1288         word(s.s, name);
1289         popen(s);
1290         commasep(s, consistent, items, print_meta_item);
1291         pclose(s);
1292       }
1293     }
1294     end(s);
1295 }
1296
1297 fn print_view_item(s: ps, item: @ast::view_item) {
1298     hardbreak_if_not_bol(s);
1299     maybe_print_comment(s, item.span.lo);
1300     alt item.node {
1301       ast::view_item_use(id, mta, _) {
1302         head(s, "use");
1303         word(s.s, id);
1304         if vec::len(mta) > 0u {
1305             popen(s);
1306             commasep(s, consistent, mta, print_meta_item);
1307             pclose(s);
1308         }
1309       }
1310       ast::view_item_import(id, ids, _) {
1311         head(s, "import");
1312         if !str::eq(id, ids[vec::len(*ids) - 1u]) {
1313             word_space(s, id);
1314             word_space(s, "=");
1315         }
1316         let first = true;
1317         for elt: ast::ident in *ids {
1318             if first { first = false; } else { word(s.s, "::"); }
1319             word(s.s, elt);
1320         }
1321       }
1322       ast::view_item_import_from(mod_path, idents, _) {
1323         head(s, "import");
1324         for elt: ast::ident in *mod_path { word(s.s, elt); word(s.s, "::"); }
1325         word(s.s, "{");
1326         commasep(s, inconsistent, idents,
1327                  fn (s: ps, w: ast::import_ident) { word(s.s, w.node.name) });
1328         word(s.s, "}");
1329       }
1330       ast::view_item_import_glob(ids, _) {
1331         head(s, "import");
1332         let first = true;
1333         for elt: ast::ident in *ids {
1334             if first { first = false; } else { word(s.s, "::"); }
1335             word(s.s, elt);
1336         }
1337         word(s.s, "::*");
1338       }
1339       ast::view_item_export(ids, _) {
1340         head(s, "export");
1341         commasep(s, inconsistent, ids,
1342                  fn (s: ps, &&w: ast::ident) { word(s.s, w) });
1343       }
1344     }
1345     word(s.s, ";");
1346     end(s); // end inner head-block
1347
1348     end(s); // end outer head-block
1349
1350 }
1351
1352
1353 // FIXME: The fact that this builds up the table anew for every call is
1354 // not good. Eventually, table should be a const.
1355 fn operator_prec(op: ast::binop) -> int {
1356     for spec: parse::parser::op_spec in *parse::parser::prec_table() {
1357         if spec.op == op { ret spec.prec; }
1358     }
1359     fail;
1360 }
1361
1362 fn need_parens(expr: @ast::expr, outer_prec: int) -> bool {
1363     alt expr.node {
1364       ast::expr_binary(op, _, _) { operator_prec(op) < outer_prec }
1365       ast::expr_cast(_, _) { parse::parser::as_prec < outer_prec }
1366       ast::expr_ternary(_, _, _) { parse::parser::ternary_prec < outer_prec }
1367       // This may be too conservative in some cases
1368       ast::expr_assign(_, _) { true }
1369       ast::expr_move(_, _) { true }
1370       ast::expr_swap(_, _) { true }
1371       ast::expr_assign_op(_, _, _) { true }
1372       ast::expr_ret(_) { true }
1373       ast::expr_be(_) { true }
1374       ast::expr_assert(_) { true }
1375       ast::expr_check(_, _) { true }
1376       ast::expr_log(_, _, _) { true }
1377       _ { !parse::parser::expr_requires_semi_to_be_stmt(expr) }
1378     }
1379 }
1380
1381 fn print_op_maybe_parens(s: ps, expr: @ast::expr, outer_prec: int) {
1382     let add_them = need_parens(expr, outer_prec);
1383     if add_them { popen(s); }
1384     print_expr(s, expr);
1385     if add_them { pclose(s); }
1386 }
1387
1388 fn print_mutability(s: ps, mut: ast::mutability) {
1389     alt mut {
1390       ast::mut. { word_nbsp(s, "mutable"); }
1391       ast::maybe_mut. { word_nbsp(s, "const"); }
1392       ast::imm. {/* nothing */ }
1393     }
1394 }
1395
1396 fn print_mt(s: ps, mt: ast::mt) {
1397     print_mutability(s, mt.mut);
1398     print_type(s, mt.ty);
1399 }
1400
1401 fn print_ty_fn(s: ps, proto: ast::proto,
1402                decl: ast::fn_decl, id: option::t<ast::ident>,
1403                tps: option::t<[ast::ty_param]>) {
1404     ibox(s, indent_unit);
1405     word(s.s, proto_to_str(proto));
1406     alt id { some(id) { word(s.s, " "); word(s.s, id); } _ { } }
1407     alt tps { some(tps) { print_type_params(s, tps); } _ { } }
1408     zerobreak(s.s);
1409     popen(s);
1410     fn print_arg(s: ps, input: ast::arg) {
1411         print_arg_mode(s, input.mode);
1412         if str::byte_len(input.ident) > 0u {
1413             word_space(s, input.ident + ":");
1414         }
1415         print_type(s, input.ty);
1416     }
1417     commasep(s, inconsistent, decl.inputs, print_arg);
1418     pclose(s);
1419     maybe_print_comment(s, decl.output.span.lo);
1420     if decl.output.node != ast::ty_nil {
1421         space_if_not_bol(s);
1422         ibox(s, indent_unit);
1423         word_space(s, "->");
1424         if decl.cf == ast::noreturn { word_nbsp(s, "!"); }
1425         else { print_type(s, decl.output); }
1426         end(s);
1427     }
1428     word(s.s, ast_ty_fn_constrs_str(decl.constraints));
1429     end(s);
1430 }
1431
1432 fn maybe_print_trailing_comment(s: ps, span: codemap::span,
1433                                 next_pos: option::t<uint>) {
1434     let cm;
1435     alt s.cm { some(ccm) { cm = ccm; } _ { ret; } }
1436     alt next_comment(s) {
1437       some(cmnt) {
1438         if cmnt.style != lexer::trailing { ret; }
1439         let span_line = codemap::lookup_char_pos(cm, span.hi);
1440         let comment_line = codemap::lookup_char_pos(cm, cmnt.pos);
1441         let next = cmnt.pos + 1u;
1442         alt next_pos { none. { } some(p) { next = p; } }
1443         if span.hi < cmnt.pos && cmnt.pos < next &&
1444                span_line.line == comment_line.line {
1445             print_comment(s, cmnt);
1446             s.cur_cmnt += 1u;
1447         }
1448       }
1449       _ { }
1450     }
1451 }
1452
1453 fn print_remaining_comments(s: ps) {
1454     // If there aren't any remaining comments, then we need to manually
1455     // make sure there is a line break at the end.
1456     if option::is_none(next_comment(s)) { hardbreak(s.s); }
1457     while true {
1458         alt next_comment(s) {
1459           some(cmnt) { print_comment(s, cmnt); s.cur_cmnt += 1u; }
1460           _ { break; }
1461         }
1462     }
1463 }
1464
1465 fn in_cbox(s: ps) -> bool {
1466     let len = vec::len(s.boxes);
1467     if len == 0u { ret false; }
1468     ret s.boxes[len - 1u] == pp::consistent;
1469 }
1470
1471 fn print_literal(s: ps, &&lit: @ast::lit) {
1472     maybe_print_comment(s, lit.span.lo);
1473     alt next_lit(s) {
1474       some(lt) {
1475         if lt.pos == lit.span.lo { word(s.s, lt.lit); s.cur_lit += 1u; ret; }
1476       }
1477       _ { }
1478     }
1479     alt lit.node {
1480       ast::lit_str(st) { print_string(s, st); }
1481       ast::lit_int(ch, ast::ty_char.) {
1482         word(s.s, "'" + escape_str(str::from_char(ch as char), '\'') + "'");
1483       }
1484       ast::lit_int(i, t) {
1485         word(s.s, int::str(i as int) + ast_util::int_ty_to_str(t));
1486       }
1487       ast::lit_uint(u, t) {
1488         word(s.s, uint::str(u as uint) + ast_util::uint_ty_to_str(t));
1489       }
1490       ast::lit_float(f, t) {
1491         word(s.s, f + ast_util::float_ty_to_str(t));
1492       }
1493       ast::lit_nil. { word(s.s, "()"); }
1494       ast::lit_bool(val) {
1495         if val { word(s.s, "true"); } else { word(s.s, "false"); }
1496       }
1497     }
1498 }
1499
1500 fn lit_to_str(l: @ast::lit) -> str { be to_str(l, print_literal); }
1501
1502 fn next_lit(s: ps) -> option::t<lexer::lit> {
1503     alt s.literals {
1504       some(lits) {
1505         if s.cur_lit < vec::len(lits) {
1506             ret some(lits[s.cur_lit]);
1507         } else { ret none::<lexer::lit>; }
1508       }
1509       _ { ret none::<lexer::lit>; }
1510     }
1511 }
1512
1513 fn maybe_print_comment(s: ps, pos: uint) {
1514     while true {
1515         alt next_comment(s) {
1516           some(cmnt) {
1517             if cmnt.pos < pos {
1518                 print_comment(s, cmnt);
1519                 s.cur_cmnt += 1u;
1520             } else { break; }
1521           }
1522           _ { break; }
1523         }
1524     }
1525 }
1526
1527 fn print_comment(s: ps, cmnt: lexer::cmnt) {
1528     alt cmnt.style {
1529       lexer::mixed. {
1530         assert (vec::len(cmnt.lines) == 1u);
1531         zerobreak(s.s);
1532         word(s.s, cmnt.lines[0]);
1533         zerobreak(s.s);
1534       }
1535       lexer::isolated. {
1536         pprust::hardbreak_if_not_bol(s);
1537         for line: str in cmnt.lines {
1538             // Don't print empty lines because they will end up as trailing
1539             // whitespace
1540             if str::is_not_empty(line) { word(s.s, line); }
1541             hardbreak(s.s);
1542         }
1543       }
1544       lexer::trailing. {
1545         word(s.s, " ");
1546         if vec::len(cmnt.lines) == 1u {
1547             word(s.s, cmnt.lines[0]);
1548             hardbreak(s.s);
1549         } else {
1550             ibox(s, 0u);
1551             for line: str in cmnt.lines {
1552                 if str::is_not_empty(line) { word(s.s, line); }
1553                 hardbreak(s.s);
1554             }
1555             end(s);
1556         }
1557       }
1558       lexer::blank_line. {
1559         // We need to do at least one, possibly two hardbreaks.
1560         let is_semi =
1561             alt s.s.last_token() {
1562               pp::STRING(s, _) { s == ";" }
1563               _ { false }
1564             };
1565         if is_semi || is_begin(s) || is_end(s) { hardbreak(s.s); }
1566         hardbreak(s.s);
1567       }
1568     }
1569 }
1570
1571 fn print_string(s: ps, st: str) {
1572     word(s.s, "\"");
1573     word(s.s, escape_str(st, '"'));
1574     word(s.s, "\"");
1575 }
1576
1577 fn escape_str(st: str, to_escape: char) -> str {
1578     let out: str = "";
1579     let len = str::byte_len(st);
1580     let i = 0u;
1581     while i < len {
1582         alt st[i] as char {
1583           '\n' { out += "\\n"; }
1584           '\t' { out += "\\t"; }
1585           '\r' { out += "\\r"; }
1586           '\\' { out += "\\\\"; }
1587           cur {
1588             if cur == to_escape { out += "\\"; }
1589             // FIXME some (or all?) non-ascii things should be escaped
1590
1591             str::push_char(out, cur);
1592           }
1593         }
1594         i += 1u;
1595     }
1596     ret out;
1597 }
1598
1599 fn to_str<T>(t: T, f: fn@(ps, T)) -> str {
1600     let writer = io::string_writer();
1601     let s = rust_printer(writer.get_writer());
1602     f(s, t);
1603     eof(s.s);
1604     ret writer.get_str();
1605 }
1606
1607 fn next_comment(s: ps) -> option::t<lexer::cmnt> {
1608     alt s.comments {
1609       some(cmnts) {
1610         if s.cur_cmnt < vec::len(cmnts) {
1611             ret some(cmnts[s.cur_cmnt]);
1612         } else { ret none::<lexer::cmnt>; }
1613       }
1614       _ { ret none::<lexer::cmnt>; }
1615     }
1616 }
1617
1618 // Removing the aliases from the type of f in the next two functions
1619 // triggers memory corruption, but I haven't isolated the bug yet. FIXME
1620 fn constr_args_to_str<T>(f: fn@(T) -> str, args: [@ast::sp_constr_arg<T>]) ->
1621    str {
1622     let comma = false;
1623     let s = "(";
1624     for a: @ast::sp_constr_arg<T> in args {
1625         if comma { s += ", "; } else { comma = true; }
1626         s += constr_arg_to_str::<T>(f, a.node);
1627     }
1628     s += ")";
1629     ret s;
1630 }
1631
1632 fn constr_arg_to_str<T>(f: fn@(T) -> str, c: ast::constr_arg_general_<T>) ->
1633    str {
1634     alt c {
1635       ast::carg_base. { ret "*"; }
1636       ast::carg_ident(i) { ret f(i); }
1637       ast::carg_lit(l) { ret lit_to_str(l); }
1638     }
1639 }
1640
1641 // needed b/c constr_args_to_str needs
1642 // something that takes an alias
1643 // (argh)
1644 fn uint_to_str(&&i: uint) -> str { ret uint::str(i); }
1645
1646 fn ast_ty_fn_constr_to_str(c: @ast::constr) -> str {
1647     ret path_to_str(c.node.path) +
1648             constr_args_to_str(uint_to_str, c.node.args);
1649 }
1650
1651 // FIXME: fix repeated code
1652 fn ast_ty_fn_constrs_str(constrs: [@ast::constr]) -> str {
1653     let s = "";
1654     let colon = true;
1655     for c: @ast::constr in constrs {
1656         if colon { s += " : "; colon = false; } else { s += ", "; }
1657         s += ast_ty_fn_constr_to_str(c);
1658     }
1659     ret s;
1660 }
1661
1662 fn fn_arg_idx_to_str(decl: ast::fn_decl, &&idx: uint) -> str {
1663     decl.inputs[idx].ident
1664 }
1665
1666 fn ast_fn_constr_to_str(decl: ast::fn_decl, c: @ast::constr) -> str {
1667     let arg_to_str = bind fn_arg_idx_to_str(decl, _);
1668     ret path_to_str(c.node.path) +
1669             constr_args_to_str(arg_to_str, c.node.args);
1670 }
1671
1672 // FIXME: fix repeated code
1673 fn ast_fn_constrs_str(decl: ast::fn_decl, constrs: [@ast::constr]) -> str {
1674     let s = "";
1675     let colon = true;
1676     for c: @ast::constr in constrs {
1677         if colon { s += " : "; colon = false; } else { s += ", "; }
1678         s += ast_fn_constr_to_str(decl, c);
1679     }
1680     ret s;
1681 }
1682
1683 fn proto_to_str(p: ast::proto) -> str {
1684     ret alt p {
1685           ast::proto_bare. { "fn" }
1686           ast::proto_block. { "block" }
1687           ast::proto_send. { "sendfn" }
1688           ast::proto_shared. { "fn@" }
1689         };
1690 }
1691
1692 fn ty_constr_to_str(c: @ast::ty_constr) -> str {
1693     fn ty_constr_path_to_str(&&p: @ast::path) -> str { "*." + path_to_str(p) }
1694
1695     ret path_to_str(c.node.path) +
1696             constr_args_to_str::<@ast::path>(ty_constr_path_to_str,
1697                                              c.node.args);
1698 }
1699
1700
1701 fn ast_ty_constrs_str(constrs: [@ast::ty_constr]) -> str {
1702     let s = "";
1703     let colon = true;
1704     for c: @ast::ty_constr in constrs {
1705         if colon { s += " : "; colon = false; } else { s += ", "; }
1706         s += ty_constr_to_str(c);
1707     }
1708     ret s;
1709 }
1710
1711 fn ends_in_lit_int(ex: @ast::expr) -> bool {
1712     alt ex.node {
1713       ast::expr_lit(@{node: ast::lit_int(_, ast::ty_i.), _}) { true }
1714       ast::expr_binary(_, _, sub) | ast::expr_unary(_, sub) |
1715       ast::expr_ternary(_, _, sub) | ast::expr_move(_, sub) |
1716       ast::expr_copy(sub) | ast::expr_assign(_, sub) | ast::expr_be(sub) |
1717       ast::expr_assign_op(_, _, sub) | ast::expr_swap(_, sub) |
1718       ast::expr_log(_, _, sub) | ast::expr_assert(sub) |
1719       ast::expr_check(_, sub) { ends_in_lit_int(sub) }
1720       ast::expr_fail(osub) | ast::expr_ret(osub) {
1721         alt osub {
1722           some(ex) { ends_in_lit_int(ex) }
1723           _ { false }
1724         }
1725       }
1726       _ { false }
1727     }
1728 }
1729
1730 //
1731 // Local Variables:
1732 // mode: rust
1733 // fill-column: 78;
1734 // indent-tabs-mode: nil
1735 // c-basic-offset: 4
1736 // buffer-file-coding-system: utf-8-unix
1737 // End:
1738 //