From 1a2a8975bc92b0ebae90d7d9a0cfffec08ec5cf4 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 14:17:48 -0500 Subject: [PATCH 01/12] fmt: replace Conv record with struct Conv --- src/libcore/extfmt.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index 5d7e0306b064b..5033b9ae40581 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -130,12 +130,12 @@ pub mod ct { } // A formatted conversion from an expression to a string - pub type Conv = + pub struct Conv {param: Option, flags: ~[Flag], width: Count, precision: Count, - ty: Ty}; + ty: Ty} // A fragment of the output sequence @@ -208,7 +208,7 @@ pub mod ct { let prec = parse_precision(s, width.next, lim); let ty = parse_type(s, prec.next, lim, err); return {piece: - PieceConv({param: parm.param, + PieceConv(Conv {param: parm.param, flags: copy flags.flags, width: width.count, precision: prec.count, From 0a17ab2a9cb4a9c1ce34da670127e45c70e2b99f Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 14:30:34 -0500 Subject: [PATCH 02/12] fmt: replace parse_* structural records with struct Parsed --- src/libcore/extfmt.rs | 91 ++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 40 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index 5033b9ae40581..98cd9910f50a0 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -129,6 +129,17 @@ pub mod ct { CountImplied, } + struct Parsed { + val: T, + next: uint + } + + impl Parsed { + static pure fn new(val: T, next: uint) -> Parsed { + Parsed { val: val, next: next } + } + } + // A formatted conversion from an expression to a string pub struct Conv {param: Option, @@ -169,7 +180,7 @@ pub mod ct { } else { buf = flush_buf(move buf, &mut pieces); let rs = parse_conversion(s, i, lim, err); - pieces.push(copy rs.piece); + pieces.push(copy rs.val); i = rs.next; } } else { buf += curr; i += size; } @@ -178,7 +189,7 @@ pub mod ct { move pieces } pub fn peek_num(s: &str, i: uint, lim: uint) -> - Option<{num: uint, next: uint}> { + Option> { let mut j = i; let mut accum = 0u; let mut found = false; @@ -194,54 +205,54 @@ pub mod ct { } } if found { - Some({num: accum, next: j}) + Some(Parsed::new(accum, j)) } else { None } } pub fn parse_conversion(s: &str, i: uint, lim: uint, err: ErrorFn) -> - {piece: Piece, next: uint} { + Parsed { 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, err); - return {piece: - PieceConv(Conv {param: parm.param, - flags: copy flags.flags, - width: width.count, - precision: prec.count, - ty: ty.ty}), - next: ty.next}; + return Parsed::new( + PieceConv(Conv {param: parm.val, + flags: copy flags.val, + width: width.val, + precision: prec.val, + ty: ty.val}), + ty.next); } pub fn parse_parameter(s: &str, i: uint, lim: uint) -> - {param: Option, next: uint} { - if i >= lim { return {param: None, next: i}; } + Parsed> { + if i >= lim { return Parsed::new(None, i); } let num = peek_num(s, i, lim); return match num { - None => {param: None, next: i}, + None => Parsed::new(None, i), Some(t) => { - let n = t.num; + let n = t.val; let j = t.next; if j < lim && s[j] == '$' as u8 { - {param: Some(n), next: j + 1} - } else { {param: None, next: i} } + Parsed::new(Some(n), j + 1) + } else { Parsed::new(None, i) } } }; } pub fn parse_flags(s: &str, i: uint, lim: uint) -> - {flags: ~[Flag], next: uint} { + Parsed<~[Flag]> { let noflags: ~[Flag] = ~[]; - if i >= lim { return {flags: move noflags, next: i}; } + if i >= lim { return Parsed::new(move noflags, i); } fn more(f: Flag, s: &str, i: uint, lim: uint) -> - {flags: ~[Flag], next: uint} { + Parsed<~[Flag]> { let next = parse_flags(s, i + 1u, lim); - let rest = copy next.flags; + let rest = copy next.val; let j = next.next; let curr: ~[Flag] = ~[f]; - return {flags: vec::append(move curr, rest), next: j}; + return Parsed::new(vec::append(move curr, rest), j); } // Unfortunate, but because s is borrowed, can't use a closure // fn more(f: Flag, s: &str) { more_(f, s, i, lim); } @@ -256,48 +267,48 @@ pub mod ct { more(FlagSignAlways, s, i, lim) } else if f == '#' as u8 { more(FlagAlternate, s, i, lim) - } else { {flags: move noflags, next: i} }; + } else { Parsed::new(move noflags, i) }; } pub fn parse_count(s: &str, i: uint, lim: uint) - -> {count: Count, next: uint} { + -> Parsed { return if i >= lim { - {count: CountImplied, next: i} + Parsed::new(CountImplied, i) } else if s[i] == '*' as u8 { let param = parse_parameter(s, i + 1, lim); let j = param.next; - match param.param { - None => {count: CountIsNextParam, next: j}, - Some(n) => {count: CountIsParam(n), next: j} + match param.val { + None => Parsed::new(CountIsNextParam, j), + Some(n) => Parsed::new(CountIsParam(n), j) } } else { let num = peek_num(s, i, lim); match num { - None => {count: CountImplied, next: i}, - Some(num) => { - count: CountIs(num.num), - next: num.next - } + None => Parsed::new(CountImplied, i), + Some(num) => Parsed::new( + CountIs(num.val), + num.next + ) } }; } pub fn parse_precision(s: &str, i: uint, lim: uint) -> - {count: Count, next: uint} { + Parsed { return if i >= lim { - {count: CountImplied, next: i} + Parsed::new(CountImplied, 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 - match count.count { - CountImplied => {count: CountIs(0), next: count.next}, + match count.val { + CountImplied => Parsed::new(CountIs(0), count.next), _ => count } - } else { {count: CountImplied, next: i} }; + } else { Parsed::new(CountImplied, i) }; } pub fn parse_type(s: &str, i: uint, lim: uint, err: ErrorFn) -> - {ty: Ty, next: uint} { + Parsed { if i >= lim { err(~"missing type in conversion"); } let tstr = str::slice(s, i, i+1u); // FIXME (#2249): Do we really want two signed types here? @@ -326,7 +337,7 @@ pub mod ct { } else if tstr == ~"?" { TyPoly } else { err(~"unknown type in conversion: " + tstr) }; - return {ty: t, next: i + 1u}; + return Parsed::new(t, i + 1u); } } From 51b141e3e499558ddfece1f00dd6714f442c2404 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 14:36:46 -0500 Subject: [PATCH 03/12] fmt: simplify parse_type --- src/libcore/extfmt.rs | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index 98cd9910f50a0..3cc7e2458b25c 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -310,34 +310,23 @@ pub mod ct { pub fn parse_type(s: &str, i: uint, lim: uint, err: ErrorFn) -> Parsed { if i >= lim { err(~"missing type in conversion"); } - let tstr = str::slice(s, i, i+1u); // FIXME (#2249): Do we really want two signed types here? // How important is it to be printf compatible? - let t = - if tstr == ~"b" { - TyBool - } else if tstr == ~"s" { - TyStr - } else if tstr == ~"c" { - TyChar - } else if tstr == ~"d" || tstr == ~"i" { - TyInt(Signed) - } else if tstr == ~"u" { - TyInt(Unsigned) - } else if tstr == ~"x" { - TyHex(CaseLower) - } else if tstr == ~"X" { - TyHex(CaseUpper) - } else if tstr == ~"t" { - TyBits - } else if tstr == ~"o" { - TyOctal - } else if tstr == ~"f" { - TyFloat - } else if tstr == ~"?" { - TyPoly - } else { err(~"unknown type in conversion: " + tstr) }; - return Parsed::new(t, i + 1u); + let t = match s[i] { + 'b' as u8 => TyBool, + 's' as u8 => TyStr, + 'c' as u8 => TyChar, + 'd' as u8 | 'i' as u8 => TyInt(Signed), + 'u' as u8 => TyInt(Unsigned), + 'x' as u8 => TyHex(CaseLower), + 'X' as u8 => TyHex(CaseUpper), + 't' as u8 => TyBits, + 'o' as u8 => TyOctal, + 'f' as u8 => TyFloat, + '?' as u8 => TyPoly, + _ => err(~"unknown type in conversion: " + s.substr(i, 1)) + }; + Parsed::new(t, i + 1) } } From f4bc9e6d57c1e005c6539db59762e4030bd7bd2f Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 14:39:45 -0500 Subject: [PATCH 04/12] fmt: simplify parse_flags --- src/libcore/extfmt.rs | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index 3cc7e2458b25c..25e349f0dc321 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -243,31 +243,24 @@ pub mod ct { } pub fn parse_flags(s: &str, i: uint, lim: uint) -> Parsed<~[Flag]> { - let noflags: ~[Flag] = ~[]; - if i >= lim { return Parsed::new(move noflags, i); } - - fn more(f: Flag, s: &str, i: uint, lim: uint) -> - Parsed<~[Flag]> { - let next = parse_flags(s, i + 1u, lim); - let rest = copy next.val; - let j = next.next; - let curr: ~[Flag] = ~[f]; - return Parsed::new(vec::append(move curr, rest), j); + let mut i = i; + let mut flags = ~[]; + + while i < lim { + let f = match s[i] { + '-' as u8 => FlagLeftJustify, + '0' as u8 => FlagLeftZeroPad, + ' ' as u8 => FlagSpaceForSign, + '+' as u8 => FlagSignAlways, + '#' as u8 => FlagAlternate, + _ => break + }; + + flags.push(f); + i += 1; } - // Unfortunate, but because s is borrowed, can't use a closure - // fn more(f: Flag, s: &str) { more_(f, s, i, lim); } - let f = s[i]; - return if f == '-' as u8 { - more(FlagLeftJustify, s, i, lim) - } else if f == '0' as u8 { - more(FlagLeftZeroPad, s, i, lim) - } else if f == ' ' as u8 { - more(FlagSpaceForSign, s, i, lim) - } else if f == '+' as u8 { - more(FlagSignAlways, s, i, lim) - } else if f == '#' as u8 { - more(FlagAlternate, s, i, lim) - } else { Parsed::new(move noflags, i) }; + + Parsed::new(flags, i) } pub fn parse_count(s: &str, i: uint, lim: uint) -> Parsed { From 9c383deaa6683e5519ce6727e1d3d152d70e66a0 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 14:41:37 -0500 Subject: [PATCH 05/12] fmt: simplify parse_count --- src/libcore/extfmt.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index 25e349f0dc321..1216d96530062 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -264,7 +264,7 @@ pub mod ct { } pub fn parse_count(s: &str, i: uint, lim: uint) -> Parsed { - return if i >= lim { + if i >= lim { Parsed::new(CountImplied, i) } else if s[i] == '*' as u8 { let param = parse_parameter(s, i + 1, lim); @@ -274,15 +274,14 @@ pub mod ct { Some(n) => Parsed::new(CountIsParam(n), j) } } else { - let num = peek_num(s, i, lim); - match num { + 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 { From 4550cb05626a1b982560710d6757145049cca247 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 14:43:22 -0500 Subject: [PATCH 06/12] fmt: simplify parse_precision --- src/libcore/extfmt.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index 1216d96530062..5b178e6c82388 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -285,9 +285,7 @@ pub mod ct { } pub fn parse_precision(s: &str, i: uint, lim: uint) -> Parsed { - return if i >= lim { - Parsed::new(CountImplied, i) - } else if s[i] == '.' as u8 { + if i < lim && s[i] == '.' as u8 { let count = parse_count(s, i + 1u, lim); @@ -297,7 +295,7 @@ pub mod ct { CountImplied => Parsed::new(CountIs(0), count.next), _ => count } - } else { Parsed::new(CountImplied, i) }; + } else { Parsed::new(CountImplied, i) } } pub fn parse_type(s: &str, i: uint, lim: uint, err: ErrorFn) -> Parsed { From af298deff7141c3b8b95acb94ef2145d2ef9adc0 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 14:47:06 -0500 Subject: [PATCH 07/12] fmt: simplify parse_parameter --- src/libcore/extfmt.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index 5b178e6c82388..e2b290405333e 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -229,17 +229,11 @@ pub mod ct { pub fn parse_parameter(s: &str, i: uint, lim: uint) -> Parsed> { if i >= lim { return Parsed::new(None, i); } - let num = peek_num(s, i, lim); - return match num { - None => Parsed::new(None, i), - Some(t) => { - let n = t.val; - let j = t.next; - if j < lim && s[j] == '$' as u8 { - Parsed::new(Some(n), j + 1) - } else { 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]> { From 9e5a4166d5bf0ebad622535a6619e12014fc6470 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 14:50:04 -0500 Subject: [PATCH 08/12] fmt: simplify parse_conversion --- src/libcore/extfmt.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index e2b290405333e..2b7783e720e7c 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -214,17 +214,19 @@ pub mod ct { err: ErrorFn) -> Parsed { let parm = parse_parameter(s, i, lim); - let flags = parse_flags(s, parm.next, lim); - let width = parse_count(s, flags.next, lim); + // avoid copying ~[Flag] by destructuring + let Parsed {val: flags_val, next: flags_next} = 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, err); - return Parsed::new( + Parsed::new( PieceConv(Conv {param: parm.val, - flags: copy flags.val, + flags: flags_val, width: width.val, precision: prec.val, ty: ty.val}), - ty.next); + ty.next) } pub fn parse_parameter(s: &str, i: uint, lim: uint) -> Parsed> { From 428abb3d97bbecb90f29e6238dfbedfcf67df2b6 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 15:00:03 -0500 Subject: [PATCH 09/12] fmt: simplify parse_fmt_string --- src/libcore/extfmt.rs | 46 ++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index 2b7783e720e7c..d69015cbfc4ea 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -154,39 +154,41 @@ pub mod ct { pub type ErrorFn = fn@(&str) -> ! ; pub fn parse_fmt_string(s: &str, err: ErrorFn) -> ~[Piece] { - let mut pieces: ~[Piece] = ~[]; - let lim = str::len(s); - let mut buf = ~""; - fn flush_buf(buf: ~str, pieces: &mut ~[Piece]) -> ~str { - if buf.len() > 0 { - let piece = PieceString(move buf); - pieces.push(move piece); + fn push_slice(ps: &mut ~[Piece], s: &str, from: uint, to: uint) { + if to > from { + ps.push(PieceString(s.slice(from, to))); } - return ~""; } + + let lim = s.len(); + let mut h = 0; let mut i = 0; + let mut pieces = ~[]; + while i < lim { - let size = str::utf8_char_width(s[i]); - let curr = str::slice(s, i, i+size); - if curr == ~"%" { + if s[i] == '%' as u8 { i += 1; + if i >= lim { err(~"unterminated conversion at end of string"); - } - let curr2 = str::slice(s, i, i+1); - if curr2 == ~"%" { - buf += curr2; + } else if s[i] == '%' as u8 { + push_slice(&mut pieces, s, h, i); i += 1; } else { - buf = flush_buf(move buf, &mut pieces); - let rs = parse_conversion(s, i, lim, err); - pieces.push(copy rs.val); - i = rs.next; + push_slice(&mut pieces, s, h, i - 1); + let Parsed {val, next} = parse_conversion(s, i, lim, err); + pieces.push(val); + i = next; } - } else { buf += curr; i += size; } + + h = i; + } else { + i += str::utf8_char_width(s[i]); + } } - flush_buf(move buf, &mut pieces); - move pieces + + push_slice(&mut pieces, s, h, i); + pieces } pub fn peek_num(s: &str, i: uint, lim: uint) -> Option> { From 0429493363414d5be8e028812fd01a8a309ef24e Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 15:06:59 -0500 Subject: [PATCH 10/12] fmt: fix formatting and style --- src/libcore/extfmt.rs | 139 +++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 64 deletions(-) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index d69015cbfc4ea..f92ed6ad8347c 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -103,7 +103,9 @@ pub mod ct { use vec; pub enum Signedness { Signed, Unsigned, } + pub enum Caseness { CaseUpper, CaseLower, } + pub enum Ty { TyBool, TyStr, @@ -115,6 +117,7 @@ pub mod ct { TyFloat, TyPoly, } + pub enum Flag { FlagLeftJustify, FlagLeftZeroPad, @@ -122,6 +125,7 @@ pub mod ct { FlagSignAlways, FlagAlternate, } + pub enum Count { CountIs(uint), CountIsParam(uint), @@ -136,22 +140,23 @@ pub mod ct { impl Parsed { static pure fn new(val: T, next: uint) -> Parsed { - Parsed { val: val, next: next } + Parsed {val: val, next: next} } } // A formatted conversion from an expression to a string - pub struct Conv - {param: Option, - flags: ~[Flag], - width: Count, - precision: Count, - ty: Ty} - + pub struct Conv { + param: Option, + flags: ~[Flag], + width: Count, + precision: Count, + ty: Ty + } // A fragment of the output sequence pub enum Piece { PieceString(~str), PieceConv(Conv), } - pub type ErrorFn = fn@(&str) -> ! ; + + pub type ErrorFn = @fn(&str) -> !; pub fn parse_fmt_string(s: &str, err: ErrorFn) -> ~[Piece] { fn push_slice(ps: &mut ~[Piece], s: &str, from: uint, to: uint) { @@ -190,57 +195,61 @@ pub mod ct { push_slice(&mut pieces, s, h, i); pieces } - pub fn peek_num(s: &str, i: uint, lim: uint) -> - Option> { - let mut j = i; - let mut accum = 0u; + + pub fn peek_num(s: &str, i: uint, lim: uint) -> Option> { + let mut i = i; + let mut accum = 0; let mut found = false; - while j < lim { - match char::to_digit(s[j] as char, 10) { + + while i < lim { + match char::to_digit(s[i] as char, 10) { Some(x) => { found = true; accum *= 10; accum += x; - j += 1; - }, + i += 1; + } None => break } } + if found { - Some(Parsed::new(accum, j)) + Some(Parsed::new(accum, i)) } else { None } } - pub fn parse_conversion(s: &str, i: uint, lim: uint, - err: ErrorFn) -> - Parsed { - let parm = parse_parameter(s, i, lim); + + pub fn parse_conversion(s: &str, i: uint, lim: uint, err: ErrorFn) -> + Parsed { + let param = parse_parameter(s, i, lim); // avoid copying ~[Flag] by destructuring let Parsed {val: flags_val, next: flags_next} = parse_flags(s, - parm.next, lim); + 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: parm.val, - flags: flags_val, - width: width.val, - precision: prec.val, - ty: ty.val}), - ty.next) + + 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> { + Parsed> { 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]> { + + pub fn parse_flags(s: &str, i: uint, lim: uint) -> Parsed<~[Flag]> { let mut i = i; let mut flags = ~[]; @@ -260,44 +269,45 @@ pub mod ct { Parsed::new(flags, i) } - pub fn parse_count(s: &str, i: uint, lim: uint) - -> Parsed { - 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_count(s: &str, i: uint, lim: uint) -> Parsed { + 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 { - if i < lim && s[i] == '.' as u8 { - let count = parse_count(s, i + 1u, lim); + pub fn parse_precision(s: &str, i: uint, lim: uint) -> Parsed { + 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) } + // 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(s: &str, i: uint, lim: uint, err: ErrorFn) -> - Parsed { + Parsed { 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] { @@ -314,6 +324,7 @@ pub mod ct { '?' as u8 => TyPoly, _ => err(~"unknown type in conversion: " + s.substr(i, 1)) }; + Parsed::new(t, i + 1) } } From 73649f36e8092006560e5d48213fcf067de38a6f Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 17:05:13 -0500 Subject: [PATCH 11/12] fmt: derive Eq for all types --- src/libcore/extfmt.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index f92ed6ad8347c..3caafc47b926e 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -102,10 +102,13 @@ pub mod ct { use str; use vec; + #[deriving_eq] pub enum Signedness { Signed, Unsigned, } + #[deriving_eq] pub enum Caseness { CaseUpper, CaseLower, } + #[deriving_eq] pub enum Ty { TyBool, TyStr, @@ -118,6 +121,7 @@ pub mod ct { TyPoly, } + #[deriving_eq] pub enum Flag { FlagLeftJustify, FlagLeftZeroPad, @@ -126,6 +130,7 @@ pub mod ct { FlagAlternate, } + #[deriving_eq] pub enum Count { CountIs(uint), CountIsParam(uint), @@ -133,6 +138,7 @@ pub mod ct { CountImplied, } + #[deriving_eq] struct Parsed { val: T, next: uint @@ -145,6 +151,7 @@ pub mod ct { } // A formatted conversion from an expression to a string + #[deriving_eq] pub struct Conv { param: Option, flags: ~[Flag], @@ -154,6 +161,7 @@ pub mod ct { } // A fragment of the output sequence + #[deriving_eq] pub enum Piece { PieceString(~str), PieceConv(Conv), } pub type ErrorFn = @fn(&str) -> !; From 1d4c5d90bde5b96cbb0fe55605e11b0f85aefbb6 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Mon, 7 Jan 2013 17:05:31 -0500 Subject: [PATCH 12/12] fmt: add unit tests --- src/libcore/extfmt.rs | 122 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index 3caafc47b926e..312fc18a033c1 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -335,6 +335,128 @@ pub mod ct { Parsed::new(t, i + 1) } + + #[cfg(test)] + fn die(s: &str) -> ! { fail 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.foldl(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 pack(f.val) == pack(flags); + assert 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, 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("?", 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 peek_num(s3, 0, s3.len()) == Some(Parsed::new(123, 3)); + + let s4 = "123foo"; + assert 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