1
0
mirror of https://github.com/golang/go synced 2024-11-12 10:20:27 -07:00

fmt: add verbs:

%E - upper case %e
	%G - upper case %g
	%#v - Go syntax

R=r
DELTA=332  (238 added, 47 deleted, 47 changed)
OCL=34091
CL=34145
This commit is contained in:
Russ Cox 2009-08-31 16:38:30 -07:00
parent e596297139
commit a843b4541a
4 changed files with 266 additions and 75 deletions

View File

@ -10,6 +10,7 @@ import (
"math";
"strings";
"testing";
"unsafe";
)
func TestFmtInterface(t *testing.T) {
@ -32,6 +33,15 @@ const b64 uint64 = 1<<64 - 1
var array = []int{1, 2, 3, 4, 5}
var iarray = []interface{}{1, "hello", 2.5, nil}
type A struct {
i int;
j uint;
s string;
x []int;
}
var b byte;
var fmttests = []fmtTest{
// basic string
fmtTest{ "%s", "abc", "abc" },
@ -79,15 +89,9 @@ var fmttests = []fmtTest{
fmtTest{ "%+d", -12345, "-12345" },
fmtTest{ "% d", 12345, " 12345" },
// arrays
fmtTest{ "%v", array, "[1 2 3 4 5]" },
fmtTest{ "%v", iarray, "[1 hello 2.5 <nil>]" },
fmtTest{ "%v", &array, "&[1 2 3 4 5]" },
fmtTest{ "%v", &iarray, "&[1 hello 2.5 <nil>]" },
// erroneous formats
fmtTest{ "", 2, "?(extra int=2)" },
fmtTest{ "%d", "hello", "%d(string=hello)%" },
fmtTest{ "%d", "hello", "%d(string=hello)" },
// old test/fmt_test.go
fmtTest{ "%d", 1234, "1234" },
@ -123,6 +127,17 @@ var fmttests = []fmtTest{
fmtTest{ "%g", float64(-7), "-7" },
fmtTest{ "%g", float64(-1e-9), "-1e-09", },
fmtTest{ "%g", float32(-1e-9), "-1e-09" },
fmtTest{ "%E", float64(1), "1.000000E+00" },
fmtTest{ "%E", float64(1234.5678e3), "1.234568E+06" },
fmtTest{ "%E", float64(1234.5678e-8), "1.234568E-05" },
fmtTest{ "%E", float64(-7), "-7.000000E+00" },
fmtTest{ "%E", float64(-1e-9), "-1.000000E-09" },
fmtTest{ "%G", float64(1234.5678e3), "1.2345678E+06" },
fmtTest{ "%G", float32(1234.5678e3), "1.2345678E+06" },
fmtTest{ "%G", float64(1234.5678e-8), "1.2345678E-05" },
fmtTest{ "%G", float64(-7), "-7" },
fmtTest{ "%G", float64(-1e-9), "-1E-09", },
fmtTest{ "%G", float32(-1e-9), "-1E-09" },
fmtTest{ "%c", 'x', "x" },
fmtTest{ "%c", 0xe4, "ä" },
fmtTest{ "%c", 0x672c, "本" },
@ -158,11 +173,39 @@ var fmttests = []fmtTest{
fmtTest{ "%20e", math.Inf(1), " +Inf" },
fmtTest{ "%-20f", math.Inf(-1), "-Inf " },
fmtTest{ "%20g", math.NaN(), " NaN" },
// arrays
fmtTest{ "%v", array, "[1 2 3 4 5]" },
fmtTest{ "%v", iarray, "[1 hello 2.5 <nil>]" },
fmtTest{ "%v", &array, "&[1 2 3 4 5]" },
fmtTest{ "%v", &iarray, "&[1 hello 2.5 <nil>]" },
// structs
fmtTest{ "%v", A{1,2,"a",[]int{1,2}}, `{1 2 a [1 2]}` },
fmtTest{ "%+v", A{1,2,"a",[]int{1,2}}, `{i:1 j:2 s:a x:[1 2]}` },
// go syntax
fmtTest{ "%#v", A{1,2,"a",[]int{1,2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}` },
fmtTest{ "%#v", &b, "(*uint8)(PTR)" },
fmtTest{ "%#v", TestFmtInterface, "(func(*testing.T))(PTR)" },
fmtTest{ "%#v", make(chan int), "(chan int)(PTR)" },
fmtTest{ "%#v", uint64(1<<64-1), "0xffffffffffffffff" },
fmtTest{ "%#v", 1000000000, "1000000000" },
}
func TestSprintf(t *testing.T) {
for i, tt := range fmttests {
s := Sprintf(tt.fmt, tt.val);
if i := strings.Index(s, "0x"); i >= 0 && strings.Index(tt.out, "PTR") >= 0 {
j := i+2;
for ; j < len(s); j++ {
c := s[j];
if (c < '0' || c > '9') && (c < 'a' || c > 'f') {
break;
}
}
s = s[0:i] + "PTR" + s[j:len(s)];
}
if s != tt.out {
if ss, ok := tt.val.(string); ok {
// Don't requote the already-quoted strings.

View File

@ -465,6 +465,11 @@ func (f *Fmt) Fmt_e64(v float64) *Fmt {
return fmtString(f, strconv.Ftoa64(v, 'e', doPrec(f, 6)));
}
// Fmt_E64 formats a float64 in the form -1.23E+12.
func (f *Fmt) Fmt_E64(v float64) *Fmt {
return fmtString(f, strconv.Ftoa64(v, 'E', doPrec(f, 6)));
}
// Fmt_f64 formats a float64 in the form -1.23.
func (f *Fmt) Fmt_f64(v float64) *Fmt {
return fmtString(f, strconv.Ftoa64(v, 'f', doPrec(f, 6)));
@ -475,6 +480,11 @@ func (f *Fmt) Fmt_g64(v float64) *Fmt {
return fmtString(f, strconv.Ftoa64(v, 'g', doPrec(f, -1)));
}
// Fmt_g64 formats a float64 in the 'f' or 'E' form according to size.
func (f *Fmt) Fmt_G64(v float64) *Fmt {
return fmtString(f, strconv.Ftoa64(v, 'G', doPrec(f, -1)));
}
// Fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2).
func (f *Fmt) Fmt_fb64(v float64) *Fmt {
return fmtString(f, strconv.Ftoa64(v, 'b', 0));
@ -489,6 +499,11 @@ func (f *Fmt) Fmt_e32(v float32) *Fmt {
return fmtString(f, strconv.Ftoa32(v, 'e', doPrec(f, 6)));
}
// Fmt_E32 formats a float32 in the form -1.23E+12.
func (f *Fmt) Fmt_E32(v float32) *Fmt {
return fmtString(f, strconv.Ftoa32(v, 'e', doPrec(f, 6)));
}
// Fmt_f32 formats a float32 in the form -1.23.
func (f *Fmt) Fmt_f32(v float32) *Fmt {
return fmtString(f, strconv.Ftoa32(v, 'f', doPrec(f, 6)));
@ -499,6 +514,11 @@ func (f *Fmt) Fmt_g32(v float32) *Fmt {
return fmtString(f, strconv.Ftoa32(v, 'g', doPrec(f, -1)));
}
// Fmt_G32 formats a float32 in the 'f' or 'E' form according to size.
func (f *Fmt) Fmt_G32(v float32) *Fmt {
return fmtString(f, strconv.Ftoa32(v, 'G', doPrec(f, -1)));
}
// Fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2).
func (f *Fmt) Fmt_fb32(v float32) *Fmt {
return fmtString(f, strconv.Ftoa32(v, 'b', 0));

View File

@ -10,8 +10,11 @@
The verbs:
General:
%v for any operand type, the value in a default format.
%v the value in a default format.
when printing structs, the plus flag (%+v) adds field names
%#v a Go-syntax representation of the value
%T a Go-syntax representation of the type of the value
Boolean:
%t the word true or false
Integer:
@ -23,16 +26,16 @@
%X base 16, with upper-case letters for A-F
Floating-point:
%e scientific notation, e.g. -1234.456e+78
%E scientific notation, e.g. -1234.456E+78
%f decimal point but no exponent, e.g. 123.456
%g whichever of %e or %f produces more compact output
%G whichever of %E or %f produces more compact output
String and slice of bytes:
%s the uninterpreted bytes of the string or slice
%q a double-quoted string safely escaped with Go syntax
%x base 16 notation with two characters per byte
Pointer:
%p base 16 notation, with leading 0x
Type:
%T a Go-syntax representation of the type of the operand
There is no 'u' flag. Integers are printed unsigned if they have unsigned type.
Similarly, there is no need to specify the size of the operand (int8, int64).
@ -95,21 +98,29 @@ type State interface {
Flag(int) bool;
}
// Formatter is the interface implemented by objects with a custom formatter.
// Formatter is the interface implemented by values with a custom formatter.
// The implementation of Format may call Sprintf or Fprintf(f) etc.
// to generate its output.
type Formatter interface {
Format(f State, c int);
}
// String represents any object being printed that has a String() method that
// returns a string, which defines the ``native'' format for that object.
// Any such object will be printed using that method if passed
// as operand to a %s or %v format or to an unformatted printer such as Print.
// Stringer is implemented by any value that has a String method(),
// which defines the ``native'' format for that value.
// The String method is used to print values passed as an operand
// to a %s or %v format or to an unformatted printer such as Print.
type Stringer interface {
String() string
}
// GoStringer is implemented by any value that has a GoString() method,
// which defines the Go syntax for that value.
// The GoString method is used to print values passed as an operand
// to a %#v format.
type GoStringer interface {
GoString() string
}
const runeSelf = utf8.RuneSelf
const allocSize = 32
@ -392,15 +403,28 @@ func parsenum(s string, start, end int) (n int, got bool, newi int) {
return num, isnum, start;
}
func (p *pp) printField(field reflect.Value) (was_string bool) {
type uintptrGetter interface {
Get() uintptr;
}
func (p *pp) printField(field reflect.Value, plus, sharp bool, depth int) (was_string bool) {
inter := field.Interface();
if inter != nil {
if stringer, ok := inter.(Stringer); ok {
p.addstr(stringer.String());
return false; // this value is not a string
switch {
default:
if stringer, ok := inter.(Stringer); ok {
p.addstr(stringer.String());
return false; // this value is not a string
}
case sharp:
if stringer, ok := inter.(GoStringer); ok {
p.addstr(stringer.GoString());
return false; // this value is not a string
}
}
}
s := "";
BigSwitch:
switch f := field.(type) {
case *reflect.BoolValue:
s = p.fmt.Fmt_boolean(f.Get()).Str();
@ -415,79 +439,160 @@ func (p *pp) printField(field reflect.Value) (was_string bool) {
s = p.fmt.Fmt_g64(float64(f.Get())).Str();
}
case *reflect.StringValue:
s = p.fmt.Fmt_s(f.Get()).Str();
was_string = true;
case *reflect.PtrValue:
v := f.Get();
if v == 0 {
s = "<nil>";
break;
if sharp {
s = p.fmt.Fmt_q(f.Get()).Str();
} else {
s = p.fmt.Fmt_s(f.Get()).Str();
was_string = true;
}
// pointer to array?
if a, ok := f.Elem().(reflect.ArrayOrSliceValue); ok {
p.addstr("&");
p.printField(a);
break;
}
p.fmt.sharp = !p.fmt.sharp; // turn 0x on by default
s = p.fmt.Fmt_ux64(uint64(v)).Str();
case reflect.ArrayOrSliceValue:
p.addstr("[");
for i := 0; i < f.Len(); i++ {
if i > 0 {
p.addstr(" ");
}
p.printField(f.Elem(i));
}
p.addstr("]");
case *reflect.MapValue:
p.addstr("map[");
if sharp {
p.addstr(field.Type().String());
p.addstr("{");
} else {
p.addstr("map[");
}
keys := f.Keys();
for i, key := range keys {
if i > 0 {
p.addstr(" ");
if sharp {
p.addstr(", ");
} else {
p.addstr(" ");
}
}
p.printField(key);
p.printField(key, plus, sharp, depth+1);
p.addstr(":");
p.printField(f.Elem(key));
p.printField(f.Elem(key), plus, sharp, depth+1);
}
if sharp {
p.addstr("}");
} else {
p.addstr("]");
}
p.addstr("]");
case *reflect.StructValue:
if sharp {
p.addstr(field.Type().String());
}
p.add('{');
v := f;
t := v.Type().(*reflect.StructType);
donames := p.fmt.plus;
p.fmt.clearflags(); // clear flags for p.printField
for i := 0; i < v.NumField(); i++ {
if i > 0 {
p.add(' ')
if sharp {
p.addstr(", ");
} else {
p.addstr(" ");
}
}
if donames {
if plus || sharp {
if f := t.Field(i); f.Name != "" {
p.addstr(f.Name);
p.add(':');
}
}
p.printField(getField(v, i));
p.printField(getField(v, i), plus, sharp, depth+1);
}
p.add('}');
p.addstr("}");
case *reflect.InterfaceValue:
value := f.Elem();
if value == nil {
s = "<nil>"
if sharp {
p.addstr(field.Type().String());
p.addstr("(nil)");
} else {
s = "<nil>"
}
} else {
return p.printField(value);
return p.printField(value, plus, sharp, depth+1);
}
case reflect.ArrayOrSliceValue:
if sharp {
p.addstr(field.Type().String());
p.addstr("{");
} else {
p.addstr("[");
}
for i := 0; i < f.Len(); i++ {
if i > 0 {
if sharp {
p.addstr(", ");
} else {
p.addstr(" ");
}
}
p.printField(f.Elem(i), plus, sharp, depth+1);
}
if sharp {
p.addstr("}");
} else {
p.addstr("]");
}
case *reflect.PtrValue:
v := f.Get();
// pointer to array or slice or struct? ok at top level
// but not embedded (avoid loops)
if v != 0 && depth == 0 {
switch a := f.Elem().(type) {
case reflect.ArrayOrSliceValue:
p.addstr("&");
p.printField(a, plus, sharp, depth+1);
break BigSwitch;
case *reflect.StructValue:
p.addstr("&");
p.printField(a, plus, sharp, depth+1);
break BigSwitch;
}
}
if sharp {
p.addstr("(");
p.addstr(field.Type().String());
p.addstr(")(");
if v == 0 {
p.addstr("nil");
} else {
p.fmt.sharp = true;
p.addstr(p.fmt.Fmt_ux64(uint64(v)).Str());
}
p.addstr(")");
break;
}
if v == 0 {
s = "<nil>";
break;
}
p.fmt.sharp = true; // turn 0x on
s = p.fmt.Fmt_ux64(uint64(v)).Str();
case uintptrGetter:
v := f.Get();
if sharp {
p.addstr("(");
p.addstr(field.Type().String());
p.addstr(")(");
if v == 0 {
p.addstr("nil");
} else {
p.fmt.sharp = true;
p.addstr(p.fmt.Fmt_ux64(uint64(v)).Str());
}
p.addstr(")");
} else {
p.fmt.sharp = true; // turn 0x on
p.addstr(p.fmt.Fmt_ux64(uint64(f.Get())).Str());
}
case *reflect.UintptrValue:
p.fmt.sharp = !p.fmt.sharp; // turn 0x on by default
s = p.fmt.Fmt_ux64(uint64(f.Get())).Str();
default:
v, signed, ok := getInt(field);
if ok {
if signed {
s = p.fmt.Fmt_d64(v).Str();
} else {
s = p.fmt.Fmt_ud64(uint64(v)).Str();
if sharp {
p.fmt.sharp = true; // turn on 0x
s = p.fmt.Fmt_ux64(uint64(v)).Str();
} else {
s = p.fmt.Fmt_ud64(uint64(v)).Str();
}
}
break;
}
@ -548,13 +653,17 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
}
field := getField(v, fieldnum);
fieldnum++;
// Try formatter except for %T,
// which is special and handled internally.
inter := field.Interface();
if inter != nil && c != 'T' { // don't want thing to describe itself if we're asking for its type
if inter != nil && c != 'T' {
if formatter, ok := inter.(Formatter); ok {
formatter.Format(p, c);
continue;
}
}
s := "";
switch c {
// bool
@ -640,6 +749,14 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
} else {
goto badtype
}
case 'E':
if v, ok := getFloat32(field); ok {
s = p.fmt.Fmt_E32(v).Str()
} else if v, ok := getFloat64(field); ok {
s = p.fmt.Fmt_E64(v).Str()
} else {
goto badtype
}
case 'f':
if v, ok := getFloat32(field); ok {
s = p.fmt.Fmt_f32(v).Str()
@ -656,6 +773,14 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
} else {
goto badtype
}
case 'G':
if v, ok := getFloat32(field); ok {
s = p.fmt.Fmt_G32(v).Str()
} else if v, ok := getFloat64(field); ok {
s = p.fmt.Fmt_G64(v).Str()
} else {
goto badtype
}
// string
case 's':
@ -692,7 +817,10 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
// arbitrary value; do your best
case 'v':
p.printField(field);
plus, sharp := p.fmt.plus, p.fmt.sharp;
p.fmt.plus = false;
p.fmt.sharp = false;
p.printField(field, plus, sharp, 0);
// the value's type
case 'T':
@ -702,8 +830,8 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
badtype:
s = "%" + string(c) + "(" + field.Type().String() + "=";
p.addstr(s);
p.printField(field);
s= ")%";
p.printField(field, false, false, 0);
s = ")";
}
p.addstr(s);
}
@ -713,7 +841,7 @@ func (p *pp) doprintf(format string, v *reflect.StructValue) {
field := getField(v, fieldnum);
p.addstr(field.Type().String());
p.addstr("=");
p.printField(field);
p.printField(field, false, false, 0);
if fieldnum + 1 < v.NumField() {
p.addstr(", ");
}
@ -733,7 +861,7 @@ func (p *pp) doprint(v *reflect.StructValue, addspace, addnewline bool) {
p.add(' ');
}
}
prev_string = p.printField(field);
prev_string = p.printField(field, false, false, 0);
}
if addnewline {
p.add('\n')

View File

@ -115,20 +115,20 @@ func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string {
shortest = true;
roundShortest(d, mant, exp, flt);
switch fmt {
case 'e':
case 'e', 'E':
prec = d.nd - 1;
case 'f':
prec = max(d.nd - d.dp, 0);
case 'g':
case 'g', 'G':
prec = d.nd;
}
} else {
switch fmt {
case 'e':
case 'e', 'E':
d.Round(prec+1);
case 'f':
d.Round(d.dp+prec);
case 'g':
case 'g', 'G':
if prec == 0 {
prec = 1;
}
@ -137,11 +137,11 @@ func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string {
}
switch fmt {
case 'e':
return fmtE(neg, d, prec);
case 'e', 'E':
return fmtE(neg, d, prec, fmt);
case 'f':
return fmtF(neg, d, prec);
case 'g':
case 'g', 'G':
// trailing zeros are removed.
if prec > d.nd {
prec = d.nd;
@ -155,7 +155,7 @@ func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string {
}
exp := d.dp - 1;
if exp < -4 || exp >= eprec {
return fmtE(neg, d, prec - 1);
return fmtE(neg, d, prec - 1, fmt + 'e' - 'g');
}
return fmtF(neg, d, max(prec - d.dp, 0));
}
@ -251,7 +251,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
}
// %e: -d.ddddde±dd
func fmtE(neg bool, d *decimal, prec int) string {
func fmtE(neg bool, d *decimal, prec int, fmt byte) string {
buf := make([]byte, 3+max(prec, 0)+30); // "-0." + prec digits + exp
w := 0; // write index
@ -284,7 +284,7 @@ func fmtE(neg bool, d *decimal, prec int) string {
}
// e±
buf[w] = 'e';
buf[w] = fmt;
w++;
exp := d.dp - 1;
if d.nd == 0 { // special case: 0 has exponent 0