|
| 1 | + |
| 2 | + |
| 3 | +/* |
| 4 | + * The compiler code necessary to support the #fmt extension. Eventually this |
| 5 | + * should all get sucked into either the standard library extfmt module or the |
| 6 | + * compiler syntax extension plugin interface. |
| 7 | + */ |
| 8 | +import std::vec; |
| 9 | +import std::str; |
| 10 | +import std::istr; |
| 11 | +import std::option; |
| 12 | +import std::option::none; |
| 13 | +import std::option::some; |
| 14 | +import std::extifmt::ct::*; |
| 15 | +import base::*; |
| 16 | +import codemap::span; |
| 17 | +export expand_syntax_ext; |
| 18 | + |
| 19 | +fn expand_syntax_ext(cx: &ext_ctxt, sp: span, arg: @ast::expr, |
| 20 | + _body: &option::t<istr>) -> @ast::expr { |
| 21 | + let args: [@ast::expr] = |
| 22 | + alt arg.node { |
| 23 | + ast::expr_vec(elts, _) { elts } |
| 24 | + _ { |
| 25 | + cx.span_fatal(sp, ~"#fmt requires arguments of the form `[...]`.") |
| 26 | + } |
| 27 | + }; |
| 28 | + if vec::len::<@ast::expr>(args) == 0u { |
| 29 | + cx.span_fatal(sp, ~"#fmt requires a format string"); |
| 30 | + } |
| 31 | + let fmt = |
| 32 | + expr_to_str(cx, args[0], |
| 33 | + ~"first argument to #fmt must be a " |
| 34 | + + ~"string literal."); |
| 35 | + let fmtspan = args[0].span; |
| 36 | + log "Format string:"; |
| 37 | + log fmt; |
| 38 | + fn parse_fmt_err_(cx: &ext_ctxt, sp: span, msg: &istr) -> ! { |
| 39 | + cx.span_fatal(sp, msg); |
| 40 | + } |
| 41 | + let parse_fmt_err = bind parse_fmt_err_(cx, fmtspan, _); |
| 42 | + let pieces = parse_fmt_string(fmt, parse_fmt_err); |
| 43 | + ret pieces_to_expr(cx, sp, pieces, args); |
| 44 | +} |
| 45 | + |
| 46 | +// FIXME: A lot of these functions for producing expressions can probably |
| 47 | +// be factored out in common with other code that builds expressions. |
| 48 | +// FIXME: Cleanup the naming of these functions |
| 49 | +fn pieces_to_expr(cx: &ext_ctxt, sp: span, pieces: &[piece], |
| 50 | + args: &[@ast::expr]) -> @ast::expr { |
| 51 | + fn make_new_lit(cx: &ext_ctxt, sp: span, lit: ast::lit_) -> @ast::expr { |
| 52 | + let sp_lit = @{node: lit, span: sp}; |
| 53 | + ret @{id: cx.next_id(), node: ast::expr_lit(sp_lit), span: sp}; |
| 54 | + } |
| 55 | + fn make_new_str(cx: &ext_ctxt, sp: span, s: &istr) -> @ast::expr { |
| 56 | + let lit = ast::lit_str(s, ast::sk_unique); |
| 57 | + ret make_new_lit(cx, sp, lit); |
| 58 | + } |
| 59 | + fn make_new_int(cx: &ext_ctxt, sp: span, i: int) -> @ast::expr { |
| 60 | + let lit = ast::lit_int(i); |
| 61 | + ret make_new_lit(cx, sp, lit); |
| 62 | + } |
| 63 | + fn make_new_uint(cx: &ext_ctxt, sp: span, u: uint) -> @ast::expr { |
| 64 | + let lit = ast::lit_uint(u); |
| 65 | + ret make_new_lit(cx, sp, lit); |
| 66 | + } |
| 67 | + fn make_add_expr(cx: &ext_ctxt, sp: span, lhs: @ast::expr, |
| 68 | + rhs: @ast::expr) -> @ast::expr { |
| 69 | + let binexpr = ast::expr_binary(ast::add, lhs, rhs); |
| 70 | + ret @{id: cx.next_id(), node: binexpr, span: sp}; |
| 71 | + } |
| 72 | + fn make_path_expr(cx: &ext_ctxt, sp: span, idents: &[ast::ident]) -> |
| 73 | + @ast::expr { |
| 74 | + let path = {global: false, idents: idents, types: []}; |
| 75 | + let sp_path = {node: path, span: sp}; |
| 76 | + let pathexpr = ast::expr_path(sp_path); |
| 77 | + ret @{id: cx.next_id(), node: pathexpr, span: sp}; |
| 78 | + } |
| 79 | + fn make_vec_expr(cx: &ext_ctxt, sp: span, exprs: &[@ast::expr]) -> |
| 80 | + @ast::expr { |
| 81 | + let vecexpr = ast::expr_vec(exprs, ast::imm); |
| 82 | + ret @{id: cx.next_id(), node: vecexpr, span: sp}; |
| 83 | + } |
| 84 | + fn make_call(cx: &ext_ctxt, sp: span, fn_path: &[ast::ident], |
| 85 | + args: &[@ast::expr]) -> @ast::expr { |
| 86 | + let pathexpr = make_path_expr(cx, sp, fn_path); |
| 87 | + let callexpr = ast::expr_call(pathexpr, args); |
| 88 | + ret @{id: cx.next_id(), node: callexpr, span: sp}; |
| 89 | + } |
| 90 | + fn make_rec_expr(cx: &ext_ctxt, sp: span, |
| 91 | + fields: &[{ident: ast::ident, ex: @ast::expr}]) -> |
| 92 | + @ast::expr { |
| 93 | + let astfields: [ast::field] = []; |
| 94 | + for field: {ident: ast::ident, ex: @ast::expr} in fields { |
| 95 | + let ident = field.ident; |
| 96 | + let val = field.ex; |
| 97 | + let astfield = |
| 98 | + {node: {mut: ast::imm, ident: ident, expr: val}, span: sp}; |
| 99 | + astfields += [astfield]; |
| 100 | + } |
| 101 | + let recexpr = ast::expr_rec(astfields, option::none::<@ast::expr>); |
| 102 | + ret @{id: cx.next_id(), node: recexpr, span: sp}; |
| 103 | + } |
| 104 | + fn make_path_vec(cx: &ext_ctxt, ident: &ast::ident) -> [ast::ident] { |
| 105 | + fn compiling_std(cx: &ext_ctxt) -> bool { |
| 106 | + ret istr::find(cx.crate_file_name(), ~"std.rc") >= 0; |
| 107 | + } |
| 108 | + if compiling_std(cx) { |
| 109 | + ret [~"extifmt", ~"rt", ident]; |
| 110 | + } else { ret [~"std", ~"extifmt", ~"rt", ident]; } |
| 111 | + } |
| 112 | + fn make_rt_path_expr(cx: &ext_ctxt, sp: span, |
| 113 | + ident: &istr) -> @ast::expr { |
| 114 | + let path = make_path_vec(cx, ident); |
| 115 | + ret make_path_expr(cx, sp, path); |
| 116 | + } |
| 117 | + // Produces an AST expression that represents a RT::conv record, |
| 118 | + // which tells the RT::conv* functions how to perform the conversion |
| 119 | + |
| 120 | + fn make_rt_conv_expr(cx: &ext_ctxt, sp: span, cnv: &conv) -> @ast::expr { |
| 121 | + fn make_flags(cx: &ext_ctxt, sp: span, flags: &[flag]) -> @ast::expr { |
| 122 | + let flagexprs: [@ast::expr] = []; |
| 123 | + for f: flag in flags { |
| 124 | + let fstr; |
| 125 | + alt f { |
| 126 | + flag_left_justify. { fstr = ~"flag_left_justify"; } |
| 127 | + flag_left_zero_pad. { fstr = ~"flag_left_zero_pad"; } |
| 128 | + flag_space_for_sign. { fstr = ~"flag_space_for_sign"; } |
| 129 | + flag_sign_always. { fstr = ~"flag_sign_always"; } |
| 130 | + flag_alternate. { fstr = ~"flag_alternate"; } |
| 131 | + } |
| 132 | + flagexprs += [make_rt_path_expr(cx, sp, fstr)]; |
| 133 | + } |
| 134 | + // FIXME: 0-length vectors can't have their type inferred |
| 135 | + // through the rec that these flags are a member of, so |
| 136 | + // this is a hack placeholder flag |
| 137 | + |
| 138 | + if vec::len::<@ast::expr>(flagexprs) == 0u { |
| 139 | + flagexprs += [make_rt_path_expr(cx, sp, ~"flag_none")]; |
| 140 | + } |
| 141 | + ret make_vec_expr(cx, sp, flagexprs); |
| 142 | + } |
| 143 | + fn make_count(cx: &ext_ctxt, sp: span, cnt: &count) -> @ast::expr { |
| 144 | + alt cnt { |
| 145 | + count_implied. { |
| 146 | + ret make_rt_path_expr(cx, sp, ~"count_implied"); |
| 147 | + } |
| 148 | + count_is(c) { |
| 149 | + let count_lit = make_new_int(cx, sp, c); |
| 150 | + let count_is_path = make_path_vec(cx, ~"count_is"); |
| 151 | + let count_is_args = [count_lit]; |
| 152 | + ret make_call(cx, sp, count_is_path, count_is_args); |
| 153 | + } |
| 154 | + _ { cx.span_unimpl(sp, ~"unimplemented #fmt conversion"); } |
| 155 | + } |
| 156 | + } |
| 157 | + fn make_ty(cx: &ext_ctxt, sp: span, t: &ty) -> @ast::expr { |
| 158 | + let rt_type; |
| 159 | + alt t { |
| 160 | + ty_hex(c) { |
| 161 | + alt c { |
| 162 | + case_upper. { rt_type = ~"ty_hex_upper"; } |
| 163 | + case_lower. { rt_type = ~"ty_hex_lower"; } |
| 164 | + } |
| 165 | + } |
| 166 | + ty_bits. { rt_type = ~"ty_bits"; } |
| 167 | + ty_octal. { rt_type = ~"ty_octal"; } |
| 168 | + _ { rt_type = ~"ty_default"; } |
| 169 | + } |
| 170 | + ret make_rt_path_expr(cx, sp, rt_type); |
| 171 | + } |
| 172 | + fn make_conv_rec(cx: &ext_ctxt, sp: span, flags_expr: @ast::expr, |
| 173 | + width_expr: @ast::expr, precision_expr: @ast::expr, |
| 174 | + ty_expr: @ast::expr) -> @ast::expr { |
| 175 | + ret make_rec_expr(cx, sp, |
| 176 | + [{ident: ~"flags", ex: flags_expr}, |
| 177 | + {ident: ~"width", ex: width_expr}, |
| 178 | + {ident: ~"precision", ex: precision_expr}, |
| 179 | + {ident: ~"ty", ex: ty_expr}]); |
| 180 | + } |
| 181 | + let rt_conv_flags = make_flags(cx, sp, cnv.flags); |
| 182 | + let rt_conv_width = make_count(cx, sp, cnv.width); |
| 183 | + let rt_conv_precision = make_count(cx, sp, cnv.precision); |
| 184 | + let rt_conv_ty = make_ty(cx, sp, cnv.ty); |
| 185 | + ret make_conv_rec(cx, sp, rt_conv_flags, rt_conv_width, |
| 186 | + rt_conv_precision, rt_conv_ty); |
| 187 | + } |
| 188 | + fn make_conv_call(cx: &ext_ctxt, sp: span, conv_type: &istr, |
| 189 | + cnv: &conv, arg: @ast::expr) -> @ast::expr { |
| 190 | + let fname = ~"conv_" + conv_type; |
| 191 | + let path = make_path_vec(cx, fname); |
| 192 | + let cnv_expr = make_rt_conv_expr(cx, sp, cnv); |
| 193 | + let args = [cnv_expr, arg]; |
| 194 | + ret make_call(cx, arg.span, path, args); |
| 195 | + } |
| 196 | + fn make_new_conv(cx: &ext_ctxt, sp: span, cnv: conv, arg: @ast::expr) -> |
| 197 | + @ast::expr { |
| 198 | + // FIXME: Extract all this validation into extfmt::ct |
| 199 | + |
| 200 | + fn is_signed_type(cnv: conv) -> bool { |
| 201 | + alt cnv.ty { |
| 202 | + ty_int(s) { |
| 203 | + alt s { signed. { ret true; } unsigned. { ret false; } } |
| 204 | + } |
| 205 | + _ { ret false; } |
| 206 | + } |
| 207 | + } |
| 208 | + let unsupported = ~"conversion not supported in #fmt string"; |
| 209 | + alt cnv.param { |
| 210 | + option::none. { } |
| 211 | + _ { cx.span_unimpl(sp, unsupported); } |
| 212 | + } |
| 213 | + for f: flag in cnv.flags { |
| 214 | + alt f { |
| 215 | + flag_left_justify. { } |
| 216 | + flag_sign_always. { |
| 217 | + if !is_signed_type(cnv) { |
| 218 | + cx.span_fatal(sp, |
| 219 | + ~"+ flag only valid in " + |
| 220 | + ~"signed #fmt conversion"); |
| 221 | + } |
| 222 | + } |
| 223 | + flag_space_for_sign. { |
| 224 | + if !is_signed_type(cnv) { |
| 225 | + cx.span_fatal(sp, |
| 226 | + ~"space flag only valid in " + |
| 227 | + ~"signed #fmt conversions"); |
| 228 | + } |
| 229 | + } |
| 230 | + flag_left_zero_pad. { } |
| 231 | + _ { cx.span_unimpl(sp, unsupported); } |
| 232 | + } |
| 233 | + } |
| 234 | + alt cnv.width { |
| 235 | + count_implied. { } |
| 236 | + count_is(_) { } |
| 237 | + _ { cx.span_unimpl(sp, unsupported); } |
| 238 | + } |
| 239 | + alt cnv.precision { |
| 240 | + count_implied. { } |
| 241 | + count_is(_) { } |
| 242 | + _ { cx.span_unimpl(sp, unsupported); } |
| 243 | + } |
| 244 | + alt cnv.ty { |
| 245 | + ty_str. { ret make_conv_call(cx, arg.span, ~"str", cnv, arg); } |
| 246 | + ty_int(sign) { |
| 247 | + alt sign { |
| 248 | + signed. { ret make_conv_call(cx, arg.span, ~"int", cnv, arg); } |
| 249 | + unsigned. { |
| 250 | + ret make_conv_call(cx, arg.span, ~"uint", cnv, arg); |
| 251 | + } |
| 252 | + } |
| 253 | + } |
| 254 | + ty_bool. { ret make_conv_call(cx, arg.span, ~"bool", cnv, arg); } |
| 255 | + ty_char. { ret make_conv_call(cx, arg.span, ~"char", cnv, arg); } |
| 256 | + ty_hex(_) { ret make_conv_call(cx, arg.span, ~"uint", cnv, arg); } |
| 257 | + ty_bits. { ret make_conv_call(cx, arg.span, ~"uint", cnv, arg); } |
| 258 | + ty_octal. { ret make_conv_call(cx, arg.span, ~"uint", cnv, arg); } |
| 259 | + _ { cx.span_unimpl(sp, unsupported); } |
| 260 | + } |
| 261 | + } |
| 262 | + fn log_conv(c: conv) { |
| 263 | + alt c.param { |
| 264 | + some(p) { |
| 265 | + log "param: " |
| 266 | + + istr::to_estr(std::int::to_str(p, 10u)); |
| 267 | + } |
| 268 | + _ { log "param: none"; } |
| 269 | + } |
| 270 | + for f: flag in c.flags { |
| 271 | + alt f { |
| 272 | + flag_left_justify. { log "flag: left justify"; } |
| 273 | + flag_left_zero_pad. { log "flag: left zero pad"; } |
| 274 | + flag_space_for_sign. { log "flag: left space pad"; } |
| 275 | + flag_sign_always. { log "flag: sign always"; } |
| 276 | + flag_alternate. { log "flag: alternate"; } |
| 277 | + } |
| 278 | + } |
| 279 | + alt c.width { |
| 280 | + count_is(i) { log "width: count is " |
| 281 | + + istr::to_estr(std::int::to_str(i, 10u)); } |
| 282 | + count_is_param(i) { |
| 283 | + log "width: count is param " |
| 284 | + + istr::to_estr(std::int::to_str(i, 10u)); |
| 285 | + } |
| 286 | + count_is_next_param. { log "width: count is next param"; } |
| 287 | + count_implied. { log "width: count is implied"; } |
| 288 | + } |
| 289 | + alt c.precision { |
| 290 | + count_is(i) { log "prec: count is " |
| 291 | + + istr::to_estr(std::int::to_str(i, 10u)); } |
| 292 | + count_is_param(i) { |
| 293 | + log "prec: count is param " |
| 294 | + + istr::to_estr(std::int::to_str(i, 10u)); |
| 295 | + } |
| 296 | + count_is_next_param. { log "prec: count is next param"; } |
| 297 | + count_implied. { log "prec: count is implied"; } |
| 298 | + } |
| 299 | + alt c.ty { |
| 300 | + ty_bool. { log "type: bool"; } |
| 301 | + ty_str. { log "type: str"; } |
| 302 | + ty_char. { log "type: char"; } |
| 303 | + ty_int(s) { |
| 304 | + alt s { |
| 305 | + signed. { log "type: signed"; } |
| 306 | + unsigned. { log "type: unsigned"; } |
| 307 | + } |
| 308 | + } |
| 309 | + ty_bits. { log "type: bits"; } |
| 310 | + ty_hex(cs) { |
| 311 | + alt cs { |
| 312 | + case_upper. { log "type: uhex"; } |
| 313 | + case_lower. { log "type: lhex"; } |
| 314 | + } |
| 315 | + } |
| 316 | + ty_octal. { log "type: octal"; } |
| 317 | + } |
| 318 | + } |
| 319 | + let fmt_sp = args[0].span; |
| 320 | + let n = 0u; |
| 321 | + let tmp_expr = make_new_str(cx, sp, ~""); |
| 322 | + let nargs = vec::len::<@ast::expr>(args); |
| 323 | + for pc: piece in pieces { |
| 324 | + alt pc { |
| 325 | + piece_string(s) { |
| 326 | + let s_expr = make_new_str(cx, fmt_sp, s); |
| 327 | + tmp_expr = make_add_expr(cx, fmt_sp, tmp_expr, s_expr); |
| 328 | + } |
| 329 | + piece_conv(conv) { |
| 330 | + n += 1u; |
| 331 | + if n >= nargs { |
| 332 | + cx.span_fatal(sp, |
| 333 | + ~"not enough arguments to #fmt " + |
| 334 | + ~"for the given format string"); |
| 335 | + } |
| 336 | + log "Building conversion:"; |
| 337 | + log_conv(conv); |
| 338 | + let arg_expr = args[n]; |
| 339 | + let c_expr = make_new_conv(cx, fmt_sp, conv, arg_expr); |
| 340 | + tmp_expr = make_add_expr(cx, fmt_sp, tmp_expr, c_expr); |
| 341 | + } |
| 342 | + } |
| 343 | + } |
| 344 | + let expected_nargs = n + 1u; // n conversions + the fmt string |
| 345 | + |
| 346 | + if expected_nargs < nargs { |
| 347 | + cx.span_fatal( |
| 348 | + sp, istr::from_estr( |
| 349 | + #fmt["too many arguments to #fmt. found %u, expected %u", |
| 350 | + nargs, expected_nargs])); |
| 351 | + } |
| 352 | + ret tmp_expr; |
| 353 | +} |
| 354 | +// |
| 355 | +// Local Variables: |
| 356 | +// mode: rust |
| 357 | +// fill-column: 78; |
| 358 | +// indent-tabs-mode: nil |
| 359 | +// c-basic-offset: 4 |
| 360 | +// buffer-file-coding-system: utf-8-unix |
| 361 | +// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; |
| 362 | +// End: |
| 363 | +// |
0 commit comments