From d84132cce77c7826a9b5d55eb385e71e0ad22ade Mon Sep 17 00:00:00 2001 From: Rob Pike <r@golang.org> Date: Wed, 29 May 2013 11:29:29 -0400 Subject: [PATCH] fmt: change evalutation of indexed arg to match docs The old code put the index before the period in the precision; it should be after so it's always before the star, as documented. A little trickier to do in one pass but compensated for by more tests and catching a couple of other error cases. R=rsc CC=golang-dev https://golang.org/cl/9751044 --- src/pkg/fmt/doc.go | 7 +++--- src/pkg/fmt/fmt_test.go | 25 ++++++++++++------ src/pkg/fmt/print.go | 56 ++++++++++++++++++++++++++--------------- 3 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go index a0c6795d81..2910198508 100644 --- a/src/pkg/fmt/doc.go +++ b/src/pkg/fmt/doc.go @@ -131,7 +131,7 @@ For example, fmt.Sprintf("%[2]d %[1]d\n", 11, 22) will yield "22, 11", while - fmt.Sprintf("%[3]*[2].*[1]f", 12.0, 2, 6), + fmt.Sprintf("%[3]*.[2]*[1]f", 12.0, 2, 6), equivalent to fmt.Sprintf("%6.2f", 12.0), will yield " 12.00". Because an explicit index affects subsequent verbs, @@ -155,8 +155,9 @@ Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC) Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi - Invalid or out-of-range argument index: %!(BADARGNUM) - Printf("%*[2]d", 7): %d(BADARGNUM) + Invalid or invalid use of argument index: %!(BADINDEX) + Printf("%*[2]d", 7): %d(BADINDEX) + Printf("%.[2]d", 7): %d(BADINDEX) All errors begin with the string "%!" followed sometimes by a single character (the verb) and end with a parenthesized diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index a121c9c248..85173aa4bc 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -550,18 +550,29 @@ var reorderTests = []struct { {"%[2]d", SE{2, 1}, "1"}, {"%[2]d %[1]d", SE{1, 2}, "2 1"}, {"%[2]*[1]d", SE{2, 5}, " 2"}, - {"%6.2f", SE{12.0}, " 12.00"}, - {"%[3]*[2].*[1]f", SE{12.0, 2, 6}, " 12.00"}, - {"%[1]*[2].*[3]f", SE{6, 2, 12.0}, " 12.00"}, + {"%6.2f", SE{12.0}, " 12.00"}, // Explicit version of next line. + {"%[3]*.[2]*[1]f", SE{12.0, 2, 6}, " 12.00"}, + {"%[1]*.[2]*[3]f", SE{6, 2, 12.0}, " 12.00"}, + {"%10f", SE{12.0}, " 12.000000"}, + {"%[1]*[3]f", SE{10, 99, 12.0}, " 12.000000"}, + {"%.6f", SE{12.0}, "12.000000"}, // Explicit version of next line. + {"%.[1]*[3]f", SE{6, 99, 12.0}, "12.000000"}, + {"%6.f", SE{12.0}, " 12"}, // // Explicit version of next line; empty precision means zero. + {"%[1]*.[3]f", SE{6, 3, 12.0}, " 12"}, // An actual use! Print the same arguments twice. {"%d %d %d %#[1]o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015"}, // Erroneous cases. - {"%[]d", SE{2, 1}, "%d(BADARGNUM)"}, - {"%[-3]d", SE{2, 1}, "%d(BADARGNUM)"}, - {"%[x]d", SE{2, 1}, "%d(BADARGNUM)"}, - {"%[23]d", SE{2, 1}, "%d(BADARGNUM)"}, + {"%[d", SE{2, 1}, "%d(BADINDEX)"}, + {"%]d", SE{2, 1}, "%!](int=2)d%!(EXTRA int=1)"}, + {"%[]d", SE{2, 1}, "%d(BADINDEX)"}, + {"%[-3]d", SE{2, 1}, "%d(BADINDEX)"}, + {"%[99]d", SE{2, 1}, "%d(BADINDEX)"}, {"%[3]", SE{2, 1}, "%!(NOVERB)"}, + {"%[1].2d", SE{5, 6}, "%d(BADINDEX)"}, + {"%[1]2d", SE{2, 1}, "%d(BADINDEX)"}, + {"%3.[2]d", SE{7}, "%d(BADINDEX)"}, + {"%.[2]d", SE{7}, "%d(BADINDEX)"}, {"%d %d %d %#[1]o %#o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015 %o(MISSING)"}, } diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index 58ffe216e9..fa9eb52c6a 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -22,7 +22,7 @@ var ( nilBytes = []byte("nil") mapBytes = []byte("map[") missingBytes = []byte("(MISSING)") - badArgNum = []byte("(BADARGNUM)") + badIndexBytes = []byte("(BADINDEX)") panicBytes = []byte("(PANIC=") extraBytes = []byte("%!(EXTRA ") irparenBytes = []byte("i)") @@ -117,7 +117,7 @@ type pp struct { value reflect.Value // reordered records whether the format string used argument reordering. reordered bool - // goodArgNum records whether the last reordering directive was valid. + // goodArgNum records whether all reordering directives were valid. goodArgNum bool runeBuf [utf8.UTFMax]byte fmt fmt @@ -1021,11 +1021,11 @@ BigSwitch: } // intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has type int. -func intFromArg(a []interface{}, end, i, argNum int) (num int, isInt bool, newi, newArgNum int) { - newi, newArgNum = end, argNum - if i < end && argNum < len(a) { +func intFromArg(a []interface{}, argNum int) (num int, isInt bool, newArgNum int) { + newArgNum = argNum + if argNum < len(a) { num, isInt = a[argNum].(int) - newi, newArgNum = i+1, argNum+1 + newArgNum = argNum + 1 } return } @@ -1053,24 +1053,25 @@ func parseArgNumber(format string) (index int, wid int, ok bool) { // argNumber returns the next argument to evaluate, which is either the value of the passed-in // argNum or the value of the bracketed integer that begins format[i:]. It also returns // the new value of i, that is, the index of the next byte of the format to process. -func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int) { - p.goodArgNum = true +func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) { if len(format) <= i || format[i] != '[' { - return argNum, i + return argNum, i, false } p.reordered = true index, wid, ok := parseArgNumber(format[i:]) if ok && 0 <= index && index < numArgs { - return index, i + wid + return index, i + wid, true } p.goodArgNum = false - return argNum, i + wid + return argNum, i + wid, true } func (p *pp) doPrintf(format string, a []interface{}) { end := len(format) - argNum := 0 // we process one argument per non-trivial format + argNum := 0 // we process one argument per non-trivial format + afterIndex := false // previous item in format was an index like [3]. p.reordered = false + p.goodArgNum = true for i := 0; i < end; { lasti := i for i < end && format[i] != '%' { @@ -1108,35 +1109,50 @@ func (p *pp) doPrintf(format string, a []interface{}) { } // Do we have an explicit argument index? - argNum, i = p.argNumber(argNum, format, i, len(a)) + argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) // Do we have width? if i < end && format[i] == '*' { - p.fmt.wid, p.fmt.widPresent, i, argNum = intFromArg(a, end, i, argNum) + i++ + p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum) if !p.fmt.widPresent { p.buf.Write(badWidthBytes) } - argNum, i = p.argNumber(argNum, format, i, len(a)) // We consumed []; another can follow here. + afterIndex = false } else { p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) + if afterIndex && p.fmt.widPresent { // "%[3]2d" + p.goodArgNum = false + } } // Do we have precision? if i+1 < end && format[i] == '.' { - if format[i+1] == '*' { - p.fmt.prec, p.fmt.precPresent, i, argNum = intFromArg(a, end, i+1, argNum) + i++ + if afterIndex { // "%[3].2d" + p.goodArgNum = false + } + argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) + if format[i] == '*' { + i++ + p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum) if !p.fmt.precPresent { p.buf.Write(badPrecBytes) } - argNum, i = p.argNumber(argNum, format, i, len(a)) // We consumed []; another can follow here. + afterIndex = false } else { - p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end) + p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i, end) if !p.fmt.precPresent { p.fmt.prec = 0 p.fmt.precPresent = true } } } + + if !afterIndex { + argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) + } + if i >= end { p.buf.Write(noVerbBytes) continue @@ -1151,7 +1167,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { if !p.goodArgNum { p.buf.WriteByte('%') p.add(c) - p.buf.Write(badArgNum) + p.buf.Write(badIndexBytes) continue } else if argNum >= len(a) { // out of operands p.buf.WriteByte('%') -- 2.30.9