Remove the estr #fmt. Issue #855
This commit is contained in:
parent
a45068cf27
commit
775b64c955
5 changed files with 0 additions and 795 deletions
|
@ -63,7 +63,6 @@ mod syntax {
|
|||
mod base;
|
||||
mod expand;
|
||||
|
||||
mod fmt;
|
||||
mod ifmt;
|
||||
mod env;
|
||||
mod simplext;
|
||||
|
|
|
@ -22,7 +22,6 @@ tag syntax_extension {
|
|||
// AST nodes into full ASTs
|
||||
fn syntax_expander_table() -> hashmap<istr, syntax_extension> {
|
||||
let syntax_expanders = new_str_hash::<syntax_extension>();
|
||||
syntax_expanders.insert(~"fmt", normal(ext::fmt::expand_syntax_ext));
|
||||
syntax_expanders.insert(~"ifmt", normal(ext::ifmt::expand_syntax_ext));
|
||||
syntax_expanders.insert(~"env", normal(ext::env::expand_syntax_ext));
|
||||
syntax_expanders.insert(~"macro",
|
||||
|
|
|
@ -1,364 +0,0 @@
|
|||
|
||||
|
||||
/*
|
||||
* The compiler code necessary to support the #ifmt extension. Eventually this
|
||||
* should all get sucked into either the standard library extfmt module or the
|
||||
* compiler syntax extension plugin interface.
|
||||
*/
|
||||
import std::vec;
|
||||
import std::str;
|
||||
import std::istr;
|
||||
import std::option;
|
||||
import std::option::none;
|
||||
import std::option::some;
|
||||
import std::extfmt::ct::*;
|
||||
import base::*;
|
||||
import codemap::span;
|
||||
export expand_syntax_ext;
|
||||
|
||||
fn expand_syntax_ext(cx: &ext_ctxt, sp: span, arg: @ast::expr,
|
||||
_body: &option::t<istr>) -> @ast::expr {
|
||||
let args: [@ast::expr] =
|
||||
alt arg.node {
|
||||
ast::expr_vec(elts, _) { elts }
|
||||
_ {
|
||||
cx.span_fatal(
|
||||
sp, ~"#ifmt requires arguments of the form `[...]`.")
|
||||
}
|
||||
};
|
||||
if vec::len::<@ast::expr>(args) == 0u {
|
||||
cx.span_fatal(sp, ~"#ifmt requires a format string");
|
||||
}
|
||||
let fmt =
|
||||
expr_to_str(cx, args[0],
|
||||
~"first argument to #ifmt must be a "
|
||||
+ ~"string literal.");
|
||||
let fmtspan = args[0].span;
|
||||
log "Format string:";
|
||||
log fmt;
|
||||
fn parse_fmt_err_(cx: &ext_ctxt, sp: span, msg: str) -> ! {
|
||||
cx.span_fatal(sp, istr::from_estr(msg));
|
||||
}
|
||||
let parse_fmt_err = bind parse_fmt_err_(cx, fmtspan, _);
|
||||
let pieces = parse_fmt_string(istr::to_estr(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
|
||||
fn pieces_to_expr(cx: &ext_ctxt, sp: span, pieces: &[piece],
|
||||
args: &[@ast::expr]) -> @ast::expr {
|
||||
fn make_new_lit(cx: &ext_ctxt, sp: span, lit: ast::lit_) -> @ast::expr {
|
||||
let sp_lit = @{node: lit, span: sp};
|
||||
ret @{id: cx.next_id(), node: ast::expr_lit(sp_lit), span: sp};
|
||||
}
|
||||
fn make_new_str(cx: &ext_ctxt, sp: span, s: &istr) -> @ast::expr {
|
||||
let lit = ast::lit_str(s, ast::sk_rc);
|
||||
ret make_new_lit(cx, sp, lit);
|
||||
}
|
||||
fn make_new_int(cx: &ext_ctxt, sp: span, i: int) -> @ast::expr {
|
||||
let lit = ast::lit_int(i);
|
||||
ret make_new_lit(cx, sp, lit);
|
||||
}
|
||||
fn make_new_uint(cx: &ext_ctxt, sp: span, u: uint) -> @ast::expr {
|
||||
let lit = ast::lit_uint(u);
|
||||
ret make_new_lit(cx, sp, lit);
|
||||
}
|
||||
fn make_add_expr(cx: &ext_ctxt, sp: span, lhs: @ast::expr,
|
||||
rhs: @ast::expr) -> @ast::expr {
|
||||
let binexpr = ast::expr_binary(ast::add, lhs, rhs);
|
||||
ret @{id: cx.next_id(), node: binexpr, span: sp};
|
||||
}
|
||||
fn make_path_expr(cx: &ext_ctxt, sp: span, idents: &[ast::ident]) ->
|
||||
@ast::expr {
|
||||
let path = {global: false, idents: idents, types: []};
|
||||
let sp_path = {node: path, span: sp};
|
||||
let pathexpr = ast::expr_path(sp_path);
|
||||
ret @{id: cx.next_id(), node: pathexpr, span: sp};
|
||||
}
|
||||
fn make_vec_expr(cx: &ext_ctxt, sp: span, exprs: &[@ast::expr]) ->
|
||||
@ast::expr {
|
||||
let vecexpr = ast::expr_vec(exprs, ast::imm);
|
||||
ret @{id: cx.next_id(), node: vecexpr, span: sp};
|
||||
}
|
||||
fn make_call(cx: &ext_ctxt, sp: span, fn_path: &[ast::ident],
|
||||
args: &[@ast::expr]) -> @ast::expr {
|
||||
let pathexpr = make_path_expr(cx, sp, fn_path);
|
||||
let callexpr = ast::expr_call(pathexpr, args);
|
||||
ret @{id: cx.next_id(), node: callexpr, span: sp};
|
||||
}
|
||||
fn make_rec_expr(cx: &ext_ctxt, sp: span,
|
||||
fields: &[{ident: ast::ident, ex: @ast::expr}]) ->
|
||||
@ast::expr {
|
||||
let astfields: [ast::field] = [];
|
||||
for field: {ident: ast::ident, ex: @ast::expr} in fields {
|
||||
let ident = field.ident;
|
||||
let val = field.ex;
|
||||
let astfield =
|
||||
{node: {mut: ast::imm, ident: ident, expr: val}, span: sp};
|
||||
astfields += [astfield];
|
||||
}
|
||||
let recexpr = ast::expr_rec(astfields, option::none::<@ast::expr>);
|
||||
ret @{id: cx.next_id(), node: recexpr, span: sp};
|
||||
}
|
||||
fn make_path_vec(cx: &ext_ctxt, ident: &ast::ident) -> [ast::ident] {
|
||||
fn compiling_std(cx: &ext_ctxt) -> bool {
|
||||
ret istr::find(cx.crate_file_name(), ~"std.rc") >= 0;
|
||||
}
|
||||
if compiling_std(cx) {
|
||||
ret [~"extfmt", ~"rt", ident];
|
||||
} else { ret [~"std", ~"extfmt", ~"rt", ident]; }
|
||||
}
|
||||
fn make_rt_path_expr(cx: &ext_ctxt, sp: span,
|
||||
ident: &istr) -> @ast::expr {
|
||||
let path = make_path_vec(cx, ident);
|
||||
ret make_path_expr(cx, sp, path);
|
||||
}
|
||||
// Produces an AST expression that represents a RT::conv record,
|
||||
// which tells the RT::conv* functions how to perform the conversion
|
||||
|
||||
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 flagexprs: [@ast::expr] = [];
|
||||
for f: flag in flags {
|
||||
let fstr;
|
||||
alt f {
|
||||
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"; }
|
||||
}
|
||||
flagexprs += [make_rt_path_expr(cx, sp, fstr)];
|
||||
}
|
||||
// FIXME: 0-length vectors can't have their type inferred
|
||||
// through the rec that these flags are a member of, so
|
||||
// this is a hack placeholder flag
|
||||
|
||||
if vec::len::<@ast::expr>(flagexprs) == 0u {
|
||||
flagexprs += [make_rt_path_expr(cx, sp, ~"flag_none")];
|
||||
}
|
||||
ret make_vec_expr(cx, sp, flagexprs);
|
||||
}
|
||||
fn make_count(cx: &ext_ctxt, sp: span, cnt: &count) -> @ast::expr {
|
||||
alt cnt {
|
||||
count_implied. {
|
||||
ret make_rt_path_expr(cx, sp, ~"count_implied");
|
||||
}
|
||||
count_is(c) {
|
||||
let count_lit = make_new_int(cx, sp, c);
|
||||
let count_is_path = make_path_vec(cx, ~"count_is");
|
||||
let count_is_args = [count_lit];
|
||||
ret make_call(cx, sp, count_is_path, count_is_args);
|
||||
}
|
||||
_ { cx.span_unimpl(sp, ~"unimplemented #ifmt conversion"); }
|
||||
}
|
||||
}
|
||||
fn make_ty(cx: &ext_ctxt, sp: span, t: &ty) -> @ast::expr {
|
||||
let rt_type;
|
||||
alt t {
|
||||
ty_hex(c) {
|
||||
alt c {
|
||||
case_upper. { rt_type = ~"ty_hex_upper"; }
|
||||
case_lower. { rt_type = ~"ty_hex_lower"; }
|
||||
}
|
||||
}
|
||||
ty_bits. { rt_type = ~"ty_bits"; }
|
||||
ty_octal. { rt_type = ~"ty_octal"; }
|
||||
_ { rt_type = ~"ty_default"; }
|
||||
}
|
||||
ret 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 {
|
||||
ret make_rec_expr(cx, sp,
|
||||
[{ident: ~"flags", ex: flags_expr},
|
||||
{ident: ~"width", ex: width_expr},
|
||||
{ident: ~"precision", ex: precision_expr},
|
||||
{ident: ~"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);
|
||||
ret make_conv_rec(cx, sp, rt_conv_flags, rt_conv_width,
|
||||
rt_conv_precision, rt_conv_ty);
|
||||
}
|
||||
fn make_conv_call(cx: &ext_ctxt, sp: span, conv_type: &istr,
|
||||
cnv: &conv, arg: @ast::expr) -> @ast::expr {
|
||||
let fname = ~"conv_" + conv_type;
|
||||
let path = make_path_vec(cx, fname);
|
||||
let cnv_expr = make_rt_conv_expr(cx, sp, cnv);
|
||||
let args = [cnv_expr, arg];
|
||||
ret make_call(cx, arg.span, path, args);
|
||||
}
|
||||
fn make_new_conv(cx: &ext_ctxt, sp: span, cnv: conv, arg: @ast::expr) ->
|
||||
@ast::expr {
|
||||
// FIXME: Extract all this validation into extfmt::ct
|
||||
|
||||
fn is_signed_type(cnv: conv) -> bool {
|
||||
alt cnv.ty {
|
||||
ty_int(s) {
|
||||
alt s { signed. { ret true; } unsigned. { ret false; } }
|
||||
}
|
||||
_ { ret false; }
|
||||
}
|
||||
}
|
||||
let unsupported = ~"conversion not supported in #ifmt string";
|
||||
alt cnv.param {
|
||||
option::none. { }
|
||||
_ { cx.span_unimpl(sp, unsupported); }
|
||||
}
|
||||
for f: flag in cnv.flags {
|
||||
alt f {
|
||||
flag_left_justify. { }
|
||||
flag_sign_always. {
|
||||
if !is_signed_type(cnv) {
|
||||
cx.span_fatal(sp,
|
||||
~"+ flag only valid in " +
|
||||
~"signed #ifmt conversion");
|
||||
}
|
||||
}
|
||||
flag_space_for_sign. {
|
||||
if !is_signed_type(cnv) {
|
||||
cx.span_fatal(sp,
|
||||
~"space flag only valid in " +
|
||||
~"signed #ifmt conversions");
|
||||
}
|
||||
}
|
||||
flag_left_zero_pad. { }
|
||||
_ { cx.span_unimpl(sp, unsupported); }
|
||||
}
|
||||
}
|
||||
alt cnv.width {
|
||||
count_implied. { }
|
||||
count_is(_) { }
|
||||
_ { cx.span_unimpl(sp, unsupported); }
|
||||
}
|
||||
alt cnv.precision {
|
||||
count_implied. { }
|
||||
count_is(_) { }
|
||||
_ { cx.span_unimpl(sp, unsupported); }
|
||||
}
|
||||
alt cnv.ty {
|
||||
ty_str. { ret make_conv_call(cx, arg.span, ~"str", cnv, arg); }
|
||||
ty_int(sign) {
|
||||
alt sign {
|
||||
signed. { ret make_conv_call(cx, arg.span, ~"int", cnv, arg); }
|
||||
unsigned. {
|
||||
ret make_conv_call(cx, arg.span, ~"uint", cnv, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
ty_bool. { ret make_conv_call(cx, arg.span, ~"bool", cnv, arg); }
|
||||
ty_char. { ret make_conv_call(cx, arg.span, ~"char", cnv, arg); }
|
||||
ty_hex(_) { ret make_conv_call(cx, arg.span, ~"uint", cnv, arg); }
|
||||
ty_bits. { ret make_conv_call(cx, arg.span, ~"uint", cnv, arg); }
|
||||
ty_octal. { ret make_conv_call(cx, arg.span, ~"uint", cnv, arg); }
|
||||
_ { cx.span_unimpl(sp, unsupported); }
|
||||
}
|
||||
}
|
||||
fn log_conv(c: conv) {
|
||||
alt c.param {
|
||||
some(p) {
|
||||
log "param: "
|
||||
+ istr::to_estr(std::int::to_str(p, 10u));
|
||||
}
|
||||
_ { log "param: none"; }
|
||||
}
|
||||
for f: flag in c.flags {
|
||||
alt f {
|
||||
flag_left_justify. { log "flag: left justify"; }
|
||||
flag_left_zero_pad. { log "flag: left zero pad"; }
|
||||
flag_space_for_sign. { log "flag: left space pad"; }
|
||||
flag_sign_always. { log "flag: sign always"; }
|
||||
flag_alternate. { log "flag: alternate"; }
|
||||
}
|
||||
}
|
||||
alt c.width {
|
||||
count_is(i) { log "width: count is "
|
||||
+ istr::to_estr(std::int::to_str(i, 10u)); }
|
||||
count_is_param(i) {
|
||||
log "width: count is param "
|
||||
+ istr::to_estr(std::int::to_str(i, 10u));
|
||||
}
|
||||
count_is_next_param. { log "width: count is next param"; }
|
||||
count_implied. { log "width: count is implied"; }
|
||||
}
|
||||
alt c.precision {
|
||||
count_is(i) { log "prec: count is "
|
||||
+ istr::to_estr(std::int::to_str(i, 10u)); }
|
||||
count_is_param(i) {
|
||||
log "prec: count is param "
|
||||
+ istr::to_estr(std::int::to_str(i, 10u));
|
||||
}
|
||||
count_is_next_param. { log "prec: count is next param"; }
|
||||
count_implied. { log "prec: count is implied"; }
|
||||
}
|
||||
alt c.ty {
|
||||
ty_bool. { log "type: bool"; }
|
||||
ty_str. { log "type: str"; }
|
||||
ty_char. { log "type: char"; }
|
||||
ty_int(s) {
|
||||
alt s {
|
||||
signed. { log "type: signed"; }
|
||||
unsigned. { log "type: unsigned"; }
|
||||
}
|
||||
}
|
||||
ty_bits. { log "type: bits"; }
|
||||
ty_hex(cs) {
|
||||
alt cs {
|
||||
case_upper. { log "type: uhex"; }
|
||||
case_lower. { log "type: lhex"; }
|
||||
}
|
||||
}
|
||||
ty_octal. { log "type: octal"; }
|
||||
}
|
||||
}
|
||||
let fmt_sp = args[0].span;
|
||||
let n = 0u;
|
||||
let tmp_expr = make_new_str(cx, sp, ~"");
|
||||
let nargs = vec::len::<@ast::expr>(args);
|
||||
for pc: piece in pieces {
|
||||
alt pc {
|
||||
piece_string(s) {
|
||||
let s_expr = make_new_str(cx, fmt_sp, istr::from_estr(s));
|
||||
tmp_expr = make_add_expr(cx, fmt_sp, tmp_expr, s_expr);
|
||||
}
|
||||
piece_conv(conv) {
|
||||
n += 1u;
|
||||
if n >= nargs {
|
||||
cx.span_fatal(sp,
|
||||
~"not enough arguments to #ifmt " +
|
||||
~"for the given format string");
|
||||
}
|
||||
log "Building conversion:";
|
||||
log_conv(conv);
|
||||
let arg_expr = args[n];
|
||||
let c_expr = make_new_conv(cx, fmt_sp, conv, arg_expr);
|
||||
tmp_expr = make_add_expr(cx, fmt_sp, tmp_expr, c_expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
let expected_nargs = n + 1u; // n conversions + the fmt string
|
||||
|
||||
if expected_nargs < nargs {
|
||||
cx.span_fatal(
|
||||
sp,
|
||||
#ifmt["too many arguments to #fmt. found %u, expected %u",
|
||||
nargs, expected_nargs]);
|
||||
}
|
||||
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
|
||||
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
//
|
|
@ -1,428 +0,0 @@
|
|||
|
||||
|
||||
/* The 'fmt' extension is modeled on the posix printf system.
|
||||
*
|
||||
* A posix conversion ostensibly looks like this:
|
||||
*
|
||||
* %[parameter][flags][width][.precision][length]type
|
||||
*
|
||||
* Given the different numeric type bestiary we have, we omit the 'length'
|
||||
* parameter and support slightly different conversions for 'type':
|
||||
*
|
||||
* %[parameter][flags][width][.precision]type
|
||||
*
|
||||
* we also only support translating-to-rust a tiny subset of the possible
|
||||
* combinations at the moment.
|
||||
*/
|
||||
import option::none;
|
||||
import option::some;
|
||||
|
||||
|
||||
/*
|
||||
* We have a 'ct' (compile-time) module that parses format strings into a
|
||||
* sequence of conversions. From those conversions AST fragments are built
|
||||
* that call into properly-typed functions in the 'rt' (run-time) module.
|
||||
* Each of those run-time conversion functions accepts another conversion
|
||||
* description that specifies how to format its output.
|
||||
*
|
||||
* The building of the AST is currently done in a module inside the compiler,
|
||||
* but should migrate over here as the plugin interface is defined.
|
||||
*/
|
||||
|
||||
// Functions used by the fmt extension at compile time
|
||||
mod ct {
|
||||
tag signedness { signed; unsigned; }
|
||||
tag caseness { case_upper; case_lower; }
|
||||
tag ty {
|
||||
ty_bool;
|
||||
ty_str;
|
||||
ty_char;
|
||||
ty_int(signedness);
|
||||
ty_bits;
|
||||
ty_hex(caseness);
|
||||
ty_octal;
|
||||
// FIXME: More types
|
||||
}
|
||||
tag flag {
|
||||
flag_left_justify;
|
||||
flag_left_zero_pad;
|
||||
flag_space_for_sign;
|
||||
flag_sign_always;
|
||||
flag_alternate;
|
||||
}
|
||||
tag count {
|
||||
count_is(int);
|
||||
count_is_param(int);
|
||||
count_is_next_param;
|
||||
count_implied;
|
||||
}
|
||||
|
||||
// A formatted conversion from an expression to a string
|
||||
type conv =
|
||||
{param: option::t<int>,
|
||||
flags: [flag],
|
||||
width: count,
|
||||
precision: count,
|
||||
ty: ty};
|
||||
|
||||
|
||||
// A fragment of the output sequence
|
||||
tag piece { piece_string(str); piece_conv(conv); }
|
||||
type error_fn = fn(str) -> ! ;
|
||||
|
||||
fn parse_fmt_string(s: str, error: error_fn) -> [piece] {
|
||||
let pieces: [piece] = [];
|
||||
let lim = str::byte_len(s);
|
||||
let buf = "";
|
||||
fn flush_buf(buf: str, pieces: &mutable [piece]) -> str {
|
||||
if str::byte_len(buf) > 0u {
|
||||
let piece = piece_string(buf);
|
||||
pieces += [piece];
|
||||
}
|
||||
ret "";
|
||||
}
|
||||
let i = 0u;
|
||||
while i < lim {
|
||||
let curr = str::substr(s, i, 1u);
|
||||
if str::eq(curr, "%") {
|
||||
i += 1u;
|
||||
if i >= lim {
|
||||
error("unterminated conversion at end of string");
|
||||
}
|
||||
let curr2 = str::substr(s, i, 1u);
|
||||
if str::eq(curr2, "%") {
|
||||
i += 1u;
|
||||
} else {
|
||||
buf = flush_buf(buf, pieces);
|
||||
let rs = parse_conversion(s, i, lim, error);
|
||||
pieces += [rs.piece];
|
||||
i = rs.next;
|
||||
}
|
||||
} else { buf += curr; i += 1u; }
|
||||
}
|
||||
buf = flush_buf(buf, pieces);
|
||||
ret pieces;
|
||||
}
|
||||
fn peek_num(s: str, i: uint, lim: uint) ->
|
||||
option::t<{num: uint, next: uint}> {
|
||||
if i >= lim { ret none; }
|
||||
let c = s[i];
|
||||
if !('0' as u8 <= c && c <= '9' as u8) { ret option::none; }
|
||||
let n = c - ('0' as u8) as uint;
|
||||
ret alt peek_num(s, i + 1u, lim) {
|
||||
none. { some({num: n, next: i + 1u}) }
|
||||
some(next) {
|
||||
let m = next.num;
|
||||
let j = next.next;
|
||||
some({num: n * 10u + m, next: j})
|
||||
}
|
||||
};
|
||||
}
|
||||
fn parse_conversion(s: str, i: uint, lim: uint, error: error_fn) ->
|
||||
{piece: piece, next: uint} {
|
||||
let parm = parse_parameter(s, i, lim);
|
||||
let flags = parse_flags(s, parm.next, lim);
|
||||
let width = parse_count(s, flags.next, lim);
|
||||
let prec = parse_precision(s, width.next, lim);
|
||||
let ty = parse_type(s, prec.next, lim, error);
|
||||
ret {piece:
|
||||
piece_conv({param: parm.param,
|
||||
flags: flags.flags,
|
||||
width: width.count,
|
||||
precision: prec.count,
|
||||
ty: ty.ty}),
|
||||
next: ty.next};
|
||||
}
|
||||
fn parse_parameter(s: str, i: uint, lim: uint) ->
|
||||
{param: option::t<int>, next: uint} {
|
||||
if i >= lim { ret {param: none, next: i}; }
|
||||
let num = peek_num(s, i, lim);
|
||||
ret alt num {
|
||||
none. { {param: none, next: i} }
|
||||
some(t) {
|
||||
let n = t.num;
|
||||
let j = t.next;
|
||||
if j < lim && s[j] == '$' as u8 {
|
||||
{param: some(n as int), next: j + 1u}
|
||||
} else { {param: none, next: i} }
|
||||
}
|
||||
};
|
||||
}
|
||||
fn parse_flags(s: str, i: uint, lim: uint) ->
|
||||
{flags: [flag], next: uint} {
|
||||
let noflags: [flag] = [];
|
||||
if i >= lim { ret {flags: noflags, next: i}; }
|
||||
|
||||
// FIXME: This recursion generates illegal instructions if the return
|
||||
// value isn't boxed. Only started happening after the ivec conversion
|
||||
fn more_(f: flag, s: str, i: uint, lim: uint) ->
|
||||
@{flags: [flag], next: uint} {
|
||||
let next = parse_flags(s, i + 1u, lim);
|
||||
let rest = next.flags;
|
||||
let j = next.next;
|
||||
let curr: [flag] = [f];
|
||||
ret @{flags: curr + rest, next: j};
|
||||
}
|
||||
let more = bind more_(_, s, i, lim);
|
||||
let f = s[i];
|
||||
ret if f == '-' as u8 {
|
||||
*more(flag_left_justify)
|
||||
} else if f == '0' as u8 {
|
||||
*more(flag_left_zero_pad)
|
||||
} else if f == ' ' as u8 {
|
||||
*more(flag_space_for_sign)
|
||||
} else if f == '+' as u8 {
|
||||
*more(flag_sign_always)
|
||||
} else if f == '#' as u8 {
|
||||
*more(flag_alternate)
|
||||
} else { {flags: noflags, next: i} };
|
||||
}
|
||||
fn parse_count(s: str, i: uint, lim: uint) -> {count: count, next: uint} {
|
||||
ret if i >= lim {
|
||||
{count: count_implied, next: i}
|
||||
} else if s[i] == '*' as u8 {
|
||||
let param = parse_parameter(s, i + 1u, lim);
|
||||
let j = param.next;
|
||||
alt param.param {
|
||||
none. { {count: count_is_next_param, next: j} }
|
||||
some(n) { {count: count_is_param(n), next: j} }
|
||||
}
|
||||
} else {
|
||||
let num = peek_num(s, i, lim);
|
||||
alt num {
|
||||
none. { {count: count_implied, next: i} }
|
||||
some(num) {
|
||||
{count: count_is(num.num as int), next: num.next}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
fn parse_precision(s: str, i: uint, lim: uint) ->
|
||||
{count: count, next: uint} {
|
||||
ret if i >= lim {
|
||||
{count: count_implied, next: i}
|
||||
} else if s[i] == '.' as u8 {
|
||||
let count = parse_count(s, i + 1u, lim);
|
||||
|
||||
|
||||
// If there were no digits specified, i.e. the precision
|
||||
// was ".", then the precision is 0
|
||||
alt count.count {
|
||||
count_implied. { {count: count_is(0), next: count.next} }
|
||||
_ { count }
|
||||
}
|
||||
} else { {count: count_implied, next: i} };
|
||||
}
|
||||
fn parse_type(s: str, i: uint, lim: uint, error: error_fn) ->
|
||||
{ty: ty, next: uint} {
|
||||
if i >= lim { error("missing type in conversion"); }
|
||||
let tstr = str::substr(s, i, 1u);
|
||||
// TODO: Do we really want two signed types here?
|
||||
// How important is it to be printf compatible?
|
||||
let t =
|
||||
if str::eq(tstr, "b") {
|
||||
ty_bool
|
||||
} else if str::eq(tstr, "s") {
|
||||
ty_str
|
||||
} else if str::eq(tstr, "c") {
|
||||
ty_char
|
||||
} else if str::eq(tstr, "d") || str::eq(tstr, "i") {
|
||||
ty_int(signed)
|
||||
} else if str::eq(tstr, "u") {
|
||||
ty_int(unsigned)
|
||||
} else if str::eq(tstr, "x") {
|
||||
ty_hex(case_lower)
|
||||
} else if str::eq(tstr, "X") {
|
||||
ty_hex(case_upper)
|
||||
} else if str::eq(tstr, "t") {
|
||||
ty_bits
|
||||
} else if str::eq(tstr, "o") {
|
||||
ty_octal
|
||||
} else { error("unknown type in conversion: " + tstr) };
|
||||
ret {ty: t, next: i + 1u};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Functions used by the fmt extension at runtime. For now there are a lot of
|
||||
// decisions made a runtime. If it proves worthwhile then some of these
|
||||
// conditions can be evaluated at compile-time. For now though it's cleaner to
|
||||
// implement it this way, I think.
|
||||
mod rt {
|
||||
tag flag {
|
||||
flag_left_justify;
|
||||
flag_left_zero_pad;
|
||||
flag_space_for_sign;
|
||||
flag_sign_always;
|
||||
flag_alternate;
|
||||
|
||||
|
||||
// FIXME: This is a hack to avoid creating 0-length vec exprs,
|
||||
// which have some difficulty typechecking currently. See
|
||||
// comments in front::extfmt::make_flags
|
||||
flag_none;
|
||||
}
|
||||
tag count { count_is(int); count_implied; }
|
||||
tag ty { ty_default; ty_bits; ty_hex_upper; ty_hex_lower; ty_octal; }
|
||||
|
||||
// FIXME: May not want to use a vector here for flags;
|
||||
// instead just use a bool per flag
|
||||
type conv = {flags: [flag], width: count, precision: count, ty: ty};
|
||||
|
||||
fn conv_int(cv: &conv, i: int) -> str {
|
||||
let radix = 10u;
|
||||
let prec = get_int_precision(cv);
|
||||
let s = int_to_str_prec(i, radix, prec);
|
||||
if 0 <= i {
|
||||
if have_flag(cv.flags, flag_sign_always) {
|
||||
s = "+" + s;
|
||||
} else if have_flag(cv.flags, flag_space_for_sign) {
|
||||
s = " " + s;
|
||||
}
|
||||
}
|
||||
ret pad(cv, s, pad_signed);
|
||||
}
|
||||
fn conv_uint(cv: &conv, u: uint) -> str {
|
||||
let prec = get_int_precision(cv);
|
||||
let rs =
|
||||
alt cv.ty {
|
||||
ty_default. { uint_to_str_prec(u, 10u, prec) }
|
||||
ty_hex_lower. { uint_to_str_prec(u, 16u, prec) }
|
||||
ty_hex_upper. { str::to_upper(uint_to_str_prec(u, 16u, prec)) }
|
||||
ty_bits. { uint_to_str_prec(u, 2u, prec) }
|
||||
ty_octal. { uint_to_str_prec(u, 8u, prec) }
|
||||
};
|
||||
ret pad(cv, rs, pad_unsigned);
|
||||
}
|
||||
fn conv_bool(cv: &conv, b: bool) -> str {
|
||||
let s = if b { "true" } else { "false" };
|
||||
// run the boolean conversion through the string conversion logic,
|
||||
// giving it the same rules for precision, etc.
|
||||
|
||||
ret conv_str(cv, s);
|
||||
}
|
||||
fn conv_char(cv: &conv, c: char) -> str {
|
||||
ret pad(cv, str::from_char(c), pad_nozero);
|
||||
}
|
||||
fn conv_str(cv: &conv, s: str) -> str {
|
||||
// For strings, precision is the maximum characters
|
||||
// displayed
|
||||
|
||||
// FIXME: substr works on bytes, not chars!
|
||||
let unpadded =
|
||||
alt cv.precision {
|
||||
count_implied. { s }
|
||||
count_is(max) {
|
||||
if max as uint < str::char_len(s) {
|
||||
str::substr(s, 0u, max as uint)
|
||||
} else { s }
|
||||
}
|
||||
};
|
||||
ret pad(cv, unpadded, pad_nozero);
|
||||
}
|
||||
|
||||
// Convert an int to string with minimum number of digits. If precision is
|
||||
// 0 and num is 0 then the result is the empty string.
|
||||
fn int_to_str_prec(num: int, radix: uint, prec: uint) -> str {
|
||||
ret if num < 0 {
|
||||
"-" + uint_to_str_prec(-num as uint, radix, prec)
|
||||
} else { uint_to_str_prec(num as uint, radix, prec) };
|
||||
}
|
||||
|
||||
// Convert a uint to string with a minimum number of digits. If precision
|
||||
// is 0 and num is 0 then the result is the empty string. Could move this
|
||||
// to uint: but it doesn't seem all that useful.
|
||||
fn uint_to_str_prec(num: uint, radix: uint, prec: uint) -> str {
|
||||
ret if prec == 0u && num == 0u {
|
||||
""
|
||||
} else {
|
||||
let s = istr::to_estr(uint::to_str(num, radix));
|
||||
let len = str::char_len(s);
|
||||
if len < prec {
|
||||
let diff = prec - len;
|
||||
let pad = str_init_elt('0', diff);
|
||||
pad + s
|
||||
} else { s }
|
||||
};
|
||||
}
|
||||
fn get_int_precision(cv: &conv) -> uint {
|
||||
ret alt cv.precision {
|
||||
count_is(c) { c as uint }
|
||||
count_implied. { 1u }
|
||||
};
|
||||
}
|
||||
|
||||
// FIXME: This might be useful in str: but needs to be utf8 safe first
|
||||
fn str_init_elt(c: char, n_elts: uint) -> str {
|
||||
let svec = vec::init_elt::<u8>(c as u8, n_elts);
|
||||
|
||||
ret str::unsafe_from_bytes(svec);
|
||||
}
|
||||
tag pad_mode { pad_signed; pad_unsigned; pad_nozero; }
|
||||
fn pad(cv: &conv, s: str, mode: pad_mode) -> str {
|
||||
let uwidth;
|
||||
alt cv.width {
|
||||
count_implied. { ret s; }
|
||||
count_is(width) {
|
||||
// FIXME: Maybe width should be uint
|
||||
|
||||
uwidth = width as uint;
|
||||
}
|
||||
}
|
||||
let strlen = str::char_len(s);
|
||||
if uwidth <= strlen { ret s; }
|
||||
let padchar = ' ';
|
||||
let diff = uwidth - strlen;
|
||||
if have_flag(cv.flags, flag_left_justify) {
|
||||
let padstr = str_init_elt(padchar, diff);
|
||||
ret s + padstr;
|
||||
}
|
||||
let might_zero_pad = false;
|
||||
let signed = false;
|
||||
alt mode {
|
||||
pad_nozero. {
|
||||
// fallthrough
|
||||
|
||||
}
|
||||
pad_signed. { might_zero_pad = true; signed = true; }
|
||||
pad_unsigned. { might_zero_pad = true; }
|
||||
}
|
||||
fn have_precision(cv: &conv) -> bool {
|
||||
ret alt cv.precision { count_implied. { false } _ { true } };
|
||||
}
|
||||
let zero_padding = false;
|
||||
if might_zero_pad && have_flag(cv.flags, flag_left_zero_pad) &&
|
||||
!have_precision(cv) {
|
||||
padchar = '0';
|
||||
zero_padding = true;
|
||||
}
|
||||
let padstr = str_init_elt(padchar, diff);
|
||||
// This is completely heinous. If we have a signed value then
|
||||
// potentially rip apart the intermediate result and insert some
|
||||
// zeros. It may make sense to convert zero padding to a precision
|
||||
// instead.
|
||||
|
||||
if signed && zero_padding && str::byte_len(s) > 0u {
|
||||
let head = s[0];
|
||||
if head == '+' as u8 || head == '-' as u8 || head == ' ' as u8 {
|
||||
let headstr = str::unsafe_from_bytes([head]);
|
||||
let bytelen = str::byte_len(s);
|
||||
let numpart = str::substr(s, 1u, bytelen - 1u);
|
||||
ret headstr + padstr + numpart;
|
||||
}
|
||||
}
|
||||
ret padstr + s;
|
||||
}
|
||||
fn have_flag(flags: &[flag], f: flag) -> bool {
|
||||
for candidate: flag in flags { if candidate == f { ret true; } }
|
||||
ret false;
|
||||
}
|
||||
}
|
||||
// Local Variables:
|
||||
// mode: rust;
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
|
@ -86,7 +86,6 @@ mod sort;
|
|||
mod sha1;
|
||||
mod ebml;
|
||||
mod ufind;
|
||||
mod extfmt;
|
||||
mod extifmt;
|
||||
mod box;
|
||||
mod getopts;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue