rust/src/libsyntax/ext/fmt.rs

288 lines
11 KiB
Rust
Raw Normal View History

/*
* The compiler code necessary to support the #fmt extension. Eventually this
* should all get sucked into either the standard library extfmt module or the
* compiler syntax extension plugin interface.
*/
2012-09-04 13:37:29 -05:00
use extfmt::ct::*;
use base::*;
use codemap::span;
use ext::build::*;
export expand_syntax_ext;
fn expand_syntax_ext(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
_body: ast::mac_body) -> @ast::expr {
let args = get_mac_args_no_max(cx, sp, arg, 1u, ~"fmt");
let fmt =
expr_to_str(cx, args[0],
~"first argument to #fmt must be a string literal.");
let fmtspan = args[0].span;
2012-08-22 19:24:52 -05:00
debug!("Format string:");
log(debug, fmt);
fn parse_fmt_err_(cx: ext_ctxt, sp: span, msg: ~str) -> ! {
cx.span_fatal(sp, msg);
}
let parse_fmt_err = fn@(s: ~str) -> ! {
2012-06-19 21:34:01 -05:00
parse_fmt_err_(cx, fmtspan, s)
};
let pieces = parse_fmt_string(fmt, parse_fmt_err);
2012-08-01 19:30:05 -05:00
return pieces_to_expr(cx, sp, pieces, args);
}
// FIXME (#2249): A lot of these functions for producing expressions can
// probably be factored out in common with other code that builds
// expressions. Also: Cleanup the naming of these functions.
// NOTE: Moved many of the common ones to build.rs --kevina
fn pieces_to_expr(cx: ext_ctxt, sp: span,
2012-09-11 21:37:29 -05:00
pieces: ~[Piece], args: ~[@ast::expr])
-> @ast::expr {
2012-07-18 18:18:02 -05:00
fn make_path_vec(_cx: ext_ctxt, ident: @~str) -> ~[ast::ident] {
let intr = _cx.parse_sess().interner;
return ~[intr.intern(@~"extfmt"), intr.intern(@~"rt2"),
2012-07-18 18:18:02 -05:00
intr.intern(ident)];
}
2012-07-18 18:18:02 -05:00
fn make_rt_path_expr(cx: ext_ctxt, sp: span, nm: @~str) -> @ast::expr {
let path = make_path_vec(cx, nm);
2012-08-01 19:30:05 -05:00
return mk_path(cx, sp, path);
}
// Produces an AST expression that represents a RT::conv record,
// which tells the RT::conv* functions how to perform the conversion
2012-09-11 21:37:29 -05:00
fn make_rt_conv_expr(cx: ext_ctxt, sp: span, cnv: Conv) -> @ast::expr {
fn make_flags(cx: ext_ctxt, sp: span, flags: ~[Flag]) -> @ast::expr {
let mut tmp_expr = make_rt_path_expr(cx, sp, @~"flag_none");
2012-06-30 18:19:07 -05:00
for flags.each |f| {
2012-08-06 14:34:08 -05:00
let fstr = match f {
2012-09-11 21:37:29 -05:00
FlagLeftJustify => ~"flag_left_justify",
FlagLeftZeroPad => ~"flag_left_zero_pad",
FlagSpaceForSign => ~"flag_space_for_sign",
FlagSignAlways => ~"flag_sign_always",
FlagAlternate => ~"flag_alternate"
};
tmp_expr = mk_binary(cx, sp, ast::bitor, tmp_expr,
make_rt_path_expr(cx, sp, @fstr));
}
2012-08-01 19:30:05 -05:00
return tmp_expr;
}
2012-09-11 21:37:29 -05:00
fn make_count(cx: ext_ctxt, sp: span, cnt: Count) -> @ast::expr {
2012-08-06 14:34:08 -05:00
match cnt {
2012-09-11 21:37:29 -05:00
CountImplied => {
return make_rt_path_expr(cx, sp, @~"CountImplied");
}
2012-09-11 21:37:29 -05:00
CountIs(c) => {
let count_lit = mk_int(cx, sp, c);
2012-09-11 21:37:29 -05:00
let count_is_path = make_path_vec(cx, @~"CountIs");
let count_is_args = ~[count_lit];
2012-08-01 19:30:05 -05:00
return mk_call(cx, sp, count_is_path, count_is_args);
}
2012-08-03 21:59:04 -05:00
_ => cx.span_unimpl(sp, ~"unimplemented #fmt conversion")
}
}
2012-09-11 21:37:29 -05:00
fn make_ty(cx: ext_ctxt, sp: span, t: Ty) -> @ast::expr {
let mut rt_type;
2012-08-06 14:34:08 -05:00
match t {
2012-09-11 21:37:29 -05:00
TyHex(c) => match c {
CaseUpper => rt_type = ~"TyHexUpper",
CaseLower => rt_type = ~"TyHexLower"
},
2012-09-11 21:37:29 -05:00
TyBits => rt_type = ~"TyBits",
TyOctal => rt_type = ~"TyOctal",
_ => rt_type = ~"TyDefault"
}
2012-08-01 19:30:05 -05:00
return make_rt_path_expr(cx, sp, @rt_type);
}
fn make_conv_rec(cx: ext_ctxt, sp: span, flags_expr: @ast::expr,
width_expr: @ast::expr, precision_expr: @ast::expr,
ty_expr: @ast::expr) -> @ast::expr {
2012-07-18 18:18:02 -05:00
let intr = cx.parse_sess().interner;
2012-08-01 19:30:05 -05:00
return mk_rec_e(cx, sp,
2012-07-18 18:18:02 -05:00
~[{ident: intr.intern(@~"flags"), ex: flags_expr},
{ident: intr.intern(@~"width"), ex: width_expr},
{ident: intr.intern(@~"precision"),
ex: precision_expr},
{ident: intr.intern(@~"ty"), ex: ty_expr}]);
}
let rt_conv_flags = make_flags(cx, sp, cnv.flags);
let rt_conv_width = make_count(cx, sp, cnv.width);
let rt_conv_precision = make_count(cx, sp, cnv.precision);
let rt_conv_ty = make_ty(cx, sp, cnv.ty);
2012-08-01 19:30:05 -05:00
return make_conv_rec(cx, sp, rt_conv_flags, rt_conv_width,
rt_conv_precision, rt_conv_ty);
}
2012-09-11 21:37:29 -05:00
fn make_conv_call(cx: ext_ctxt, sp: span, conv_type: ~str, cnv: Conv,
2011-09-02 17:34:58 -05:00
arg: @ast::expr) -> @ast::expr {
let fname = ~"conv_" + conv_type;
2012-06-10 02:49:59 -05:00
let path = make_path_vec(cx, @fname);
let cnv_expr = make_rt_conv_expr(cx, sp, cnv);
let args = ~[cnv_expr, arg];
2012-08-01 19:30:05 -05:00
return mk_call(cx, arg.span, path, args);
}
2012-09-11 21:37:29 -05:00
fn make_new_conv(cx: ext_ctxt, sp: span, cnv: Conv, arg: @ast::expr) ->
@ast::expr {
2012-04-19 17:20:11 -05:00
// FIXME: Move validation code into core::extfmt (Issue #2249)
2012-09-11 21:37:29 -05:00
fn is_signed_type(cnv: Conv) -> bool {
2012-08-06 14:34:08 -05:00
match cnv.ty {
2012-09-11 21:37:29 -05:00
TyInt(s) => match s {
Signed => return true,
Unsigned => return false
},
2012-09-11 21:37:29 -05:00
TyFloat => return true,
2012-08-03 21:59:04 -05:00
_ => return false
}
}
let unsupported = ~"conversion not supported in #fmt string";
2012-08-06 14:34:08 -05:00
match cnv.param {
2012-08-20 14:23:37 -05:00
option::None => (),
2012-08-03 21:59:04 -05:00
_ => cx.span_unimpl(sp, unsupported)
}
2012-06-30 18:19:07 -05:00
for cnv.flags.each |f| {
2012-08-06 14:34:08 -05:00
match f {
2012-09-11 21:37:29 -05:00
FlagLeftJustify => (),
FlagSignAlways => {
if !is_signed_type(cnv) {
cx.span_fatal(sp,
~"+ flag only valid in " +
~"signed #fmt conversion");
}
}
2012-09-11 21:37:29 -05:00
FlagSpaceForSign => {
if !is_signed_type(cnv) {
cx.span_fatal(sp,
~"space flag only valid in " +
~"signed #fmt conversions");
}
}
2012-09-11 21:37:29 -05:00
FlagLeftZeroPad => (),
2012-08-03 21:59:04 -05:00
_ => cx.span_unimpl(sp, unsupported)
}
}
2012-08-06 14:34:08 -05:00
match cnv.width {
2012-09-11 21:37:29 -05:00
CountImplied => (),
CountIs(_) => (),
2012-08-03 21:59:04 -05:00
_ => cx.span_unimpl(sp, unsupported)
}
2012-08-06 14:34:08 -05:00
match cnv.precision {
2012-09-11 21:37:29 -05:00
CountImplied => (),
CountIs(_) => (),
2012-08-03 21:59:04 -05:00
_ => cx.span_unimpl(sp, unsupported)
}
2012-08-06 14:34:08 -05:00
match cnv.ty {
2012-09-11 21:37:29 -05:00
TyStr => return make_conv_call(cx, arg.span, ~"str", cnv, arg),
TyInt(sign) => match sign {
Signed => return make_conv_call(cx, arg.span, ~"int", cnv, arg),
Unsigned => {
2012-08-03 21:59:04 -05:00
return make_conv_call(cx, arg.span, ~"uint", cnv, arg)
}
},
2012-09-11 21:37:29 -05:00
TyBool => return make_conv_call(cx, arg.span, ~"bool", cnv, arg),
TyChar => return make_conv_call(cx, arg.span, ~"char", cnv, arg),
TyHex(_) => {
2012-08-01 19:30:05 -05:00
return make_conv_call(cx, arg.span, ~"uint", cnv, arg);
}
2012-09-11 21:37:29 -05:00
TyBits => return make_conv_call(cx, arg.span, ~"uint", cnv, arg),
TyOctal => return make_conv_call(cx, arg.span, ~"uint", cnv, arg),
TyFloat => {
2012-08-01 19:30:05 -05:00
return make_conv_call(cx, arg.span, ~"float", cnv, arg);
}
2012-09-11 21:37:29 -05:00
TyPoly => return make_conv_call(cx, arg.span, ~"poly", cnv, arg)
}
}
2012-09-11 21:37:29 -05:00
fn log_conv(c: Conv) {
2012-08-06 14:34:08 -05:00
match c.param {
2012-08-20 14:23:37 -05:00
Some(p) => { log(debug, ~"param: " + int::to_str(p, 10u)); }
2012-08-22 19:24:52 -05:00
_ => debug!("param: none")
}
2012-06-30 18:19:07 -05:00
for c.flags.each |f| {
2012-08-06 14:34:08 -05:00
match f {
2012-09-11 21:37:29 -05:00
FlagLeftJustify => debug!("flag: left justify"),
FlagLeftZeroPad => debug!("flag: left zero pad"),
FlagSpaceForSign => debug!("flag: left space pad"),
FlagSignAlways => debug!("flag: sign always"),
FlagAlternate => debug!("flag: alternate")
}
}
2012-08-06 14:34:08 -05:00
match c.width {
2012-09-11 21:37:29 -05:00
CountIs(i) => log(
2012-08-03 21:59:04 -05:00
debug, ~"width: count is " + int::to_str(i, 10u)),
2012-09-11 21:37:29 -05:00
CountIsParam(i) => log(
2012-08-03 21:59:04 -05:00
debug, ~"width: count is param " + int::to_str(i, 10u)),
2012-09-11 21:37:29 -05:00
CountIsNextParam => debug!("width: count is next param"),
CountImplied => debug!("width: count is implied")
}
2012-08-06 14:34:08 -05:00
match c.precision {
2012-09-11 21:37:29 -05:00
CountIs(i) => log(
2012-08-03 21:59:04 -05:00
debug, ~"prec: count is " + int::to_str(i, 10u)),
2012-09-11 21:37:29 -05:00
CountIsParam(i) => log(
2012-08-03 21:59:04 -05:00
debug, ~"prec: count is param " + int::to_str(i, 10u)),
2012-09-11 21:37:29 -05:00
CountIsNextParam => debug!("prec: count is next param"),
CountImplied => debug!("prec: count is implied")
}
2012-08-06 14:34:08 -05:00
match c.ty {
2012-09-11 21:37:29 -05:00
TyBool => debug!("type: bool"),
TyStr => debug!("type: str"),
TyChar => debug!("type: char"),
TyInt(s) => match s {
Signed => debug!("type: signed"),
Unsigned => debug!("type: unsigned")
},
2012-09-11 21:37:29 -05:00
TyBits => debug!("type: bits"),
TyHex(cs) => match cs {
CaseUpper => debug!("type: uhex"),
CaseLower => debug!("type: lhex"),
},
2012-09-11 21:37:29 -05:00
TyOctal => debug!("type: octal"),
TyFloat => debug!("type: float"),
TyPoly => debug!("type: poly")
}
}
let fmt_sp = args[0].span;
let mut n = 0u;
let mut piece_exprs = ~[];
let nargs = args.len();
2012-06-30 18:19:07 -05:00
for pieces.each |pc| {
2012-08-06 14:34:08 -05:00
match pc {
2012-09-11 21:37:29 -05:00
PieceString(s) => {
2012-08-03 21:59:04 -05:00
vec::push(piece_exprs, mk_uniq_str(cx, fmt_sp, s))
}
2012-09-11 21:37:29 -05:00
PieceConv(conv) => {
n += 1u;
if n >= nargs {
cx.span_fatal(sp,
~"not enough arguments to #fmt " +
~"for the given format string");
}
2012-08-22 19:24:52 -05:00
debug!("Building conversion:");
log_conv(conv);
let arg_expr = args[n];
let c_expr = make_new_conv(cx, fmt_sp, conv, arg_expr);
vec::push(piece_exprs, c_expr);
}
}
}
let expected_nargs = n + 1u; // n conversions + the fmt string
if expected_nargs < nargs {
2011-09-12 05:39:38 -05:00
cx.span_fatal
2012-08-22 19:24:52 -05:00
(sp, fmt!("too many arguments to #fmt. found %u, expected %u",
nargs, expected_nargs));
}
let arg_vec = mk_fixed_vec_e(cx, fmt_sp, piece_exprs);
2012-07-18 18:18:02 -05:00
return mk_call(cx, fmt_sp,
~[cx.parse_sess().interner.intern(@~"str"),
cx.parse_sess().interner.intern(@~"concat")],
~[arg_vec]);
}
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//