Remove the fmt! syntax extension
It lived a good life, but its time has come. The groundwork is set for the official transition after the next snapshot (removal of XXX2 macros)
This commit is contained in:
parent
d773a024a2
commit
29c58c473f
|
@ -88,7 +88,7 @@ ifneq ($(wildcard $(NON_BUILD_TARGET_TRIPLES)),)
|
||||||
CFG_INFO := $(info cfg: non-build target triples $(NON_BUILD_TARGET_TRIPLES))
|
CFG_INFO := $(info cfg: non-build target triples $(NON_BUILD_TARGET_TRIPLES))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CFG_RUSTC_FLAGS := $(RUSTFLAGS) --cfg nofmt
|
CFG_RUSTC_FLAGS := $(RUSTFLAGS)
|
||||||
CFG_GCCISH_CFLAGS :=
|
CFG_GCCISH_CFLAGS :=
|
||||||
CFG_GCCISH_LINK_FLAGS :=
|
CFG_GCCISH_LINK_FLAGS :=
|
||||||
|
|
||||||
|
|
|
@ -1,703 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
//! Support for fmt! expressions.
|
|
||||||
//!
|
|
||||||
//! The syntax is close to that of Posix format strings:
|
|
||||||
//!
|
|
||||||
//! ~~~~~~
|
|
||||||
//! Format := '%' Parameter? Flag* Width? Precision? Type
|
|
||||||
//! Parameter := [0-9]+ '$'
|
|
||||||
//! Flag := [ 0#+-]
|
|
||||||
//! Width := Parameter | [0-9]+
|
|
||||||
//! Precision := '.' [0-9]+
|
|
||||||
//! Type := [bcdfiostuxX?]
|
|
||||||
//! ~~~~~~
|
|
||||||
//!
|
|
||||||
//! * Parameter is the 1-based argument to apply the format to. Currently not
|
|
||||||
//! implemented.
|
|
||||||
//! * Flag 0 causes leading zeros to be used for padding when converting
|
|
||||||
//! numbers.
|
|
||||||
//! * Flag # causes the conversion to be done in an *alternative* manner.
|
|
||||||
//! Currently not implemented.
|
|
||||||
//! * Flag + causes signed numbers to always be prepended with a sign
|
|
||||||
//! character.
|
|
||||||
//! * Flag - left justifies the result
|
|
||||||
//! * Width specifies the minimum field width of the result. By default
|
|
||||||
//! leading spaces are added.
|
|
||||||
//! * Precision specifies the minimum number of digits for integral types
|
|
||||||
//! and the minimum number
|
|
||||||
//! of decimal places for float.
|
|
||||||
//!
|
|
||||||
//! The types currently supported are:
|
|
||||||
//!
|
|
||||||
//! * b - bool
|
|
||||||
//! * c - char
|
|
||||||
//! * d - int
|
|
||||||
//! * f - float
|
|
||||||
//! * i - int (same as d)
|
|
||||||
//! * o - uint as octal
|
|
||||||
//! * t - uint as binary
|
|
||||||
//! * u - uint
|
|
||||||
//! * x - uint as lower-case hexadecimal
|
|
||||||
//! * X - uint as upper-case hexadecimal
|
|
||||||
//! * s - str (any flavor)
|
|
||||||
//! * ? - arbitrary type (does not use the to_str trait)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Syntax Extension: fmt
|
|
||||||
|
|
||||||
Format a string
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
debug!("hello, %s!", "world");
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
use prelude::*;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub mod ct {
|
|
||||||
use char;
|
|
||||||
use container::Container;
|
|
||||||
use prelude::*;
|
|
||||||
use str;
|
|
||||||
|
|
||||||
#[deriving(Eq)]
|
|
||||||
pub enum Signedness { Signed, Unsigned, }
|
|
||||||
|
|
||||||
#[deriving(Eq)]
|
|
||||||
pub enum Caseness { CaseUpper, CaseLower, }
|
|
||||||
|
|
||||||
#[deriving(Eq)]
|
|
||||||
pub enum Ty {
|
|
||||||
TyBool,
|
|
||||||
TyStr,
|
|
||||||
TyChar,
|
|
||||||
TyInt(Signedness),
|
|
||||||
TyBits,
|
|
||||||
TyHex(Caseness),
|
|
||||||
TyOctal,
|
|
||||||
TyFloat,
|
|
||||||
TyPointer,
|
|
||||||
TyPoly,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deriving(Eq)]
|
|
||||||
pub enum Flag {
|
|
||||||
FlagLeftJustify,
|
|
||||||
FlagLeftZeroPad,
|
|
||||||
FlagSpaceForSign,
|
|
||||||
FlagSignAlways,
|
|
||||||
FlagAlternate,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deriving(Eq)]
|
|
||||||
pub enum Count {
|
|
||||||
CountIs(uint),
|
|
||||||
CountIsParam(uint),
|
|
||||||
CountIsNextParam,
|
|
||||||
CountImplied,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deriving(Eq)]
|
|
||||||
struct Parsed<T> {
|
|
||||||
val: T,
|
|
||||||
next: uint
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Parsed<T> {
|
|
||||||
pub fn new(val: T, next: uint) -> Parsed<T> {
|
|
||||||
Parsed {val: val, next: next}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A formatted conversion from an expression to a string
|
|
||||||
#[deriving(Eq)]
|
|
||||||
pub struct Conv {
|
|
||||||
param: Option<uint>,
|
|
||||||
flags: ~[Flag],
|
|
||||||
width: Count,
|
|
||||||
precision: Count,
|
|
||||||
ty: Ty
|
|
||||||
}
|
|
||||||
|
|
||||||
// A fragment of the output sequence
|
|
||||||
#[deriving(Eq)]
|
|
||||||
pub enum Piece {
|
|
||||||
PieceString(~str),
|
|
||||||
PieceConv(Conv),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ErrorFn<'self> = &'self fn(&str) -> !;
|
|
||||||
|
|
||||||
pub fn parse_fmt_string<'a>(s: &str, err: ErrorFn<'a>) -> ~[Piece] {
|
|
||||||
fn push_slice(ps: &mut ~[Piece], s: &str, from: uint, to: uint) {
|
|
||||||
if to > from {
|
|
||||||
ps.push(PieceString(s.slice(from, to).to_owned()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let lim = s.len();
|
|
||||||
let mut h = 0;
|
|
||||||
let mut i = 0;
|
|
||||||
let mut pieces = ~[];
|
|
||||||
|
|
||||||
while i < lim {
|
|
||||||
if s[i] == '%' as u8 {
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
if i >= lim {
|
|
||||||
err("unterminated conversion at end of string");
|
|
||||||
} else if s[i] == '%' as u8 {
|
|
||||||
push_slice(&mut pieces, s, h, i);
|
|
||||||
i += 1;
|
|
||||||
} else {
|
|
||||||
push_slice(&mut pieces, s, h, i - 1);
|
|
||||||
let Parsed {
|
|
||||||
val,
|
|
||||||
next
|
|
||||||
} = parse_conversion(s, i, lim, |s| err(s));
|
|
||||||
pieces.push(val);
|
|
||||||
i = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
h = i;
|
|
||||||
} else {
|
|
||||||
i += str::utf8_char_width(s[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
push_slice(&mut pieces, s, h, i);
|
|
||||||
pieces
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn peek_num(s: &str, i: uint, lim: uint) -> Option<Parsed<uint>> {
|
|
||||||
let mut i = i;
|
|
||||||
let mut accum = 0;
|
|
||||||
let mut found = false;
|
|
||||||
|
|
||||||
while i < lim {
|
|
||||||
match char::to_digit(s[i] as char, 10) {
|
|
||||||
Some(x) => {
|
|
||||||
found = true;
|
|
||||||
accum *= 10;
|
|
||||||
accum += x;
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
None => break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if found {
|
|
||||||
Some(Parsed::new(accum, i))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_conversion<'a>(s: &str, i: uint, lim: uint, err: ErrorFn<'a>)
|
|
||||||
-> Parsed<Piece> {
|
|
||||||
let param = parse_parameter(s, i, lim);
|
|
||||||
// avoid copying ~[Flag] by destructuring
|
|
||||||
let Parsed {val: flags_val, next: flags_next} = parse_flags(s,
|
|
||||||
param.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, err);
|
|
||||||
|
|
||||||
Parsed::new(PieceConv(Conv {
|
|
||||||
param: param.val,
|
|
||||||
flags: flags_val,
|
|
||||||
width: width.val,
|
|
||||||
precision: prec.val,
|
|
||||||
ty: ty.val}), ty.next)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_parameter(s: &str, i: uint, lim: uint) ->
|
|
||||||
Parsed<Option<uint>> {
|
|
||||||
if i >= lim { return Parsed::new(None, i); }
|
|
||||||
|
|
||||||
match peek_num(s, i, lim) {
|
|
||||||
Some(num) if num.next < lim && s[num.next] == '$' as u8 =>
|
|
||||||
Parsed::new(Some(num.val), num.next + 1),
|
|
||||||
_ => Parsed::new(None, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_flags(s: &str, i: uint, lim: uint) -> Parsed<~[Flag]> {
|
|
||||||
let mut i = i;
|
|
||||||
let mut flags = ~[];
|
|
||||||
|
|
||||||
while i < lim {
|
|
||||||
let f = match s[i] as char {
|
|
||||||
'-' => FlagLeftJustify,
|
|
||||||
'0' => FlagLeftZeroPad,
|
|
||||||
' ' => FlagSpaceForSign,
|
|
||||||
'+' => FlagSignAlways,
|
|
||||||
'#' => FlagAlternate,
|
|
||||||
_ => break
|
|
||||||
};
|
|
||||||
|
|
||||||
flags.push(f);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Parsed::new(flags, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_count(s: &str, i: uint, lim: uint) -> Parsed<Count> {
|
|
||||||
if i >= lim {
|
|
||||||
Parsed::new(CountImplied, i)
|
|
||||||
} else if s[i] == '*' as u8 {
|
|
||||||
let param = parse_parameter(s, i + 1, lim);
|
|
||||||
let j = param.next;
|
|
||||||
|
|
||||||
match param.val {
|
|
||||||
None => Parsed::new(CountIsNextParam, j),
|
|
||||||
Some(n) => Parsed::new(CountIsParam(n), j)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match peek_num(s, i, lim) {
|
|
||||||
None => Parsed::new(CountImplied, i),
|
|
||||||
Some(num) => Parsed::new(CountIs(num.val), num.next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_precision(s: &str, i: uint, lim: uint) -> Parsed<Count> {
|
|
||||||
if i < lim && s[i] == '.' as u8 {
|
|
||||||
let count = parse_count(s, i + 1, lim);
|
|
||||||
|
|
||||||
// If there were no digits specified, i.e. the precision
|
|
||||||
// was ".", then the precision is 0
|
|
||||||
match count.val {
|
|
||||||
CountImplied => Parsed::new(CountIs(0), count.next),
|
|
||||||
_ => count
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Parsed::new(CountImplied, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_type<'a>(s: &str, i: uint, lim: uint, err: ErrorFn<'a>)
|
|
||||||
-> Parsed<Ty> {
|
|
||||||
if i >= lim { err("missing type in conversion"); }
|
|
||||||
|
|
||||||
// FIXME (#2249): Do we really want two signed types here?
|
|
||||||
// How important is it to be printf compatible?
|
|
||||||
let t = match s[i] as char {
|
|
||||||
'b' => TyBool,
|
|
||||||
's' => TyStr,
|
|
||||||
'c' => TyChar,
|
|
||||||
'd' | 'i' => TyInt(Signed),
|
|
||||||
'u' => TyInt(Unsigned),
|
|
||||||
'x' => TyHex(CaseLower),
|
|
||||||
'X' => TyHex(CaseUpper),
|
|
||||||
't' => TyBits,
|
|
||||||
'o' => TyOctal,
|
|
||||||
'f' => TyFloat,
|
|
||||||
'p' => TyPointer,
|
|
||||||
'?' => TyPoly,
|
|
||||||
_ => err(format!("unknown type in conversion: {}", s.char_at(i)))
|
|
||||||
};
|
|
||||||
|
|
||||||
Parsed::new(t, i + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn die(s: &str) -> ! { fail2!(s.to_owned()) }
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_count() {
|
|
||||||
fn test(s: &str, count: Count, next: uint) -> bool {
|
|
||||||
parse_count(s, 0, s.len()) == Parsed::new(count, next)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(test("", CountImplied, 0));
|
|
||||||
assert!(test("*", CountIsNextParam, 1));
|
|
||||||
assert!(test("*1", CountIsNextParam, 1));
|
|
||||||
assert!(test("*1$", CountIsParam(1), 3));
|
|
||||||
assert!(test("123", CountIs(123), 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_flags() {
|
|
||||||
fn pack(fs: &[Flag]) -> uint {
|
|
||||||
fs.iter().fold(0, |p, &f| p | (1 << f as uint))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test(s: &str, flags: &[Flag], next: uint) {
|
|
||||||
let f = parse_flags(s, 0, s.len());
|
|
||||||
assert_eq!(pack(f.val), pack(flags));
|
|
||||||
assert_eq!(f.next, next);
|
|
||||||
}
|
|
||||||
|
|
||||||
test("", [], 0);
|
|
||||||
test("!#-+ 0", [], 0);
|
|
||||||
test("#-+", [FlagAlternate, FlagLeftJustify, FlagSignAlways], 3);
|
|
||||||
test(" 0", [FlagSpaceForSign, FlagLeftZeroPad], 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_fmt_string() {
|
|
||||||
assert!(parse_fmt_string("foo %s bar", die) == ~[
|
|
||||||
PieceString(~"foo "),
|
|
||||||
PieceConv(Conv {
|
|
||||||
param: None,
|
|
||||||
flags: ~[],
|
|
||||||
width: CountImplied,
|
|
||||||
precision: CountImplied,
|
|
||||||
ty: TyStr,
|
|
||||||
}),
|
|
||||||
PieceString(~" bar")]);
|
|
||||||
|
|
||||||
assert!(parse_fmt_string("%s", die) == ~[
|
|
||||||
PieceConv(Conv {
|
|
||||||
param: None,
|
|
||||||
flags: ~[],
|
|
||||||
width: CountImplied,
|
|
||||||
precision: CountImplied,
|
|
||||||
ty: TyStr,
|
|
||||||
})]);
|
|
||||||
|
|
||||||
assert!(parse_fmt_string("%%%%", die) == ~[
|
|
||||||
PieceString(~"%"), PieceString(~"%")]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_parameter() {
|
|
||||||
fn test(s: &str, param: Option<uint>, next: uint) -> bool {
|
|
||||||
parse_parameter(s, 0, s.len()) == Parsed::new(param, next)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(test("", None, 0));
|
|
||||||
assert!(test("foo", None, 0));
|
|
||||||
assert!(test("123", None, 0));
|
|
||||||
assert!(test("123$", Some(123), 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_precision() {
|
|
||||||
fn test(s: &str, count: Count, next: uint) -> bool {
|
|
||||||
parse_precision(s, 0, s.len()) == Parsed::new(count, next)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(test("", CountImplied, 0));
|
|
||||||
assert!(test(".", CountIs(0), 1));
|
|
||||||
assert!(test(".*", CountIsNextParam, 2));
|
|
||||||
assert!(test(".*1", CountIsNextParam, 2));
|
|
||||||
assert!(test(".*1$", CountIsParam(1), 4));
|
|
||||||
assert!(test(".123", CountIs(123), 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_type() {
|
|
||||||
fn test(s: &str, ty: Ty) -> bool {
|
|
||||||
parse_type(s, 0, s.len(), die) == Parsed::new(ty, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(test("b", TyBool));
|
|
||||||
assert!(test("c", TyChar));
|
|
||||||
assert!(test("d", TyInt(Signed)));
|
|
||||||
assert!(test("f", TyFloat));
|
|
||||||
assert!(test("i", TyInt(Signed)));
|
|
||||||
assert!(test("o", TyOctal));
|
|
||||||
assert!(test("s", TyStr));
|
|
||||||
assert!(test("t", TyBits));
|
|
||||||
assert!(test("x", TyHex(CaseLower)));
|
|
||||||
assert!(test("X", TyHex(CaseUpper)));
|
|
||||||
assert!(test("p", TyPointer));
|
|
||||||
assert!(test("?", TyPoly));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_fail]
|
|
||||||
fn test_parse_type_missing() {
|
|
||||||
parse_type("", 0, 0, die);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_fail]
|
|
||||||
fn test_parse_type_unknown() {
|
|
||||||
parse_type("!", 0, 1, die);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_peek_num() {
|
|
||||||
let s1 = "";
|
|
||||||
assert!(peek_num(s1, 0, s1.len()).is_none());
|
|
||||||
|
|
||||||
let s2 = "foo";
|
|
||||||
assert!(peek_num(s2, 0, s2.len()).is_none());
|
|
||||||
|
|
||||||
let s3 = "123";
|
|
||||||
assert_eq!(peek_num(s3, 0, s3.len()), Some(Parsed::new(123, 3)));
|
|
||||||
|
|
||||||
let s4 = "123foo";
|
|
||||||
assert_eq!(peek_num(s4, 0, s4.len()), Some(Parsed::new(123, 3)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[allow(non_uppercase_statics)]
|
|
||||||
pub mod rt {
|
|
||||||
use f64;
|
|
||||||
use str;
|
|
||||||
use sys;
|
|
||||||
use num;
|
|
||||||
use vec;
|
|
||||||
use option::{Some, None, Option};
|
|
||||||
|
|
||||||
pub static flag_none : u32 = 0u32;
|
|
||||||
pub static flag_left_justify : u32 = 0b00000000000001u32;
|
|
||||||
pub static flag_left_zero_pad : u32 = 0b00000000000010u32;
|
|
||||||
pub static flag_space_for_sign : u32 = 0b00000000000100u32;
|
|
||||||
pub static flag_sign_always : u32 = 0b00000000001000u32;
|
|
||||||
pub static flag_alternate : u32 = 0b00000000010000u32;
|
|
||||||
|
|
||||||
pub enum Count { CountIs(uint), CountImplied, }
|
|
||||||
|
|
||||||
pub enum Ty { TyDefault, TyBits, TyHexUpper, TyHexLower, TyOctal, }
|
|
||||||
|
|
||||||
pub struct Conv {
|
|
||||||
flags: u32,
|
|
||||||
width: Count,
|
|
||||||
precision: Count,
|
|
||||||
ty: Ty,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn conv_int(cv: Conv, i: int, buf: &mut ~str) {
|
|
||||||
let radix = 10;
|
|
||||||
let prec = get_int_precision(cv);
|
|
||||||
let s : ~str = uint_to_str_prec(num::abs(i) as uint, radix, prec);
|
|
||||||
|
|
||||||
let head = if i >= 0 {
|
|
||||||
if have_flag(cv.flags, flag_sign_always) {
|
|
||||||
Some('+')
|
|
||||||
} else if have_flag(cv.flags, flag_space_for_sign) {
|
|
||||||
Some(' ')
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else { Some('-') };
|
|
||||||
pad(cv, s, head, PadSigned, buf);
|
|
||||||
}
|
|
||||||
pub fn conv_uint(cv: Conv, u: uint, buf: &mut ~str) {
|
|
||||||
let prec = get_int_precision(cv);
|
|
||||||
let rs =
|
|
||||||
match cv.ty {
|
|
||||||
TyDefault => uint_to_str_prec(u, 10, prec),
|
|
||||||
TyHexLower => uint_to_str_prec(u, 16, prec),
|
|
||||||
|
|
||||||
// FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
|
|
||||||
// to_ascii_move and to_str_move to not do a unnecessary copy.
|
|
||||||
TyHexUpper => {
|
|
||||||
let s = uint_to_str_prec(u, 16, prec);
|
|
||||||
s.to_ascii().to_upper().to_str_ascii()
|
|
||||||
}
|
|
||||||
TyBits => uint_to_str_prec(u, 2, prec),
|
|
||||||
TyOctal => uint_to_str_prec(u, 8, prec)
|
|
||||||
};
|
|
||||||
pad(cv, rs, None, PadUnsigned, buf);
|
|
||||||
}
|
|
||||||
pub fn conv_bool(cv: Conv, b: bool, buf: &mut ~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.
|
|
||||||
conv_str(cv, s, buf);
|
|
||||||
}
|
|
||||||
pub fn conv_char(cv: Conv, c: char, buf: &mut ~str) {
|
|
||||||
pad(cv, "", Some(c), PadNozero, buf);
|
|
||||||
}
|
|
||||||
pub fn conv_str(cv: Conv, s: &str, buf: &mut ~str) {
|
|
||||||
// For strings, precision is the maximum characters
|
|
||||||
// displayed
|
|
||||||
let unpadded = match cv.precision {
|
|
||||||
CountImplied => s,
|
|
||||||
CountIs(max) => {
|
|
||||||
if (max as uint) < s.char_len() {
|
|
||||||
s.slice(0, max as uint)
|
|
||||||
} else {
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pad(cv, unpadded, None, PadNozero, buf);
|
|
||||||
}
|
|
||||||
pub fn conv_float(cv: Conv, f: f64, buf: &mut ~str) {
|
|
||||||
let (to_str, digits) = match cv.precision {
|
|
||||||
CountIs(c) => (f64::to_str_exact, c as uint),
|
|
||||||
CountImplied => (f64::to_str_digits, 6u)
|
|
||||||
};
|
|
||||||
let s = to_str(f, digits);
|
|
||||||
let head = if 0.0 <= f {
|
|
||||||
if have_flag(cv.flags, flag_sign_always) {
|
|
||||||
Some('+')
|
|
||||||
} else if have_flag(cv.flags, flag_space_for_sign) {
|
|
||||||
Some(' ')
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else { None };
|
|
||||||
pad(cv, s, head, PadFloat, buf);
|
|
||||||
}
|
|
||||||
pub fn conv_pointer<T>(cv: Conv, ptr: *T, buf: &mut ~str) {
|
|
||||||
let s = ~"0x" + uint_to_str_prec(ptr as uint, 16, 1u);
|
|
||||||
pad(cv, s, None, PadNozero, buf);
|
|
||||||
}
|
|
||||||
pub fn conv_poly<T>(cv: Conv, v: &T, buf: &mut ~str) {
|
|
||||||
let s = sys::log_str(v);
|
|
||||||
conv_str(cv, s, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
pub fn uint_to_str_prec(num: uint, radix: uint, prec: uint) -> ~str {
|
|
||||||
return if prec == 0u && num == 0u {
|
|
||||||
~""
|
|
||||||
} else {
|
|
||||||
let s = num.to_str_radix(radix);
|
|
||||||
let len = s.char_len();
|
|
||||||
if len < prec {
|
|
||||||
let diff = prec - len;
|
|
||||||
let pad = str::from_chars(vec::from_elem(diff, '0'));
|
|
||||||
pad + s
|
|
||||||
} else { s }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub fn get_int_precision(cv: Conv) -> uint {
|
|
||||||
return match cv.precision {
|
|
||||||
CountIs(c) => c as uint,
|
|
||||||
CountImplied => 1u
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deriving(Eq)]
|
|
||||||
pub enum PadMode { PadSigned, PadUnsigned, PadNozero, PadFloat }
|
|
||||||
|
|
||||||
pub fn pad(cv: Conv, s: &str, head: Option<char>, mode: PadMode,
|
|
||||||
buf: &mut ~str) {
|
|
||||||
let headsize = match head { Some(_) => 1, _ => 0 };
|
|
||||||
let uwidth : uint = match cv.width {
|
|
||||||
CountImplied => {
|
|
||||||
for &c in head.iter() {
|
|
||||||
buf.push_char(c);
|
|
||||||
}
|
|
||||||
return buf.push_str(s);
|
|
||||||
}
|
|
||||||
CountIs(width) => { width as uint }
|
|
||||||
};
|
|
||||||
let strlen = s.char_len() + headsize;
|
|
||||||
if uwidth <= strlen {
|
|
||||||
for &c in head.iter() {
|
|
||||||
buf.push_char(c);
|
|
||||||
}
|
|
||||||
return buf.push_str(s);
|
|
||||||
}
|
|
||||||
let mut padchar = ' ';
|
|
||||||
let diff = uwidth - strlen;
|
|
||||||
if have_flag(cv.flags, flag_left_justify) {
|
|
||||||
for &c in head.iter() {
|
|
||||||
buf.push_char(c);
|
|
||||||
}
|
|
||||||
buf.push_str(s);
|
|
||||||
do diff.times {
|
|
||||||
buf.push_char(padchar);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let (might_zero_pad, signed) = match mode {
|
|
||||||
PadNozero => (false, true),
|
|
||||||
PadSigned => (true, true),
|
|
||||||
PadFloat => (true, true),
|
|
||||||
PadUnsigned => (true, false)
|
|
||||||
};
|
|
||||||
fn have_precision(cv: Conv) -> bool {
|
|
||||||
return match cv.precision { CountImplied => false, _ => true };
|
|
||||||
}
|
|
||||||
let zero_padding = {
|
|
||||||
if might_zero_pad && have_flag(cv.flags, flag_left_zero_pad) &&
|
|
||||||
(!have_precision(cv) || mode == PadFloat) {
|
|
||||||
padchar = '0';
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let padstr = str::from_chars(vec::from_elem(diff, padchar));
|
|
||||||
// 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 {
|
|
||||||
for &head in head.iter() {
|
|
||||||
if head == '+' || head == '-' || head == ' ' {
|
|
||||||
buf.push_char(head);
|
|
||||||
buf.push_str(padstr);
|
|
||||||
buf.push_str(s);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.push_str(padstr);
|
|
||||||
for &c in head.iter() {
|
|
||||||
buf.push_char(c);
|
|
||||||
}
|
|
||||||
buf.push_str(s);
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn have_flag(flags: u32, f: u32) -> bool {
|
|
||||||
flags & f != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bulk of the tests are in src/test/run-pass/syntax-extension-fmt.rs
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
#[test]
|
|
||||||
fn fmt_slice() {
|
|
||||||
let s = "abc";
|
|
||||||
let _s = format!("{}", s);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,7 +21,6 @@ pub mod dynamic_lib;
|
||||||
pub mod finally;
|
pub mod finally;
|
||||||
pub mod intrinsics;
|
pub mod intrinsics;
|
||||||
pub mod simd;
|
pub mod simd;
|
||||||
pub mod extfmt;
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
pub mod lang;
|
pub mod lang;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
|
|
|
@ -222,7 +222,7 @@ pub fn syntax_expander_table() -> SyntaxEnv {
|
||||||
span: None,
|
span: None,
|
||||||
} as @SyntaxExpanderTTItemTrait,
|
} as @SyntaxExpanderTTItemTrait,
|
||||||
None)));
|
None)));
|
||||||
syntax_expanders.insert(intern(&"oldfmt"),
|
syntax_expanders.insert(intern(&"fmt"),
|
||||||
builtin_normal_tt_no_ctxt(
|
builtin_normal_tt_no_ctxt(
|
||||||
ext::fmt::expand_syntax_ext));
|
ext::fmt::expand_syntax_ext));
|
||||||
syntax_expanders.insert(intern(&"format_args"),
|
syntax_expanders.insert(intern(&"format_args"),
|
||||||
|
|
|
@ -809,51 +809,7 @@ pub fn std_macros() -> @str {
|
||||||
|
|
||||||
macro_rules! ignore (($($x:tt)*) => (()))
|
macro_rules! ignore (($($x:tt)*) => (()))
|
||||||
|
|
||||||
#[cfg(not(nofmt))]
|
macro_rules! log(
|
||||||
mod fmt_extension {
|
|
||||||
#[macro_escape];
|
|
||||||
|
|
||||||
macro_rules! fmt(($($arg:tt)*) => (oldfmt!($($arg)*)))
|
|
||||||
|
|
||||||
macro_rules! log(
|
|
||||||
($lvl:expr, $arg:expr) => ({
|
|
||||||
let lvl = $lvl;
|
|
||||||
if lvl <= __log_level() {
|
|
||||||
format_args!(|args| {
|
|
||||||
::std::logging::log(lvl, args)
|
|
||||||
}, \"{}\", fmt!(\"%?\", $arg))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
($lvl:expr, $($arg:expr),+) => ({
|
|
||||||
let lvl = $lvl;
|
|
||||||
if lvl <= __log_level() {
|
|
||||||
format_args!(|args| {
|
|
||||||
::std::logging::log(lvl, args)
|
|
||||||
}, \"{}\", fmt!($($arg),+))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
macro_rules! error( ($($arg:tt)*) => (log!(1u32, $($arg)*)) )
|
|
||||||
macro_rules! warn ( ($($arg:tt)*) => (log!(2u32, $($arg)*)) )
|
|
||||||
macro_rules! info ( ($($arg:tt)*) => (log!(3u32, $($arg)*)) )
|
|
||||||
macro_rules! debug( ($($arg:tt)*) => (
|
|
||||||
if cfg!(not(ndebug)) { log!(4u32, $($arg)*) }
|
|
||||||
))
|
|
||||||
|
|
||||||
macro_rules! fail(
|
|
||||||
() => (
|
|
||||||
fail2!(\"explicit failure\")
|
|
||||||
);
|
|
||||||
($msg:expr) => (
|
|
||||||
::std::sys::FailWithCause::fail_with($msg, file!(), line!())
|
|
||||||
);
|
|
||||||
($( $arg:expr ),+) => (
|
|
||||||
::std::sys::FailWithCause::fail_with(fmt!( $($arg),+ ), file!(), line!())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! log2(
|
|
||||||
($lvl:expr, $($arg:tt)+) => ({
|
($lvl:expr, $($arg:tt)+) => ({
|
||||||
let lvl = $lvl;
|
let lvl = $lvl;
|
||||||
if lvl <= __log_level() {
|
if lvl <= __log_level() {
|
||||||
|
@ -863,16 +819,16 @@ pub fn std_macros() -> @str {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
macro_rules! error2( ($($arg:tt)*) => (log2!(1u32, $($arg)*)) )
|
macro_rules! error( ($($arg:tt)*) => (log!(1u32, $($arg)*)) )
|
||||||
macro_rules! warn2 ( ($($arg:tt)*) => (log2!(2u32, $($arg)*)) )
|
macro_rules! warn ( ($($arg:tt)*) => (log!(2u32, $($arg)*)) )
|
||||||
macro_rules! info2 ( ($($arg:tt)*) => (log2!(3u32, $($arg)*)) )
|
macro_rules! info ( ($($arg:tt)*) => (log!(3u32, $($arg)*)) )
|
||||||
macro_rules! debug2( ($($arg:tt)*) => (
|
macro_rules! debug( ($($arg:tt)*) => (
|
||||||
if cfg!(not(ndebug)) { log2!(4u32, $($arg)*) }
|
if cfg!(not(ndebug)) { log!(4u32, $($arg)*) }
|
||||||
))
|
))
|
||||||
|
|
||||||
macro_rules! fail2(
|
macro_rules! fail(
|
||||||
() => (
|
() => (
|
||||||
fail2!(\"explicit failure\")
|
fail!(\"explicit failure\")
|
||||||
);
|
);
|
||||||
($fmt:expr) => (
|
($fmt:expr) => (
|
||||||
::std::sys::FailWithCause::fail_with($fmt, file!(), line!())
|
::std::sys::FailWithCause::fail_with($fmt, file!(), line!())
|
||||||
|
@ -882,6 +838,14 @@ pub fn std_macros() -> @str {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NOTE (acrichto): remove these after the next snapshot
|
||||||
|
macro_rules! log2( ($($arg:tt)*) => (log!($($arg)*)) )
|
||||||
|
macro_rules! error2( ($($arg:tt)*) => (error!($($arg)*)) )
|
||||||
|
macro_rules! warn2 ( ($($arg:tt)*) => (warn!($($arg)*)) )
|
||||||
|
macro_rules! info2 ( ($($arg:tt)*) => (info!($($arg)*)) )
|
||||||
|
macro_rules! debug2( ($($arg:tt)*) => (debug!($($arg)*)) )
|
||||||
|
macro_rules! fail2( ($($arg:tt)*) => (fail!($($arg)*)) )
|
||||||
|
|
||||||
macro_rules! assert(
|
macro_rules! assert(
|
||||||
($cond:expr) => {
|
($cond:expr) => {
|
||||||
if !$cond {
|
if !$cond {
|
||||||
|
|
|
@ -8,320 +8,19 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
/*
|
/// Deprecated fmt! syntax extension
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use ast;
|
use ast;
|
||||||
use codemap::Span;
|
use codemap::Span;
|
||||||
use ext::base::*;
|
|
||||||
use ext::base;
|
use ext::base;
|
||||||
use ext::build::AstBuilder;
|
use ext::build::AstBuilder;
|
||||||
|
|
||||||
use std::option;
|
pub fn expand_syntax_ext(ecx: @base::ExtCtxt, sp: Span,
|
||||||
use std::unstable::extfmt::ct::*;
|
_tts: &[ast::token_tree]) -> base::MacResult {
|
||||||
use parse::token::{str_to_ident};
|
ecx.span_err(sp, "`fmt!` is deprecated, use `format!` instead");
|
||||||
|
ecx.parse_sess.span_diagnostic.span_note(sp,
|
||||||
|
"see http://static.rust-lang.org/doc/master/std/fmt/index.html \
|
||||||
|
for documentation");
|
||||||
|
|
||||||
pub fn expand_syntax_ext(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree])
|
base::MRExpr(ecx.expr_uint(sp, 2))
|
||||||
-> base::MacResult {
|
|
||||||
let args = get_exprs_from_tts(cx, sp, tts);
|
|
||||||
if args.len() == 0 {
|
|
||||||
cx.span_fatal(sp, "fmt! takes at least 1 argument.");
|
|
||||||
}
|
|
||||||
let (fmt, _fmt_str_style) =
|
|
||||||
expr_to_str(cx, args[0],
|
|
||||||
"first argument to fmt! must be a string literal.");
|
|
||||||
let fmtspan = args[0].span;
|
|
||||||
debug2!("Format string: {}", fmt);
|
|
||||||
fn parse_fmt_err_(cx: @ExtCtxt, sp: Span, msg: &str) -> ! {
|
|
||||||
cx.span_fatal(sp, msg);
|
|
||||||
}
|
|
||||||
let parse_fmt_err: &fn(&str) -> ! = |s| parse_fmt_err_(cx, fmtspan, s);
|
|
||||||
let pieces = parse_fmt_string(fmt, parse_fmt_err);
|
|
||||||
MRExpr(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: @ExtCtxt, sp: Span,
|
|
||||||
pieces: ~[Piece], args: ~[@ast::Expr])
|
|
||||||
-> @ast::Expr {
|
|
||||||
fn make_path_vec(ident: &str) -> ~[ast::Ident] {
|
|
||||||
return ~[str_to_ident("std"),
|
|
||||||
str_to_ident("unstable"),
|
|
||||||
str_to_ident("extfmt"),
|
|
||||||
str_to_ident("rt"),
|
|
||||||
str_to_ident(ident)];
|
|
||||||
}
|
|
||||||
fn make_rt_path_expr(cx: @ExtCtxt, sp: Span, nm: &str) -> @ast::Expr {
|
|
||||||
let path = make_path_vec(nm);
|
|
||||||
cx.expr_path(cx.path_global(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: @ExtCtxt, sp: Span, cnv: &Conv) -> @ast::Expr {
|
|
||||||
fn make_flags(cx: @ExtCtxt, sp: Span, flags: &[Flag]) -> @ast::Expr {
|
|
||||||
let mut tmp_expr = make_rt_path_expr(cx, sp, "flag_none");
|
|
||||||
for f in flags.iter() {
|
|
||||||
let fstr = match *f {
|
|
||||||
FlagLeftJustify => "flag_left_justify",
|
|
||||||
FlagLeftZeroPad => "flag_left_zero_pad",
|
|
||||||
FlagSpaceForSign => "flag_space_for_sign",
|
|
||||||
FlagSignAlways => "flag_sign_always",
|
|
||||||
FlagAlternate => "flag_alternate"
|
|
||||||
};
|
|
||||||
tmp_expr = cx.expr_binary(sp, ast::BiBitOr, tmp_expr,
|
|
||||||
make_rt_path_expr(cx, sp, fstr));
|
|
||||||
}
|
|
||||||
return tmp_expr;
|
|
||||||
}
|
|
||||||
fn make_count(cx: @ExtCtxt, sp: Span, cnt: Count) -> @ast::Expr {
|
|
||||||
match cnt {
|
|
||||||
CountImplied => {
|
|
||||||
return make_rt_path_expr(cx, sp, "CountImplied");
|
|
||||||
}
|
|
||||||
CountIs(c) => {
|
|
||||||
let count_lit = cx.expr_uint(sp, c as uint);
|
|
||||||
let count_is_path = make_path_vec("CountIs");
|
|
||||||
let count_is_args = ~[count_lit];
|
|
||||||
return cx.expr_call_global(sp, count_is_path, count_is_args);
|
|
||||||
}
|
|
||||||
_ => cx.span_unimpl(sp, "unimplemented fmt! conversion")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn make_ty(cx: @ExtCtxt, sp: Span, t: Ty) -> @ast::Expr {
|
|
||||||
let rt_type = match t {
|
|
||||||
TyHex(c) => match c {
|
|
||||||
CaseUpper => "TyHexUpper",
|
|
||||||
CaseLower => "TyHexLower"
|
|
||||||
},
|
|
||||||
TyBits => "TyBits",
|
|
||||||
TyOctal => "TyOctal",
|
|
||||||
_ => "TyDefault"
|
|
||||||
};
|
|
||||||
return make_rt_path_expr(cx, sp, rt_type);
|
|
||||||
}
|
|
||||||
fn make_conv_struct(cx: @ExtCtxt, sp: Span, flags_expr: @ast::Expr,
|
|
||||||
width_expr: @ast::Expr, precision_expr: @ast::Expr,
|
|
||||||
ty_expr: @ast::Expr) -> @ast::Expr {
|
|
||||||
cx.expr_struct(
|
|
||||||
sp,
|
|
||||||
cx.path_global(sp, make_path_vec("Conv")),
|
|
||||||
~[
|
|
||||||
cx.field_imm(sp, str_to_ident("flags"), flags_expr),
|
|
||||||
cx.field_imm(sp, str_to_ident("width"), width_expr),
|
|
||||||
cx.field_imm(sp, str_to_ident("precision"), precision_expr),
|
|
||||||
cx.field_imm(sp, str_to_ident("ty"), 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);
|
|
||||||
make_conv_struct(cx, sp, rt_conv_flags, rt_conv_width,
|
|
||||||
rt_conv_precision, rt_conv_ty)
|
|
||||||
}
|
|
||||||
fn make_conv_call(cx: @ExtCtxt, sp: Span, conv_type: &str, cnv: &Conv,
|
|
||||||
arg: @ast::Expr, buf: @ast::Expr) -> @ast::Expr {
|
|
||||||
let fname = ~"conv_" + conv_type;
|
|
||||||
let path = make_path_vec(fname);
|
|
||||||
let cnv_expr = make_rt_conv_expr(cx, sp, cnv);
|
|
||||||
let args = ~[cnv_expr, arg, buf];
|
|
||||||
cx.expr_call_global(arg.span, path, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_new_conv(cx: @ExtCtxt, sp: Span, cnv: &Conv,
|
|
||||||
arg: @ast::Expr, buf: @ast::Expr) -> @ast::Expr {
|
|
||||||
fn is_signed_type(cnv: &Conv) -> bool {
|
|
||||||
match cnv.ty {
|
|
||||||
TyInt(s) => match s {
|
|
||||||
Signed => return true,
|
|
||||||
Unsigned => return false
|
|
||||||
},
|
|
||||||
TyFloat => return true,
|
|
||||||
_ => return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let unsupported = ~"conversion not supported in fmt! string";
|
|
||||||
match cnv.param {
|
|
||||||
option::None => (),
|
|
||||||
_ => cx.span_unimpl(sp, unsupported)
|
|
||||||
}
|
|
||||||
for f in cnv.flags.iter() {
|
|
||||||
match *f {
|
|
||||||
FlagLeftJustify => (),
|
|
||||||
FlagSignAlways => {
|
|
||||||
if !is_signed_type(cnv) {
|
|
||||||
cx.span_fatal(sp,
|
|
||||||
"+ flag only valid in \
|
|
||||||
signed fmt! conversion");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FlagSpaceForSign => {
|
|
||||||
if !is_signed_type(cnv) {
|
|
||||||
cx.span_fatal(sp,
|
|
||||||
"space flag only valid in \
|
|
||||||
signed fmt! conversions");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FlagLeftZeroPad => (),
|
|
||||||
_ => cx.span_unimpl(sp, unsupported)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match cnv.width {
|
|
||||||
CountImplied => (),
|
|
||||||
CountIs(_) => (),
|
|
||||||
_ => cx.span_unimpl(sp, unsupported)
|
|
||||||
}
|
|
||||||
match cnv.precision {
|
|
||||||
CountImplied => (),
|
|
||||||
CountIs(_) => (),
|
|
||||||
_ => cx.span_unimpl(sp, unsupported)
|
|
||||||
}
|
|
||||||
let (name, actual_arg) = match cnv.ty {
|
|
||||||
TyStr => ("str", arg),
|
|
||||||
TyInt(Signed) => ("int", arg),
|
|
||||||
TyBool => ("bool", arg),
|
|
||||||
TyChar => ("char", arg),
|
|
||||||
TyBits | TyOctal | TyHex(_) | TyInt(Unsigned) => ("uint", arg),
|
|
||||||
TyFloat => ("float", arg),
|
|
||||||
TyPointer => ("pointer", arg),
|
|
||||||
TyPoly => ("poly", cx.expr_addr_of(sp, arg))
|
|
||||||
};
|
|
||||||
return make_conv_call(cx, arg.span, name, cnv, actual_arg,
|
|
||||||
cx.expr_mut_addr_of(arg.span, buf));
|
|
||||||
}
|
|
||||||
fn log_conv(c: &Conv) {
|
|
||||||
debug2!("Building conversion:");
|
|
||||||
match c.param {
|
|
||||||
Some(p) => { debug2!("param: {}", p.to_str()); }
|
|
||||||
_ => debug2!("param: none")
|
|
||||||
}
|
|
||||||
for f in c.flags.iter() {
|
|
||||||
match *f {
|
|
||||||
FlagLeftJustify => debug2!("flag: left justify"),
|
|
||||||
FlagLeftZeroPad => debug2!("flag: left zero pad"),
|
|
||||||
FlagSpaceForSign => debug2!("flag: left space pad"),
|
|
||||||
FlagSignAlways => debug2!("flag: sign always"),
|
|
||||||
FlagAlternate => debug2!("flag: alternate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match c.width {
|
|
||||||
CountIs(i) =>
|
|
||||||
debug2!("width: count is {}", i.to_str()),
|
|
||||||
CountIsParam(i) =>
|
|
||||||
debug2!("width: count is param {}", i.to_str()),
|
|
||||||
CountIsNextParam => debug2!("width: count is next param"),
|
|
||||||
CountImplied => debug2!("width: count is implied")
|
|
||||||
}
|
|
||||||
match c.precision {
|
|
||||||
CountIs(i) =>
|
|
||||||
debug2!("prec: count is {}", i.to_str()),
|
|
||||||
CountIsParam(i) =>
|
|
||||||
debug2!("prec: count is param {}", i.to_str()),
|
|
||||||
CountIsNextParam => debug2!("prec: count is next param"),
|
|
||||||
CountImplied => debug2!("prec: count is implied")
|
|
||||||
}
|
|
||||||
match c.ty {
|
|
||||||
TyBool => debug2!("type: bool"),
|
|
||||||
TyStr => debug2!("type: str"),
|
|
||||||
TyChar => debug2!("type: char"),
|
|
||||||
TyInt(s) => match s {
|
|
||||||
Signed => debug2!("type: signed"),
|
|
||||||
Unsigned => debug2!("type: unsigned")
|
|
||||||
},
|
|
||||||
TyBits => debug2!("type: bits"),
|
|
||||||
TyHex(cs) => match cs {
|
|
||||||
CaseUpper => debug2!("type: uhex"),
|
|
||||||
CaseLower => debug2!("type: lhex"),
|
|
||||||
},
|
|
||||||
TyOctal => debug2!("type: octal"),
|
|
||||||
TyFloat => debug2!("type: float"),
|
|
||||||
TyPointer => debug2!("type: pointer"),
|
|
||||||
TyPoly => debug2!("type: poly")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Short circuit an easy case up front (won't work otherwise) */
|
|
||||||
if pieces.len() == 0 {
|
|
||||||
return cx.expr_str_uniq(args[0].span, @"");
|
|
||||||
}
|
|
||||||
|
|
||||||
let fmt_sp = args[0].span;
|
|
||||||
let mut n = 0u;
|
|
||||||
let nargs = args.len();
|
|
||||||
|
|
||||||
/* 'ident' is the local buffer building up the result of fmt! */
|
|
||||||
let ident = str_to_ident("__fmtbuf");
|
|
||||||
let buf = || cx.expr_ident(fmt_sp, ident);
|
|
||||||
let core_ident = str_to_ident("std");
|
|
||||||
let str_ident = str_to_ident("str");
|
|
||||||
let push_ident = str_to_ident("push_str");
|
|
||||||
let mut stms = ~[];
|
|
||||||
|
|
||||||
/* Translate each piece (portion of the fmt expression) by invoking the
|
|
||||||
corresponding function in std::unstable::extfmt. Each function takes a
|
|
||||||
buffer to insert data into along with the data being formatted. */
|
|
||||||
let npieces = pieces.len();
|
|
||||||
for (i, pc) in pieces.move_iter().enumerate() {
|
|
||||||
match pc {
|
|
||||||
/* Raw strings get appended via str::push_str */
|
|
||||||
PieceString(s) => {
|
|
||||||
/* If this is the first portion, then initialize the local
|
|
||||||
buffer with it directly. If it's actually the only piece,
|
|
||||||
then there's no need for it to be mutable */
|
|
||||||
if i == 0 {
|
|
||||||
stms.push(cx.stmt_let(fmt_sp, npieces > 1,
|
|
||||||
ident, cx.expr_str_uniq(fmt_sp, s.to_managed())));
|
|
||||||
} else {
|
|
||||||
// we call the push_str function because the
|
|
||||||
// bootstrap doesnt't seem to work if we call the
|
|
||||||
// method.
|
|
||||||
let args = ~[cx.expr_mut_addr_of(fmt_sp, buf()),
|
|
||||||
cx.expr_str(fmt_sp, s.to_managed())];
|
|
||||||
let call = cx.expr_call_global(fmt_sp,
|
|
||||||
~[core_ident,
|
|
||||||
str_ident,
|
|
||||||
push_ident],
|
|
||||||
args);
|
|
||||||
stms.push(cx.stmt_expr(call));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invoke the correct conv function in extfmt */
|
|
||||||
PieceConv(ref conv) => {
|
|
||||||
n += 1u;
|
|
||||||
if n >= nargs {
|
|
||||||
cx.span_fatal(sp,
|
|
||||||
"not enough arguments to fmt! \
|
|
||||||
for the given format string");
|
|
||||||
}
|
|
||||||
|
|
||||||
log_conv(conv);
|
|
||||||
/* If the first portion is a conversion, then the local buffer
|
|
||||||
must be initialized as an empty string */
|
|
||||||
if i == 0 {
|
|
||||||
stms.push(cx.stmt_let(fmt_sp, true, ident,
|
|
||||||
cx.expr_str_uniq(fmt_sp, @"")));
|
|
||||||
}
|
|
||||||
stms.push(cx.stmt_expr(make_new_conv(cx, fmt_sp, conv,
|
|
||||||
args[n], buf())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let expected_nargs = n + 1u; // n conversions + the fmt string
|
|
||||||
if expected_nargs < nargs {
|
|
||||||
cx.span_fatal
|
|
||||||
(sp, format!("too many arguments to fmt!. found {}, expected {}",
|
|
||||||
nargs, expected_nargs));
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.expr_block(cx.block(fmt_sp, stms, Some(buf())))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// error-pattern:missing type
|
|
||||||
|
|
||||||
fn main() { oldfmt!("%+"); }
|
|
|
@ -1,13 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// error-pattern:fmt! takes at least 1 argument
|
|
||||||
|
|
||||||
fn main() { oldfmt!(); }
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// error-pattern: literal
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// fmt!'s first argument must be a literal. Hopefully this
|
|
||||||
// restriction can be eased eventually to just require a
|
|
||||||
// compile-time constant.
|
|
||||||
let x = oldfmt!("a" + "b");
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// error-pattern: literal
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// fmt!'s first argument must be a literal. Hopefully this
|
|
||||||
// restriction can be eased eventually to just require a
|
|
||||||
// compile-time constant.
|
|
||||||
let x = oldfmt!(20);
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// error-pattern:not enough arguments
|
|
||||||
|
|
||||||
extern mod extra;
|
|
||||||
|
|
||||||
fn main() { let s = oldfmt!("%s%s%s", "test", "test"); }
|
|
|
@ -1,15 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// error-pattern:too many arguments
|
|
||||||
|
|
||||||
extern mod extra;
|
|
||||||
|
|
||||||
fn main() { let s = oldfmt!("%s", "test", "test"); }
|
|
|
@ -1,13 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// error-pattern:unknown type
|
|
||||||
|
|
||||||
fn main() { oldfmt!("%w"); }
|
|
|
@ -1,16 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// error-pattern:only valid in signed fmt! conversion
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Can't use a sign on unsigned conversions
|
|
||||||
oldfmt!("%+u", 10u);
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// error-pattern:only valid in signed fmt! conversion
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// Can't use a space on unsigned conversions
|
|
||||||
oldfmt!("% u", 10u);
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// error-pattern:unterminated conversion
|
|
||||||
|
|
||||||
fn main() { oldfmt!("%"); }
|
|
|
@ -1,34 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// Testing that calling fmt! (via info2!) doesn't complain about impure borrows
|
|
||||||
|
|
||||||
struct Big { b: @~str, c: uint, d: int, e: char,
|
|
||||||
f: f64, g: bool }
|
|
||||||
|
|
||||||
fn foo() {
|
|
||||||
let a = Big {
|
|
||||||
b: @~"hi",
|
|
||||||
c: 0,
|
|
||||||
d: 1,
|
|
||||||
e: 'a',
|
|
||||||
f: 0.0,
|
|
||||||
g: true
|
|
||||||
};
|
|
||||||
info2!("test {:?}", a.b);
|
|
||||||
info2!("test {:u}", a.c);
|
|
||||||
info2!("test {:i}", a.d);
|
|
||||||
info2!("test {:c}", a.e);
|
|
||||||
info2!("test {:f}", a.f);
|
|
||||||
info2!("test {:b}", a.g);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
}
|
|
|
@ -1,282 +0,0 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
#[feature(macro_rules)];
|
|
||||||
|
|
||||||
// compile-flags: --cfg nofmt
|
|
||||||
|
|
||||||
extern mod extra;
|
|
||||||
|
|
||||||
macro_rules! fmt(($($arg:tt)*) => (oldfmt!($($arg)*)))
|
|
||||||
|
|
||||||
fn test(actual: ~str, expected: ~str) {
|
|
||||||
info2!("{}", actual.clone());
|
|
||||||
info2!("{}", expected.clone());
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
test(fmt!("hello %d friends and %s things", 10, "formatted"),
|
|
||||||
~"hello 10 friends and formatted things");
|
|
||||||
|
|
||||||
test(fmt!("test"), ~"test");
|
|
||||||
|
|
||||||
// a quadratic optimization in LLVM (jump-threading) makes this test a
|
|
||||||
// bit slow to compile unless we break it up
|
|
||||||
part1();
|
|
||||||
part2();
|
|
||||||
part3();
|
|
||||||
part4();
|
|
||||||
part5();
|
|
||||||
part6();
|
|
||||||
percent();
|
|
||||||
more_floats();
|
|
||||||
pointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn part1() {
|
|
||||||
// Simple tests for types
|
|
||||||
|
|
||||||
test(fmt!("%d", 1), ~"1");
|
|
||||||
test(fmt!("%i", 2), ~"2");
|
|
||||||
test(fmt!("%i", -1), ~"-1");
|
|
||||||
test(fmt!("%u", 10u), ~"10");
|
|
||||||
test(fmt!("%s", "test"), ~"test");
|
|
||||||
test(fmt!("%b", true), ~"true");
|
|
||||||
test(fmt!("%b", false), ~"false");
|
|
||||||
test(fmt!("%c", 'A'), ~"A");
|
|
||||||
test(fmt!("%x", 0xff_u), ~"ff");
|
|
||||||
test(fmt!("%X", 0x12ab_u), ~"12AB");
|
|
||||||
test(fmt!("%o", 10u), ~"12");
|
|
||||||
test(fmt!("%t", 0b11010101_u), ~"11010101");
|
|
||||||
test(fmt!("%f", 5.82), ~"5.82");
|
|
||||||
// 32-bit limits
|
|
||||||
|
|
||||||
test(fmt!("%i", -2147483648), ~"-2147483648");
|
|
||||||
test(fmt!("%i", 2147483647), ~"2147483647");
|
|
||||||
test(fmt!("%u", 4294967295u), ~"4294967295");
|
|
||||||
test(fmt!("%x", 0xffffffff_u), ~"ffffffff");
|
|
||||||
test(fmt!("%o", 0xffffffff_u), ~"37777777777");
|
|
||||||
test(fmt!("%t", 0xffffffff_u), ~"11111111111111111111111111111111");
|
|
||||||
|
|
||||||
// Don't result in a compilation error
|
|
||||||
test(fmt!(""), ~"");
|
|
||||||
}
|
|
||||||
fn part2() {
|
|
||||||
// Widths
|
|
||||||
|
|
||||||
test(fmt!("%1d", 500), ~"500");
|
|
||||||
test(fmt!("%10d", 500), ~" 500");
|
|
||||||
test(fmt!("%10d", -500), ~" -500");
|
|
||||||
test(fmt!("%10u", 500u), ~" 500");
|
|
||||||
test(fmt!("%10s", "test"), ~" test");
|
|
||||||
test(fmt!("%10b", true), ~" true");
|
|
||||||
test(fmt!("%10x", 0xff_u), ~" ff");
|
|
||||||
test(fmt!("%10X", 0xff_u), ~" FF");
|
|
||||||
test(fmt!("%10o", 10u), ~" 12");
|
|
||||||
test(fmt!("%10t", 0xff_u), ~" 11111111");
|
|
||||||
test(fmt!("%10c", 'A'), ~" A");
|
|
||||||
test(fmt!("%10f", 5.82), ~" 5.82");
|
|
||||||
// Left justify
|
|
||||||
|
|
||||||
test(fmt!("%-10d", 500), ~"500 ");
|
|
||||||
test(fmt!("%-10d", -500), ~"-500 ");
|
|
||||||
test(fmt!("%-10u", 500u), ~"500 ");
|
|
||||||
test(fmt!("%-10s", "test"), ~"test ");
|
|
||||||
test(fmt!("%-10b", true), ~"true ");
|
|
||||||
test(fmt!("%-10x", 0xff_u), ~"ff ");
|
|
||||||
test(fmt!("%-10X", 0xff_u), ~"FF ");
|
|
||||||
test(fmt!("%-10o", 10u), ~"12 ");
|
|
||||||
test(fmt!("%-10t", 0xff_u), ~"11111111 ");
|
|
||||||
test(fmt!("%-10c", 'A'), ~"A ");
|
|
||||||
test(fmt!("%-10f", 5.82), ~"5.82 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn part3() {
|
|
||||||
// Precision
|
|
||||||
|
|
||||||
test(fmt!("%.d", 0), ~"");
|
|
||||||
test(fmt!("%.u", 0u), ~"");
|
|
||||||
test(fmt!("%.x", 0u), ~"");
|
|
||||||
test(fmt!("%.t", 0u), ~"");
|
|
||||||
test(fmt!("%.d", 10), ~"10");
|
|
||||||
test(fmt!("%.d", -10), ~"-10");
|
|
||||||
test(fmt!("%.u", 10u), ~"10");
|
|
||||||
test(fmt!("%.s", "test"), ~"");
|
|
||||||
test(fmt!("%.x", 127u), ~"7f");
|
|
||||||
test(fmt!("%.o", 10u), ~"12");
|
|
||||||
test(fmt!("%.t", 3u), ~"11");
|
|
||||||
test(fmt!("%.c", 'A'), ~"A");
|
|
||||||
test(fmt!("%.f", 5.82), ~"6");
|
|
||||||
test(fmt!("%.0d", 0), ~"");
|
|
||||||
test(fmt!("%.0u", 0u), ~"");
|
|
||||||
test(fmt!("%.0x", 0u), ~"");
|
|
||||||
test(fmt!("%.0t", 0u), ~"");
|
|
||||||
test(fmt!("%.0d", 10), ~"10");
|
|
||||||
test(fmt!("%.0d", -10), ~"-10");
|
|
||||||
test(fmt!("%.0u", 10u), ~"10");
|
|
||||||
test(fmt!("%.0s", "test"), ~"");
|
|
||||||
test(fmt!("%.0x", 127u), ~"7f");
|
|
||||||
test(fmt!("%.0o", 10u), ~"12");
|
|
||||||
test(fmt!("%.0t", 3u), ~"11");
|
|
||||||
test(fmt!("%.0c", 'A'), ~"A");
|
|
||||||
test(fmt!("%.0f", 5.892), ~"6");
|
|
||||||
test(fmt!("%.1d", 0), ~"0");
|
|
||||||
test(fmt!("%.1u", 0u), ~"0");
|
|
||||||
test(fmt!("%.1x", 0u), ~"0");
|
|
||||||
test(fmt!("%.1t", 0u), ~"0");
|
|
||||||
test(fmt!("%.1d", 10), ~"10");
|
|
||||||
test(fmt!("%.1d", -10), ~"-10");
|
|
||||||
test(fmt!("%.1u", 10u), ~"10");
|
|
||||||
test(fmt!("%.1s", "test"), ~"t");
|
|
||||||
test(fmt!("%.1x", 127u), ~"7f");
|
|
||||||
test(fmt!("%.1o", 10u), ~"12");
|
|
||||||
test(fmt!("%.1t", 3u), ~"11");
|
|
||||||
test(fmt!("%.1c", 'A'), ~"A");
|
|
||||||
test(fmt!("%.1f", 5.82), ~"5.8");
|
|
||||||
}
|
|
||||||
fn part4() {
|
|
||||||
test(fmt!("%.5d", 0), ~"00000");
|
|
||||||
test(fmt!("%.5u", 0u), ~"00000");
|
|
||||||
test(fmt!("%.5x", 0u), ~"00000");
|
|
||||||
test(fmt!("%.5t", 0u), ~"00000");
|
|
||||||
test(fmt!("%.5d", 10), ~"00010");
|
|
||||||
test(fmt!("%.5d", -10), ~"-00010");
|
|
||||||
test(fmt!("%.5u", 10u), ~"00010");
|
|
||||||
test(fmt!("%.5s", "test"), ~"test");
|
|
||||||
test(fmt!("%.5x", 127u), ~"0007f");
|
|
||||||
test(fmt!("%.5o", 10u), ~"00012");
|
|
||||||
test(fmt!("%.5t", 3u), ~"00011");
|
|
||||||
test(fmt!("%.5c", 'A'), ~"A");
|
|
||||||
test(fmt!("%.5f", 5.82), ~"5.82000");
|
|
||||||
test(fmt!("%.5f", 5.0), ~"5.00000");
|
|
||||||
test(fmt!("%.100f", 1.1), ~"1.1000000000000000888178419700125232338905334472656250000000000000000000000000000000000000000000000000");
|
|
||||||
|
|
||||||
// Bool precision. I'm not sure if it's good or bad to have bool
|
|
||||||
// conversions support precision - it's not standard printf so we
|
|
||||||
// can do whatever. For now I'm making it behave the same as string
|
|
||||||
// conversions.
|
|
||||||
|
|
||||||
test(fmt!("%.b", true), ~"");
|
|
||||||
test(fmt!("%.0b", true), ~"");
|
|
||||||
test(fmt!("%.1b", true), ~"t");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn part5() {
|
|
||||||
// Explicit + sign. Only for signed conversions
|
|
||||||
|
|
||||||
test(fmt!("%+d", 0), ~"+0");
|
|
||||||
test(fmt!("%+d", 1), ~"+1");
|
|
||||||
test(fmt!("%+d", -1), ~"-1");
|
|
||||||
test(fmt!("%+f", 0.0), ~"+0");
|
|
||||||
// Leave space for sign
|
|
||||||
|
|
||||||
test(fmt!("% d", 0), ~" 0");
|
|
||||||
test(fmt!("% d", 1), ~" 1");
|
|
||||||
test(fmt!("% d", -1), ~"-1");
|
|
||||||
test(fmt!("% f", 0.0), ~" 0");
|
|
||||||
// Plus overrides space
|
|
||||||
|
|
||||||
test(fmt!("% +d", 0), ~"+0");
|
|
||||||
test(fmt!("%+ d", 0), ~"+0");
|
|
||||||
test(fmt!("% +f", 0.0), ~"+0");
|
|
||||||
test(fmt!("%+ f", 0.0), ~"+0");
|
|
||||||
// 0-padding
|
|
||||||
|
|
||||||
test(fmt!("%05d", 0), ~"00000");
|
|
||||||
test(fmt!("%05d", 1), ~"00001");
|
|
||||||
test(fmt!("%05d", -1), ~"-0001");
|
|
||||||
test(fmt!("%05u", 1u), ~"00001");
|
|
||||||
test(fmt!("%05x", 127u), ~"0007f");
|
|
||||||
test(fmt!("%05X", 127u), ~"0007F");
|
|
||||||
test(fmt!("%05o", 10u), ~"00012");
|
|
||||||
test(fmt!("%05t", 3u), ~"00011");
|
|
||||||
test(fmt!("%05f", 5.82), ~"05.82");
|
|
||||||
// 0-padding a string is undefined but glibc does this:
|
|
||||||
|
|
||||||
test(fmt!("%05s", "test"), ~" test");
|
|
||||||
test(fmt!("%05c", 'A'), ~" A");
|
|
||||||
test(fmt!("%05b", true), ~" true");
|
|
||||||
// Left-justify overrides 0-padding
|
|
||||||
|
|
||||||
test(fmt!("%-05d", 0), ~"0 ");
|
|
||||||
test(fmt!("%-05d", 1), ~"1 ");
|
|
||||||
test(fmt!("%-05d", -1), ~"-1 ");
|
|
||||||
test(fmt!("%-05u", 1u), ~"1 ");
|
|
||||||
test(fmt!("%-05x", 127u), ~"7f ");
|
|
||||||
test(fmt!("%-05X", 127u), ~"7F ");
|
|
||||||
test(fmt!("%-05o", 10u), ~"12 ");
|
|
||||||
test(fmt!("%-05t", 3u), ~"11 ");
|
|
||||||
test(fmt!("%-05s", "test"), ~"test ");
|
|
||||||
test(fmt!("%-05c", 'A'), ~"A ");
|
|
||||||
test(fmt!("%-05b", true), ~"true ");
|
|
||||||
test(fmt!("%-05f", 5.82), ~"5.82 ");
|
|
||||||
}
|
|
||||||
fn part6() {
|
|
||||||
// Precision overrides 0-padding
|
|
||||||
// FIXME #2481: Recent gcc's report some of these as warnings
|
|
||||||
|
|
||||||
test(fmt!("%06.5d", 0), ~" 00000");
|
|
||||||
test(fmt!("%06.5u", 0u), ~" 00000");
|
|
||||||
test(fmt!("%06.5x", 0u), ~" 00000");
|
|
||||||
test(fmt!("%06.5d", 10), ~" 00010");
|
|
||||||
test(fmt!("%06.5d", -10), ~"-00010");
|
|
||||||
test(fmt!("%06.5u", 10u), ~" 00010");
|
|
||||||
test(fmt!("%06.5s", "test"), ~" test");
|
|
||||||
test(fmt!("%06.5c", 'A'), ~" A");
|
|
||||||
test(fmt!("%06.5x", 127u), ~" 0007f");
|
|
||||||
test(fmt!("%06.5X", 127u), ~" 0007F");
|
|
||||||
test(fmt!("%06.5o", 10u), ~" 00012");
|
|
||||||
|
|
||||||
// Precision does not override zero-padding for floats
|
|
||||||
test(fmt!("%08.5f", 5.82), ~"05.82000");
|
|
||||||
|
|
||||||
// Signed combinations
|
|
||||||
|
|
||||||
test(fmt!("% 5d", 1), ~" 1");
|
|
||||||
test(fmt!("% 5d", -1), ~" -1");
|
|
||||||
test(fmt!("%+5d", 1), ~" +1");
|
|
||||||
test(fmt!("%+5d", -1), ~" -1");
|
|
||||||
test(fmt!("% 05d", 1), ~" 0001");
|
|
||||||
test(fmt!("% 05d", -1), ~"-0001");
|
|
||||||
test(fmt!("%+05d", 1), ~"+0001");
|
|
||||||
test(fmt!("%+05d", -1), ~"-0001");
|
|
||||||
test(fmt!("%- 5d", 1), ~" 1 ");
|
|
||||||
test(fmt!("%- 5d", -1), ~"-1 ");
|
|
||||||
test(fmt!("%-+5d", 1), ~"+1 ");
|
|
||||||
test(fmt!("%-+5d", -1), ~"-1 ");
|
|
||||||
test(fmt!("%- 05d", 1), ~" 1 ");
|
|
||||||
test(fmt!("%- 05d", -1), ~"-1 ");
|
|
||||||
test(fmt!("%-+05d", 1), ~"+1 ");
|
|
||||||
test(fmt!("%-+05d", -1), ~"-1 ");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn percent() {
|
|
||||||
let s = fmt!("ab%%cd");
|
|
||||||
assert_eq!(s, ~"ab%cd");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn more_floats() {
|
|
||||||
assert_eq!(~"3.1416", fmt!("%.4f", 3.14159));
|
|
||||||
assert_eq!(~"3", fmt!("%.0f", 3.14159));
|
|
||||||
assert_eq!(~"99", fmt!("%.0f", 98.5));
|
|
||||||
assert_eq!(~"7.0000", fmt!("%.4f", 6.999999999));
|
|
||||||
assert_eq!(~"3.141590000", fmt!("%.9f", 3.14159));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pointer() {
|
|
||||||
do 10.times {
|
|
||||||
let x: uint = ::std::rand::random();
|
|
||||||
assert_eq!(fmt!("%p", x as *uint), fmt!("0x%x", x));
|
|
||||||
}
|
|
||||||
|
|
||||||
let i = &1;
|
|
||||||
assert_eq!(fmt!("%p", i), fmt!("0x%x", i as *uint as uint));
|
|
||||||
}
|
|
Loading…
Reference in New Issue