Commit d482c163 authored by Rob Pike's avatar Rob Pike

fmt.Print*: reimplement to switch on type first.

This shortens, simplifies and regularizes the code significantly.
(Improvements to reflect could make another step.)
Passes all.bash.

One semantic change occurs: The String() method changes
behavior. It used to run only for string formats such as %s and %q.
Instead, it now runs whenever the item has the method and the
result is then processed by the format as a string. Besides the
regularization, this has three effects:

	1) width is honored for String() items
	2) %x works for String() items
	3) implementations of String that merely recur will recur forever

Regarding point 3, example from the updated documentation:
	type X int
	func (x X) String() string { return Sprintf("%d", x) }
should cast the value before recurring:
	func (x X) String() string { return Sprintf("%d", int(x)) }

R=rsc
CC=golang-dev
https://golang.org/cl/1613045
parent 43b3a247
......@@ -13,6 +13,29 @@ import (
"testing"
)
type (
renamedBool bool
renamedInt int
renamedInt8 int8
renamedInt16 int16
renamedInt32 int32
renamedInt64 int64
renamedUint uint
renamedUint8 uint8
renamedUint16 uint16
renamedUint32 uint32
renamedUint64 uint64
renamedUintptr uintptr
renamedString string
renamedBytes []byte
renamedFloat float
renamedFloat32 float32
renamedFloat64 float64
renamedComplex complex
renamedComplex64 complex64
renamedComplex128 complex128
)
func TestFmtInterface(t *testing.T) {
var i1 interface{}
i1 = "abc"
......@@ -43,7 +66,7 @@ type A struct {
type I int
func (i I) String() string { return Sprintf("<%d>", i) }
func (i I) String() string { return Sprintf("<%d>", int(i)) }
type B struct {
i I
......@@ -58,6 +81,10 @@ type C struct {
var b byte
var fmttests = []fmtTest{
fmtTest{"%d", 12345, "12345"},
fmtTest{"%v", 12345, "12345"},
fmtTest{"%t", true, "true"},
// basic string
fmtTest{"%s", "abc", "abc"},
fmtTest{"%x", "abc", "616263"},
......@@ -245,7 +272,10 @@ var fmttests = []fmtTest{
fmtTest{"%+v", C{1, B{2, 3}}, `{i:1 B:{i:<2> j:3}}`},
// q on Stringable items
fmtTest{"%s", I(23), `<23>`},
fmtTest{"%q", I(23), `"<23>"`},
fmtTest{"%x", I(23), `3c32333e`},
fmtTest{"%d", I(23), `%d(string=<23>)`},
// %p on non-pointers
fmtTest{"%p", make(chan int), "PTR"},
......@@ -260,6 +290,30 @@ var fmttests = []fmtTest{
fmtTest{"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"},
fmtTest{"%#v", 1000000000, "1000000000"},
// renamings
fmtTest{"%v", renamedBool(true), "true"},
fmtTest{"%d", renamedBool(true), "%d(fmt_test.renamedBool=true)"},
fmtTest{"%o", renamedInt(8), "10"},
fmtTest{"%d", renamedInt8(-9), "-9"},
fmtTest{"%v", renamedInt16(10), "10"},
fmtTest{"%v", renamedInt32(-11), "-11"},
fmtTest{"%X", renamedInt64(255), "FF"},
fmtTest{"%v", renamedUint(13), "13"},
fmtTest{"%o", renamedUint8(14), "16"},
fmtTest{"%X", renamedUint16(15), "F"},
fmtTest{"%d", renamedUint32(16), "16"},
fmtTest{"%X", renamedUint64(17), "11"},
fmtTest{"%o", renamedUintptr(18), "22"},
fmtTest{"%x", renamedString("thing"), "7468696e67"},
// TODO: It would be nice if this one worked, but it's hard.
// fmtTest{"%q", renamedBytes([]byte("hello")), `"hello"`},
fmtTest{"%v", renamedFloat(11), "11"},
fmtTest{"%v", renamedFloat32(22), "22"},
fmtTest{"%v", renamedFloat64(33), "33"},
fmtTest{"%v", renamedComplex(7 + .2i), "(7+0.2i)"},
fmtTest{"%v", renamedComplex64(3 + 4i), "(3+4i)"},
fmtTest{"%v", renamedComplex128(4 - 3i), "(4-3i)"},
// erroneous things
fmtTest{"%d", "hello", "%d(string=hello)"},
fmtTest{"no args", "hello", "no args?(extra string=hello)"},
......
......@@ -234,87 +234,6 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
f.pad(buf[i:])
}
// fmt_d64 formats an int64 in decimal.
func (f *fmt) fmt_d64(v int64) { f.integer(v, 10, signed, ldigits) }
// fmt_d32 formats an int32 in decimal.
func (f *fmt) fmt_d32(v int32) { f.integer(int64(v), 10, signed, ldigits) }
// fmt_d formats an int in decimal.
func (f *fmt) fmt_d(v int) { f.integer(int64(v), 10, signed, ldigits) }
// fmt_ud64 formats a uint64 in decimal.
func (f *fmt) fmt_ud64(v uint64) { f.integer(int64(v), 10, unsigned, ldigits) }
// fmt_ud32 formats a uint32 in decimal.
func (f *fmt) fmt_ud32(v uint32) { f.integer(int64(v), 10, unsigned, ldigits) }
// fmt_ud formats a uint in decimal.
func (f *fmt) fmt_ud(v uint) { f.integer(int64(v), 10, unsigned, ldigits) }
// fmt_x64 formats an int64 in hexadecimal.
func (f *fmt) fmt_x64(v int64) { f.integer(v, 16, signed, ldigits) }
// fmt_x32 formats an int32 in hexadecimal.
func (f *fmt) fmt_x32(v int32) { f.integer(int64(v), 16, signed, ldigits) }
// fmt_x formats an int in hexadecimal.
func (f *fmt) fmt_x(v int) { f.integer(int64(v), 16, signed, ldigits) }
// fmt_ux64 formats a uint64 in hexadecimal.
func (f *fmt) fmt_ux64(v uint64) { f.integer(int64(v), 16, unsigned, ldigits) }
// fmt_ux32 formats a uint32 in hexadecimal.
func (f *fmt) fmt_ux32(v uint32) { f.integer(int64(v), 16, unsigned, ldigits) }
// fmt_ux formats a uint in hexadecimal.
func (f *fmt) fmt_ux(v uint) { f.integer(int64(v), 16, unsigned, ldigits) }
// fmt_X64 formats an int64 in upper case hexadecimal.
func (f *fmt) fmt_X64(v int64) { f.integer(v, 16, signed, udigits) }
// fmt_X32 formats an int32 in upper case hexadecimal.
func (f *fmt) fmt_X32(v int32) { f.integer(int64(v), 16, signed, udigits) }
// fmt_X formats an int in upper case hexadecimal.
func (f *fmt) fmt_X(v int) { f.integer(int64(v), 16, signed, udigits) }
// fmt_uX64 formats a uint64 in upper case hexadecimal.
func (f *fmt) fmt_uX64(v uint64) { f.integer(int64(v), 16, unsigned, udigits) }
// fmt_uX32 formats a uint32 in upper case hexadecimal.
func (f *fmt) fmt_uX32(v uint32) { f.integer(int64(v), 16, unsigned, udigits) }
// fmt_uX formats a uint in upper case hexadecimal.
func (f *fmt) fmt_uX(v uint) { f.integer(int64(v), 16, unsigned, udigits) }
// fmt_o64 formats an int64 in octal.
func (f *fmt) fmt_o64(v int64) { f.integer(v, 8, signed, ldigits) }
// fmt_o32 formats an int32 in octal.
func (f *fmt) fmt_o32(v int32) { f.integer(int64(v), 8, signed, ldigits) }
// fmt_o formats an int in octal.
func (f *fmt) fmt_o(v int) { f.integer(int64(v), 8, signed, ldigits) }
// fmt_uo64 formats a uint64 in octal.
func (f *fmt) fmt_uo64(v uint64) { f.integer(int64(v), 8, unsigned, ldigits) }
// fmt_uo32 formats a uint32 in octal.
func (f *fmt) fmt_uo32(v uint32) { f.integer(int64(v), 8, unsigned, ldigits) }
// fmt_uo formats a uint in octal.
func (f *fmt) fmt_uo(v uint) { f.integer(int64(v), 8, unsigned, ldigits) }
// fmt_b64 formats an int64 in binary.
func (f *fmt) fmt_b64(v int64) { f.integer(v, 2, signed, ldigits) }
// fmt_ub64 formats a uint64 in binary.
func (f *fmt) fmt_ub64(v uint64) { f.integer(int64(v), 2, unsigned, ldigits) }
// fmt_c formats a Unicode character.
func (f *fmt) fmt_c(v int) { f.padString(string(v)) }
// fmt_s formats a string.
func (f *fmt) fmt_s(s string) {
if f.precPresent {
......@@ -422,14 +341,13 @@ func (f *fmt) fmt_G32(v float32) { f.plusSpace(strconv.Ftoa32(v, 'G', doPrec(f,
// fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2).
func (f *fmt) fmt_fb32(v float32) { f.padString(strconv.Ftoa32(v, 'b', 0)) }
// fmt_c64 formats a complex64 according to its fmt_x argument.
// TODO pass in a method rather than a byte when the compilers mature.
func (f *fmt) fmt_c64(v complex64, fmt_x byte) {
// fmt_c64 formats a complex64 according to the verb.
func (f *fmt) fmt_c64(v complex64, verb int) {
f.buf.WriteByte('(')
r := real(v)
f.preserveFlags = true
for i := 0; ; i++ {
switch fmt_x {
switch verb {
case 'e':
f.fmt_e32(r)
case 'E':
......@@ -451,14 +369,13 @@ func (f *fmt) fmt_c64(v complex64, fmt_x byte) {
f.buf.Write(irparenBytes)
}
// fmt_c128 formats a complex128 according to its fmt_x argument.
// TODO pass in a method rather than a byte when the compilers mature.
func (f *fmt) fmt_c128(v complex128, fmt_x byte) {
// fmt_c128 formats a complex128 according to the verb.
func (f *fmt) fmt_c128(v complex128, verb int) {
f.buf.WriteByte('(')
r := real(v)
f.preserveFlags = true
for i := 0; ; i++ {
switch fmt_x {
switch verb {
case 'e':
f.fmt_e64(r)
case 'E':
......
This diff is collapsed.
......@@ -34,29 +34,6 @@ type ScanfMultiTest struct {
err string
}
type (
renamedBool bool
renamedInt int
renamedInt8 int8
renamedInt16 int16
renamedInt32 int32
renamedInt64 int64
renamedUint uint
renamedUint8 uint8
renamedUint16 uint16
renamedUint32 uint32
renamedUint64 uint64
renamedUintptr uintptr
renamedString string
renamedBytes []byte
renamedFloat float
renamedFloat32 float32
renamedFloat64 float64
renamedComplex complex
renamedComplex64 complex64
renamedComplex128 complex128
)
var (
boolVal bool
intVal int
......@@ -122,7 +99,7 @@ func (x *Xs) Scan(state ScanState, verb int) os.Error {
if err != nil {
return err
}
if !testing.MustCompile(string(verb) + "+").MatchString(tok) {
if !testing.MustCompile("^" + string(verb) + "+$").MatchString(tok) {
return os.ErrorString("syntax error for xs")
}
*x = Xs(tok)
......
......@@ -26,21 +26,21 @@ type TF64 float64
type TB bool
type TS string
func (v TI) String() string { return Sprintf("I: %d", v) }
func (v TI8) String() string { return Sprintf("I8: %d", v) }
func (v TI16) String() string { return Sprintf("I16: %d", v) }
func (v TI32) String() string { return Sprintf("I32: %d", v) }
func (v TI64) String() string { return Sprintf("I64: %d", v) }
func (v TU) String() string { return Sprintf("U: %d", v) }
func (v TU8) String() string { return Sprintf("U8: %d", v) }
func (v TU16) String() string { return Sprintf("U16: %d", v) }
func (v TU32) String() string { return Sprintf("U32: %d", v) }
func (v TU64) String() string { return Sprintf("U64: %d", v) }
func (v TUI) String() string { return Sprintf("UI: %d", v) }
func (v TF) String() string { return Sprintf("F: %f", v) }
func (v TF32) String() string { return Sprintf("F32: %f", v) }
func (v TF64) String() string { return Sprintf("F64: %f", v) }
func (v TB) String() string { return Sprintf("B: %t", v) }
func (v TI) String() string { return Sprintf("I: %d", int(v)) }
func (v TI8) String() string { return Sprintf("I8: %d", int8(v)) }
func (v TI16) String() string { return Sprintf("I16: %d", int16(v)) }
func (v TI32) String() string { return Sprintf("I32: %d", int32(v)) }
func (v TI64) String() string { return Sprintf("I64: %d", int64(v)) }
func (v TU) String() string { return Sprintf("U: %d", uint(v)) }
func (v TU8) String() string { return Sprintf("U8: %d", uint8(v)) }
func (v TU16) String() string { return Sprintf("U16: %d", uint16(v)) }
func (v TU32) String() string { return Sprintf("U32: %d", uint32(v)) }
func (v TU64) String() string { return Sprintf("U64: %d", uint64(v)) }
func (v TUI) String() string { return Sprintf("UI: %d", uintptr(v)) }
func (v TF) String() string { return Sprintf("F: %f", float(v)) }
func (v TF32) String() string { return Sprintf("F32: %f", float32(v)) }
func (v TF64) String() string { return Sprintf("F64: %f", float64(v)) }
func (v TB) String() string { return Sprintf("B: %t", bool(v)) }
func (v TS) String() string { return Sprintf("S: %q", string(v)) }
func check(t *testing.T, got, want string) {
......
......@@ -10,7 +10,7 @@ import "fmt"
type T int
func (t T) String() string { return fmt.Sprintf("T%d", t) }
func (t T) String() string { return fmt.Sprintf("T%d", int(t)) }
const (
A T = 1 << (1 << iota)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment