2011-08-27 23:27:39 -05:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
2011-09-01 19:49:29 -05:00
|
|
|
* The compiler code necessary to support the #fmt extension. Eventually this
|
2011-08-27 23:27:39 -05:00
|
|
|
* should all get sucked into either the standard library extfmt module or the
|
|
|
|
* compiler syntax extension plugin interface.
|
|
|
|
*/
|
2011-12-13 18:25:51 -06:00
|
|
|
import extfmt::ct::*;
|
2011-08-27 23:27:39 -05:00
|
|
|
import base::*;
|
|
|
|
import codemap::span;
|
2012-01-25 15:47:56 -06:00
|
|
|
import syntax::ext::build::*;
|
2011-08-27 23:27:39 -05:00
|
|
|
export expand_syntax_ext;
|
|
|
|
|
2012-02-01 00:50:12 -06:00
|
|
|
fn expand_syntax_ext(cx: ext_ctxt, sp: span, arg: ast::mac_arg,
|
2012-01-31 21:30:26 -06:00
|
|
|
_body: ast::mac_body) -> @ast::expr {
|
2012-02-01 01:20:31 -06:00
|
|
|
let arg = get_mac_arg(cx,sp,arg);
|
2011-08-27 23:27:39 -05:00
|
|
|
let args: [@ast::expr] =
|
|
|
|
alt arg.node {
|
|
|
|
ast::expr_vec(elts, _) { elts }
|
|
|
|
_ {
|
2011-09-02 17:34:58 -05:00
|
|
|
cx.span_fatal(sp, "#fmt requires arguments of the form `[...]`.")
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
if vec::len::<@ast::expr>(args) == 0u {
|
2011-09-02 17:34:58 -05:00
|
|
|
cx.span_fatal(sp, "#fmt requires a format string");
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
let fmt =
|
|
|
|
expr_to_str(cx, args[0],
|
2011-12-26 21:20:26 -06:00
|
|
|
"first argument to #fmt must be a string literal.");
|
2011-08-27 23:27:39 -05:00
|
|
|
let fmtspan = args[0].span;
|
2011-12-22 16:42:52 -06:00
|
|
|
#debug("Format string:");
|
2011-12-22 19:53:53 -06:00
|
|
|
log(debug, fmt);
|
2011-09-12 04:27:30 -05:00
|
|
|
fn parse_fmt_err_(cx: ext_ctxt, sp: span, msg: str) -> ! {
|
2011-08-27 23:27:39 -05:00
|
|
|
cx.span_fatal(sp, msg);
|
|
|
|
}
|
|
|
|
let parse_fmt_err = bind parse_fmt_err_(cx, fmtspan, _);
|
|
|
|
let pieces = parse_fmt_string(fmt, parse_fmt_err);
|
|
|
|
ret pieces_to_expr(cx, sp, pieces, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: A lot of these functions for producing expressions can probably
|
|
|
|
// be factored out in common with other code that builds expressions.
|
|
|
|
// FIXME: Cleanup the naming of these functions
|
2012-01-25 15:47:56 -06:00
|
|
|
// NOTE: Moved many of the common ones to build.rs --kevina
|
2011-09-12 04:27:30 -05:00
|
|
|
fn pieces_to_expr(cx: ext_ctxt, sp: span, pieces: [piece], args: [@ast::expr])
|
|
|
|
-> @ast::expr {
|
2011-12-15 18:34:19 -06:00
|
|
|
fn make_path_vec(_cx: ext_ctxt, ident: ast::ident) -> [ast::ident] {
|
|
|
|
ret ["extfmt", "rt", ident];
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2011-09-12 04:27:30 -05:00
|
|
|
fn make_rt_path_expr(cx: ext_ctxt, sp: span, ident: str) -> @ast::expr {
|
2011-08-27 23:27:39 -05:00
|
|
|
let path = make_path_vec(cx, ident);
|
2012-01-26 17:34:05 -06:00
|
|
|
ret mk_path(cx, sp, path);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
// Produces an AST expression that represents a RT::conv record,
|
|
|
|
// which tells the RT::conv* functions how to perform the conversion
|
|
|
|
|
2011-09-12 04:27:30 -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 {
|
2011-08-27 23:27:39 -05:00
|
|
|
let flagexprs: [@ast::expr] = [];
|
|
|
|
for f: flag in flags {
|
|
|
|
let fstr;
|
|
|
|
alt f {
|
2012-01-19 00:37:22 -06:00
|
|
|
flag_left_justify { fstr = "flag_left_justify"; }
|
|
|
|
flag_left_zero_pad { fstr = "flag_left_zero_pad"; }
|
|
|
|
flag_space_for_sign { fstr = "flag_space_for_sign"; }
|
|
|
|
flag_sign_always { fstr = "flag_sign_always"; }
|
|
|
|
flag_alternate { fstr = "flag_alternate"; }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
flagexprs += [make_rt_path_expr(cx, sp, fstr)];
|
|
|
|
}
|
2012-01-26 17:34:05 -06:00
|
|
|
ret mk_vec_e(cx, sp, flagexprs);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2011-09-12 04:27:30 -05:00
|
|
|
fn make_count(cx: ext_ctxt, sp: span, cnt: count) -> @ast::expr {
|
2011-08-27 23:27:39 -05:00
|
|
|
alt cnt {
|
2012-01-19 00:37:22 -06:00
|
|
|
count_implied {
|
2011-09-02 17:34:58 -05:00
|
|
|
ret make_rt_path_expr(cx, sp, "count_implied");
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
count_is(c) {
|
2012-01-26 17:34:05 -06:00
|
|
|
let count_lit = mk_int(cx, sp, c);
|
2011-09-02 17:34:58 -05:00
|
|
|
let count_is_path = make_path_vec(cx, "count_is");
|
2011-08-27 23:27:39 -05:00
|
|
|
let count_is_args = [count_lit];
|
2012-01-26 17:34:05 -06:00
|
|
|
ret mk_call(cx, sp, count_is_path, count_is_args);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2011-09-02 17:34:58 -05:00
|
|
|
_ { cx.span_unimpl(sp, "unimplemented #fmt conversion"); }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2011-09-12 04:27:30 -05:00
|
|
|
fn make_ty(cx: ext_ctxt, sp: span, t: ty) -> @ast::expr {
|
2011-08-27 23:27:39 -05:00
|
|
|
let rt_type;
|
|
|
|
alt t {
|
|
|
|
ty_hex(c) {
|
|
|
|
alt c {
|
2012-01-19 00:37:22 -06:00
|
|
|
case_upper { rt_type = "ty_hex_upper"; }
|
|
|
|
case_lower { rt_type = "ty_hex_lower"; }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-01-19 00:37:22 -06:00
|
|
|
ty_bits { rt_type = "ty_bits"; }
|
|
|
|
ty_octal { rt_type = "ty_octal"; }
|
2011-09-02 17:34:58 -05:00
|
|
|
_ { rt_type = "ty_default"; }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
ret make_rt_path_expr(cx, sp, rt_type);
|
|
|
|
}
|
2011-09-12 04:27:30 -05:00
|
|
|
fn make_conv_rec(cx: ext_ctxt, sp: span, flags_expr: @ast::expr,
|
2011-08-27 23:27:39 -05:00
|
|
|
width_expr: @ast::expr, precision_expr: @ast::expr,
|
|
|
|
ty_expr: @ast::expr) -> @ast::expr {
|
2012-01-26 17:34:05 -06:00
|
|
|
ret mk_rec_e(cx, sp,
|
|
|
|
[{ident: "flags", ex: flags_expr},
|
|
|
|
{ident: "width", ex: width_expr},
|
|
|
|
{ident: "precision", ex: precision_expr},
|
|
|
|
{ident: "ty", ex: ty_expr}]);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
ret make_conv_rec(cx, sp, rt_conv_flags, rt_conv_width,
|
|
|
|
rt_conv_precision, rt_conv_ty);
|
|
|
|
}
|
2011-09-12 04:27:30 -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;
|
2011-08-27 23:27:39 -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-01-26 17:34:05 -06:00
|
|
|
ret mk_call(cx, arg.span, path, args);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2011-09-12 04:27:30 -05:00
|
|
|
fn make_new_conv(cx: ext_ctxt, sp: span, cnv: conv, arg: @ast::expr) ->
|
2011-08-27 23:27:39 -05:00
|
|
|
@ast::expr {
|
|
|
|
// FIXME: Extract all this validation into extfmt::ct
|
|
|
|
|
|
|
|
fn is_signed_type(cnv: conv) -> bool {
|
|
|
|
alt cnv.ty {
|
|
|
|
ty_int(s) {
|
2012-01-19 00:37:22 -06:00
|
|
|
alt s { signed { ret true; } unsigned { ret false; } }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-01-19 00:37:22 -06:00
|
|
|
ty_float { ret true; }
|
2011-08-27 23:27:39 -05:00
|
|
|
_ { ret false; }
|
|
|
|
}
|
|
|
|
}
|
2011-09-02 17:34:58 -05:00
|
|
|
let unsupported = "conversion not supported in #fmt string";
|
2011-08-27 23:27:39 -05:00
|
|
|
alt cnv.param {
|
2012-01-19 00:37:22 -06:00
|
|
|
option::none { }
|
2011-08-27 23:27:39 -05:00
|
|
|
_ { cx.span_unimpl(sp, unsupported); }
|
|
|
|
}
|
|
|
|
for f: flag in cnv.flags {
|
|
|
|
alt f {
|
2012-01-19 00:37:22 -06:00
|
|
|
flag_left_justify { }
|
|
|
|
flag_sign_always {
|
2011-08-27 23:27:39 -05:00
|
|
|
if !is_signed_type(cnv) {
|
|
|
|
cx.span_fatal(sp,
|
2011-09-02 17:34:58 -05:00
|
|
|
"+ flag only valid in " +
|
|
|
|
"signed #fmt conversion");
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-01-19 00:37:22 -06:00
|
|
|
flag_space_for_sign {
|
2011-08-27 23:27:39 -05:00
|
|
|
if !is_signed_type(cnv) {
|
|
|
|
cx.span_fatal(sp,
|
2011-09-02 17:34:58 -05:00
|
|
|
"space flag only valid in " +
|
|
|
|
"signed #fmt conversions");
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-01-19 00:37:22 -06:00
|
|
|
flag_left_zero_pad { }
|
2011-08-27 23:27:39 -05:00
|
|
|
_ { cx.span_unimpl(sp, unsupported); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
alt cnv.width {
|
2012-01-19 00:37:22 -06:00
|
|
|
count_implied { }
|
2011-08-27 23:27:39 -05:00
|
|
|
count_is(_) { }
|
|
|
|
_ { cx.span_unimpl(sp, unsupported); }
|
|
|
|
}
|
|
|
|
alt cnv.precision {
|
2012-01-19 00:37:22 -06:00
|
|
|
count_implied { }
|
2011-08-27 23:27:39 -05:00
|
|
|
count_is(_) { }
|
|
|
|
_ { cx.span_unimpl(sp, unsupported); }
|
|
|
|
}
|
|
|
|
alt cnv.ty {
|
2012-01-19 00:37:22 -06:00
|
|
|
ty_str { ret make_conv_call(cx, arg.span, "str", cnv, arg); }
|
2011-08-27 23:27:39 -05:00
|
|
|
ty_int(sign) {
|
|
|
|
alt sign {
|
2012-01-19 00:37:22 -06:00
|
|
|
signed { ret make_conv_call(cx, arg.span, "int", cnv, arg); }
|
|
|
|
unsigned {
|
2011-09-02 17:34:58 -05:00
|
|
|
ret make_conv_call(cx, arg.span, "uint", cnv, arg);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-01-19 00:37:22 -06:00
|
|
|
ty_bool { ret make_conv_call(cx, arg.span, "bool", cnv, arg); }
|
|
|
|
ty_char { ret make_conv_call(cx, arg.span, "char", cnv, arg); }
|
2011-09-02 17:34:58 -05:00
|
|
|
ty_hex(_) { ret make_conv_call(cx, arg.span, "uint", cnv, arg); }
|
2012-01-19 00:37:22 -06:00
|
|
|
ty_bits { ret make_conv_call(cx, arg.span, "uint", cnv, arg); }
|
|
|
|
ty_octal { ret make_conv_call(cx, arg.span, "uint", cnv, arg); }
|
|
|
|
ty_float { ret make_conv_call(cx, arg.span, "float", cnv, arg); }
|
|
|
|
ty_poly { ret make_conv_call(cx, arg.span, "poly", cnv, arg); }
|
2011-08-27 23:27:39 -05:00
|
|
|
_ { cx.span_unimpl(sp, unsupported); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn log_conv(c: conv) {
|
|
|
|
alt c.param {
|
2011-12-22 19:53:53 -06:00
|
|
|
some(p) { log(debug, "param: " + int::to_str(p, 10u)); }
|
2011-12-22 16:42:52 -06:00
|
|
|
_ { #debug("param: none"); }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
for f: flag in c.flags {
|
|
|
|
alt f {
|
2012-01-19 00:37:22 -06:00
|
|
|
flag_left_justify { #debug("flag: left justify"); }
|
|
|
|
flag_left_zero_pad { #debug("flag: left zero pad"); }
|
|
|
|
flag_space_for_sign { #debug("flag: left space pad"); }
|
|
|
|
flag_sign_always { #debug("flag: sign always"); }
|
|
|
|
flag_alternate { #debug("flag: alternate"); }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
alt c.width {
|
2011-12-22 19:53:53 -06:00
|
|
|
count_is(i) { log(debug,
|
2011-12-22 16:42:52 -06:00
|
|
|
"width: count is " + int::to_str(i, 10u)); }
|
2011-08-27 23:27:39 -05:00
|
|
|
count_is_param(i) {
|
2011-12-22 19:53:53 -06:00
|
|
|
log(debug,
|
2011-12-22 16:42:52 -06:00
|
|
|
"width: count is param " + int::to_str(i, 10u));
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-01-19 00:37:22 -06:00
|
|
|
count_is_next_param { #debug("width: count is next param"); }
|
|
|
|
count_implied { #debug("width: count is implied"); }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
alt c.precision {
|
2011-12-22 19:53:53 -06:00
|
|
|
count_is(i) { log(debug,
|
2011-12-22 16:42:52 -06:00
|
|
|
"prec: count is " + int::to_str(i, 10u)); }
|
2011-08-27 23:27:39 -05:00
|
|
|
count_is_param(i) {
|
2011-12-22 19:53:53 -06:00
|
|
|
log(debug,
|
2011-12-22 16:42:52 -06:00
|
|
|
"prec: count is param " + int::to_str(i, 10u));
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2012-01-19 00:37:22 -06:00
|
|
|
count_is_next_param { #debug("prec: count is next param"); }
|
|
|
|
count_implied { #debug("prec: count is implied"); }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
alt c.ty {
|
2012-01-19 00:37:22 -06:00
|
|
|
ty_bool { #debug("type: bool"); }
|
|
|
|
ty_str { #debug("type: str"); }
|
|
|
|
ty_char { #debug("type: char"); }
|
2011-08-27 23:27:39 -05:00
|
|
|
ty_int(s) {
|
|
|
|
alt s {
|
2012-01-19 00:37:22 -06:00
|
|
|
signed { #debug("type: signed"); }
|
|
|
|
unsigned { #debug("type: unsigned"); }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-01-19 00:37:22 -06:00
|
|
|
ty_bits { #debug("type: bits"); }
|
2011-08-27 23:27:39 -05:00
|
|
|
ty_hex(cs) {
|
|
|
|
alt cs {
|
2012-01-19 00:37:22 -06:00
|
|
|
case_upper { #debug("type: uhex"); }
|
|
|
|
case_lower { #debug("type: lhex"); }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-01-19 00:37:22 -06:00
|
|
|
ty_octal { #debug("type: octal"); }
|
|
|
|
ty_float { #debug("type: float"); }
|
|
|
|
ty_poly { #debug("type: poly"); }
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
let fmt_sp = args[0].span;
|
|
|
|
let n = 0u;
|
2012-01-26 17:34:05 -06:00
|
|
|
let tmp_expr = mk_str(cx, sp, "");
|
2011-08-27 23:27:39 -05:00
|
|
|
let nargs = vec::len::<@ast::expr>(args);
|
|
|
|
for pc: piece in pieces {
|
|
|
|
alt pc {
|
|
|
|
piece_string(s) {
|
2012-01-26 17:34:05 -06:00
|
|
|
let s_expr = mk_str(cx, fmt_sp, s);
|
|
|
|
tmp_expr = mk_binary(cx, fmt_sp, ast::add, tmp_expr, s_expr);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
piece_conv(conv) {
|
|
|
|
n += 1u;
|
|
|
|
if n >= nargs {
|
|
|
|
cx.span_fatal(sp,
|
2011-09-02 17:34:58 -05:00
|
|
|
"not enough arguments to #fmt " +
|
|
|
|
"for the given format string");
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
2011-12-22 16:42:52 -06:00
|
|
|
#debug("Building conversion:");
|
2011-08-27 23:27:39 -05:00
|
|
|
log_conv(conv);
|
|
|
|
let arg_expr = args[n];
|
|
|
|
let c_expr = make_new_conv(cx, fmt_sp, conv, arg_expr);
|
2012-01-26 17:34:05 -06:00
|
|
|
tmp_expr = mk_binary(cx, fmt_sp, ast::add, tmp_expr, c_expr);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
(sp, #fmt["too many arguments to #fmt. found %u, expected %u",
|
2011-09-12 04:27:30 -05:00
|
|
|
nargs, expected_nargs]);
|
2011-08-27 23:27:39 -05:00
|
|
|
}
|
|
|
|
ret tmp_expr;
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// Local Variables:
|
|
|
|
// mode: rust
|
|
|
|
// fill-column: 78;
|
|
|
|
// indent-tabs-mode: nil
|
|
|
|
// c-basic-offset: 4
|
|
|
|
// buffer-file-coding-system: utf-8-unix
|
|
|
|
// End:
|
|
|
|
//
|