]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/fmt.rs
Remove 'Local Variable' comments
[rust.git] / src / libsyntax / ext / fmt.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 /*
12  * The compiler code necessary to support the fmt! extension. Eventually this
13  * should all get sucked into either the standard library extfmt module or the
14  * compiler syntax extension plugin interface.
15  */
16
17 use ast;
18 use codemap::span;
19 use ext::base::*;
20 use ext::base;
21 use ext::build;
22 use ext::build::*;
23
24 use core::unstable::extfmt::ct::*;
25
26 pub fn expand_syntax_ext(cx: @ext_ctxt, sp: span, tts: &[ast::token_tree])
27     -> base::MacResult {
28     let args = get_exprs_from_tts(cx, tts);
29     if args.len() == 0 {
30         cx.span_fatal(sp, "fmt! takes at least 1 argument.");
31     }
32     let fmt =
33         expr_to_str(cx, args[0],
34                     ~"first argument to fmt! must be a string literal.");
35     let fmtspan = args[0].span;
36     debug!("Format string: %s", fmt);
37     fn parse_fmt_err_(cx: @ext_ctxt, sp: span, msg: &str) -> ! {
38         cx.span_fatal(sp, msg);
39     }
40     let parse_fmt_err: @fn(&str) -> ! = |s| parse_fmt_err_(cx, fmtspan, s);
41     let pieces = parse_fmt_string(fmt, parse_fmt_err);
42     MRExpr(pieces_to_expr(cx, sp, pieces, args))
43 }
44
45 // FIXME (#2249): A lot of these functions for producing expressions can
46 // probably be factored out in common with other code that builds
47 // expressions.  Also: Cleanup the naming of these functions.
48 // Note: Moved many of the common ones to build.rs --kevina
49 fn pieces_to_expr(cx: @ext_ctxt, sp: span,
50                   pieces: ~[Piece], args: ~[@ast::expr])
51    -> @ast::expr {
52     fn make_path_vec(cx: @ext_ctxt, ident: @~str) -> ~[ast::ident] {
53         let intr = cx.parse_sess().interner;
54         return ~[intr.intern(@~"unstable"), intr.intern(@~"extfmt"),
55                  intr.intern(@~"rt"), intr.intern(ident)];
56     }
57     fn make_rt_path_expr(cx: @ext_ctxt, sp: span, nm: @~str) -> @ast::expr {
58         let path = make_path_vec(cx, nm);
59         return mk_path_global(cx, sp, path);
60     }
61     // Produces an AST expression that represents a RT::conv record,
62     // which tells the RT::conv* functions how to perform the conversion
63
64     fn make_rt_conv_expr(cx: @ext_ctxt, sp: span, cnv: &Conv) -> @ast::expr {
65         fn make_flags(cx: @ext_ctxt, sp: span, flags: ~[Flag]) -> @ast::expr {
66             let mut tmp_expr = make_rt_path_expr(cx, sp, @~"flag_none");
67             for flags.each |f| {
68                 let fstr = match *f {
69                   FlagLeftJustify => ~"flag_left_justify",
70                   FlagLeftZeroPad => ~"flag_left_zero_pad",
71                   FlagSpaceForSign => ~"flag_space_for_sign",
72                   FlagSignAlways => ~"flag_sign_always",
73                   FlagAlternate => ~"flag_alternate"
74                 };
75                 tmp_expr = mk_binary(cx, sp, ast::bitor, tmp_expr,
76                                      make_rt_path_expr(cx, sp, @fstr));
77             }
78             return tmp_expr;
79         }
80         fn make_count(cx: @ext_ctxt, sp: span, cnt: Count) -> @ast::expr {
81             match cnt {
82               CountImplied => {
83                 return make_rt_path_expr(cx, sp, @~"CountImplied");
84               }
85               CountIs(c) => {
86                 let count_lit = mk_uint(cx, sp, c as uint);
87                 let count_is_path = make_path_vec(cx, @~"CountIs");
88                 let count_is_args = ~[count_lit];
89                 return mk_call_global(cx, sp, count_is_path, count_is_args);
90               }
91               _ => cx.span_unimpl(sp, ~"unimplemented fmt! conversion")
92             }
93         }
94         fn make_ty(cx: @ext_ctxt, sp: span, t: Ty) -> @ast::expr {
95             let rt_type;
96             match t {
97               TyHex(c) => match c {
98                 CaseUpper => rt_type = ~"TyHexUpper",
99                 CaseLower => rt_type = ~"TyHexLower"
100               },
101               TyBits => rt_type = ~"TyBits",
102               TyOctal => rt_type = ~"TyOctal",
103               _ => rt_type = ~"TyDefault"
104             }
105             return make_rt_path_expr(cx, sp, @rt_type);
106         }
107         fn make_conv_struct(cx: @ext_ctxt, sp: span, flags_expr: @ast::expr,
108                          width_expr: @ast::expr, precision_expr: @ast::expr,
109                          ty_expr: @ast::expr) -> @ast::expr {
110             let intr = cx.parse_sess().interner;
111             mk_global_struct_e(
112                 cx,
113                 sp,
114                 make_path_vec(cx, @~"Conv"),
115                 ~[
116                     build::Field {
117                         ident: intr.intern(@~"flags"), ex: flags_expr
118                     },
119                     build::Field {
120                         ident: intr.intern(@~"width"), ex: width_expr
121                     },
122                     build::Field {
123                         ident: intr.intern(@~"precision"), ex: precision_expr
124                     },
125                     build::Field {
126                         ident: intr.intern(@~"ty"), ex: ty_expr
127                     },
128                 ]
129             )
130         }
131         let rt_conv_flags = make_flags(cx, sp, cnv.flags);
132         let rt_conv_width = make_count(cx, sp, cnv.width);
133         let rt_conv_precision = make_count(cx, sp, cnv.precision);
134         let rt_conv_ty = make_ty(cx, sp, cnv.ty);
135         make_conv_struct(cx, sp, rt_conv_flags, rt_conv_width,
136                          rt_conv_precision, rt_conv_ty)
137     }
138     fn make_conv_call(cx: @ext_ctxt, sp: span, conv_type: &str, cnv: &Conv,
139                       arg: @ast::expr, buf: @ast::expr) -> @ast::expr {
140         let fname = ~"conv_" + conv_type;
141         let path = make_path_vec(cx, @fname);
142         let cnv_expr = make_rt_conv_expr(cx, sp, cnv);
143         let args = ~[cnv_expr, arg, buf];
144         return mk_call_global(cx, arg.span, path, args);
145     }
146
147     fn make_new_conv(cx: @ext_ctxt, sp: span, cnv: &Conv,
148                      arg: @ast::expr, buf: @ast::expr) -> @ast::expr {
149         fn is_signed_type(cnv: &Conv) -> bool {
150             match cnv.ty {
151               TyInt(s) => match s {
152                 Signed => return true,
153                 Unsigned => return false
154               },
155               TyFloat => return true,
156               _ => return false
157             }
158         }
159         let unsupported = ~"conversion not supported in fmt! string";
160         match cnv.param {
161           option::None => (),
162           _ => cx.span_unimpl(sp, unsupported)
163         }
164         for cnv.flags.each |f| {
165             match *f {
166               FlagLeftJustify => (),
167               FlagSignAlways => {
168                 if !is_signed_type(cnv) {
169                     cx.span_fatal(sp,
170                                   ~"+ flag only valid in " +
171                                       ~"signed fmt! conversion");
172                 }
173               }
174               FlagSpaceForSign => {
175                 if !is_signed_type(cnv) {
176                     cx.span_fatal(sp,
177                                   ~"space flag only valid in " +
178                                       ~"signed fmt! conversions");
179                 }
180               }
181               FlagLeftZeroPad => (),
182               _ => cx.span_unimpl(sp, unsupported)
183             }
184         }
185         match cnv.width {
186           CountImplied => (),
187           CountIs(_) => (),
188           _ => cx.span_unimpl(sp, unsupported)
189         }
190         match cnv.precision {
191           CountImplied => (),
192           CountIs(_) => (),
193           _ => cx.span_unimpl(sp, unsupported)
194         }
195         let (name, actual_arg) = match cnv.ty {
196             TyStr => ("str", arg),
197             TyInt(Signed) => ("int", arg),
198             TyBool => ("bool", arg),
199             TyChar => ("char", arg),
200             TyBits | TyOctal | TyHex(_) | TyInt(Unsigned) => ("uint", arg),
201             TyFloat => ("float", arg),
202             TyPoly => ("poly", mk_addr_of(cx, sp, arg))
203         };
204         return make_conv_call(cx, arg.span, name, cnv, actual_arg,
205                               mk_mut_addr_of(cx, arg.span, buf));
206     }
207     fn log_conv(c: &Conv) {
208         debug!("Building conversion:");
209         match c.param {
210           Some(p) => { debug!("param: %s", p.to_str()); }
211           _ => debug!("param: none")
212         }
213         for c.flags.each |f| {
214             match *f {
215               FlagLeftJustify => debug!("flag: left justify"),
216               FlagLeftZeroPad => debug!("flag: left zero pad"),
217               FlagSpaceForSign => debug!("flag: left space pad"),
218               FlagSignAlways => debug!("flag: sign always"),
219               FlagAlternate => debug!("flag: alternate")
220             }
221         }
222         match c.width {
223           CountIs(i) =>
224               debug!("width: count is %s", i.to_str()),
225           CountIsParam(i) =>
226               debug!("width: count is param %s", i.to_str()),
227           CountIsNextParam => debug!("width: count is next param"),
228           CountImplied => debug!("width: count is implied")
229         }
230         match c.precision {
231           CountIs(i) =>
232               debug!("prec: count is %s", i.to_str()),
233           CountIsParam(i) =>
234               debug!("prec: count is param %s", i.to_str()),
235           CountIsNextParam => debug!("prec: count is next param"),
236           CountImplied => debug!("prec: count is implied")
237         }
238         match c.ty {
239           TyBool => debug!("type: bool"),
240           TyStr => debug!("type: str"),
241           TyChar => debug!("type: char"),
242           TyInt(s) => match s {
243             Signed => debug!("type: signed"),
244             Unsigned => debug!("type: unsigned")
245           },
246           TyBits => debug!("type: bits"),
247           TyHex(cs) => match cs {
248             CaseUpper => debug!("type: uhex"),
249             CaseLower => debug!("type: lhex"),
250           },
251           TyOctal => debug!("type: octal"),
252           TyFloat => debug!("type: float"),
253           TyPoly => debug!("type: poly")
254         }
255     }
256
257     let fmt_sp = args[0].span;
258     let mut n = 0u;
259     let nargs = args.len();
260
261     /* 'ident' is the local buffer building up the result of fmt! */
262     let ident = cx.parse_sess().interner.intern(@~"__fmtbuf");
263     let buf = || mk_path(cx, fmt_sp, ~[ident]);
264     let str_ident = cx.parse_sess().interner.intern(@~"str");
265     let push_ident = cx.parse_sess().interner.intern(@~"push_str");
266     let mut stms = ~[];
267
268     /* Translate each piece (portion of the fmt expression) by invoking the
269        corresponding function in core::unstable::extfmt. Each function takes a
270        buffer to insert data into along with the data being formatted. */
271     let npieces = pieces.len();
272     do vec::consume(pieces) |i, pc| {
273         match pc {
274             /* Raw strings get appended via str::push_str */
275             PieceString(s) => {
276                 let portion = mk_uniq_str(cx, fmt_sp, s);
277
278                 /* If this is the first portion, then initialize the local
279                    buffer with it directly. If it's actually the only piece,
280                    then there's no need for it to be mutable */
281                 if i == 0 {
282                     stms.push(mk_local(cx, fmt_sp, npieces > 1, ident, portion));
283                 } else {
284                     let args = ~[mk_mut_addr_of(cx, fmt_sp, buf()), portion];
285                     let call = mk_call_global(cx,
286                                               fmt_sp,
287                                               ~[str_ident, push_ident],
288                                               args);
289                     stms.push(mk_stmt(cx, fmt_sp, call));
290                 }
291             }
292
293             /* Invoke the correct conv function in extfmt */
294             PieceConv(ref conv) => {
295                 n += 1u;
296                 if n >= nargs {
297                     cx.span_fatal(sp,
298                                   ~"not enough arguments to fmt! " +
299                                   ~"for the given format string");
300                 }
301
302                 log_conv(conv);
303                 /* If the first portion is a conversion, then the local buffer
304                    must be initialized as an empty string */
305                 if i == 0 {
306                     stms.push(mk_local(cx, fmt_sp, true, ident,
307                                        mk_uniq_str(cx, fmt_sp, ~"")));
308                 }
309                 stms.push(mk_stmt(cx, fmt_sp,
310                                   make_new_conv(cx, fmt_sp, conv,
311                                                 args[n], buf())));
312             }
313         }
314     }
315
316     let expected_nargs = n + 1u; // n conversions + the fmt string
317     if expected_nargs < nargs {
318         cx.span_fatal
319             (sp, fmt!("too many arguments to fmt!. found %u, expected %u",
320                            nargs, expected_nargs));
321     }
322
323     return mk_block(cx, fmt_sp, ~[], stms, Some(buf()));
324 }