diff options
Diffstat (limited to 'src/pkg/fmt')
-rw-r--r-- | src/pkg/fmt/Makefile | 3 | ||||
-rw-r--r-- | src/pkg/fmt/doc.go | 163 | ||||
-rw-r--r-- | src/pkg/fmt/fmt_test.go | 612 | ||||
-rw-r--r-- | src/pkg/fmt/format.go | 11 | ||||
-rw-r--r-- | src/pkg/fmt/print.go | 384 | ||||
-rw-r--r-- | src/pkg/fmt/scan.go | 138 | ||||
-rw-r--r-- | src/pkg/fmt/scan_test.go | 422 |
7 files changed, 1079 insertions, 654 deletions
diff --git a/src/pkg/fmt/Makefile b/src/pkg/fmt/Makefile index 28ea396c7..44b48bc67 100644 --- a/src/pkg/fmt/Makefile +++ b/src/pkg/fmt/Makefile @@ -2,10 +2,11 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=fmt GOFILES=\ + doc.go\ format.go\ print.go\ scan.go\ diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go new file mode 100644 index 000000000..f3067eac9 --- /dev/null +++ b/src/pkg/fmt/doc.go @@ -0,0 +1,163 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + Package fmt implements formatted I/O with functions analogous + to C's printf and scanf. The format 'verbs' are derived from C's but + are simpler. + + Printing: + + The verbs: + + General: + %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: + %b base 2 + %c the character represented by the corresponding Unicode code point + %d base 10 + %o base 8 + %x base 16, with lower-case letters for a-f + %X base 16, with upper-case letters for A-F + %U unicode format: U+1234; same as "U+%x" with 4 digits default + Floating-point and complex constituents: + %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 + + 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). + + For numeric values, the width and precision flags control + formatting; width sets the width of the field, precision the + number of places after the decimal, if appropriate. The + format %6.2f prints 123.45. The width of a field is the number + of Unicode code points in the string. This differs from C's printf where + the field width is the number of bytes. Either or both of the + flags may be replaced with the character '*', causing their values + to be obtained from the next operand, which must be of type int. + + Other flags: + + always print a sign for numeric values + - pad with spaces on the right rather than the left (left-justify the field) + # alternate format: add leading 0 for octal (%#o), 0x for hex (%#x); + 0X for hex (%#X); suppress 0x for %p (%#p); + print a raw (backquoted) string if possible for %q (%#q) + ' ' (space) leave a space for elided sign in numbers (% d); + put spaces between bytes printing strings or slices in hex (% x, % X) + 0 pad with leading zeros rather than spaces + + For each Printf-like function, there is also a Print function + that takes no format and is equivalent to saying %v for every + operand. Another variant Println inserts blanks between + operands and appends a newline. + + Regardless of the verb, if an operand is an interface value, + the internal concrete value is used, not the interface itself. + Thus: + var i interface{} = 23 + fmt.Printf("%v\n", i) + will print 23. + + If an operand implements interface Formatter, that interface + can be used for fine control of formatting. + + If an operand implements method String() string that method + will be used to convert the object to a string, which will then + be formatted as required by the verb (if any). To avoid + recursion in cases such as + type X int + func (x X) String() string { return Sprintf("%d", x) } + cast the value before recurring: + func (x X) String() string { return Sprintf("%d", int(x)) } + + Format errors: + + If an invalid argument is given for a verb, such as providing + a string to %d, the generated string will contain a + description of the problem, as in these examples: + + Wrong type or unknown verb: %!verb(type=value) + Printf("%d", hi): %!d(string=hi) + Too many arguments: %!(EXTRA type=value) + Printf("hi", "guys"): hi%!(EXTRA string=guys) + Too few arguments: %!verb(MISSING) + Printf("hi%d"): hi %!d(MISSING) + Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC) + Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi + Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi + + All errors begin with the string "%!" followed sometimes + by a single character (the verb) and end with a parenthesized + description. + + Scanning: + + An analogous set of functions scans formatted text to yield + values. Scan, Scanf and Scanln read from os.Stdin; Fscan, + Fscanf and Fscanln read from a specified os.Reader; Sscan, + Sscanf and Sscanln read from an argument string. Sscanln, + Fscanln and Sscanln stop scanning at a newline and require that + the items be followed by one; Sscanf, Fscanf and Sscanf require + newlines in the input to match newlines in the format; the other + routines treat newlines as spaces. + + Scanf, Fscanf, and Sscanf parse the arguments according to a + format string, analogous to that of Printf. For example, %x + will scan an integer as a hexadecimal number, and %v will scan + the default representation format for the value. + + The formats behave analogously to those of Printf with the + following exceptions: + + %p is not implemented + %T is not implemented + %e %E %f %F %g %g are all equivalent and scan any floating point or complex value + %s and %v on strings scan a space-delimited token + + Width is interpreted in the input text (%5s means at most + five runes of input will be read to scan a string) but there + is no syntax for scanning with a precision (no %5.2f, just + %5f). + + When scanning with a format, all non-empty runs of space + characters (except newline) are equivalent to a single + space in both the format and the input. With that proviso, + text in the format string must match the input text; scanning + stops if it does not, with the return value of the function + indicating the number of arguments scanned. + + In all the scanning functions, if an operand implements method + Scan (that is, it implements the Scanner interface) that + method will be used to scan the text for that operand. Also, + if the number of arguments scanned is less than the number of + arguments provided, an error is returned. + + All arguments to be scanned must be either pointers to basic + types or implementations of the Scanner interface. + + Note: Fscan etc. can read one character (rune) past the + input they return, which means that a loop calling a scan + routine may skip some of the input. This is usually a + problem only when there is no space between input values. + However, if the reader provided to Fscan implements UnreadRune, + that method will be used to save the character and successive + calls will not lose data. To attach an UnreadRune method + to a reader without that capability, use bufio.NewReader. +*/ +package fmt diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index 7e59d4073..0aafe6d99 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -45,11 +45,6 @@ func TestFmtInterface(t *testing.T) { } } -type fmtTest struct { - fmt string - val interface{} - out string -} const b32 uint32 = 1<<32 - 1 const b64 uint64 = 1<<64 - 1 @@ -78,268 +73,326 @@ type C struct { B } +type F int + +func (f F) Format(s State, c int) { + Fprintf(s, "<%c=F(%d)>", c, int(f)) +} + +type G int + +func (g G) GoString() string { + return Sprintf("GoString(%d)", int(g)) +} + +type S struct { + f F // a struct field that Formats + g G // a struct field that GoStrings +} + +// A type with a String method with pointer receiver for testing %p +type P int + +var pValue P + +func (p *P) String() string { + return "String(p)" +} + var b byte -var fmttests = []fmtTest{ - fmtTest{"%d", 12345, "12345"}, - fmtTest{"%v", 12345, "12345"}, - fmtTest{"%t", true, "true"}, +var fmttests = []struct { + fmt string + val interface{} + out string +}{ + {"%d", 12345, "12345"}, + {"%v", 12345, "12345"}, + {"%t", true, "true"}, // basic string - fmtTest{"%s", "abc", "abc"}, - fmtTest{"%x", "abc", "616263"}, - fmtTest{"%x", "xyz", "78797a"}, - fmtTest{"%X", "xyz", "78797A"}, - fmtTest{"%q", "abc", `"abc"`}, + {"%s", "abc", "abc"}, + {"%x", "abc", "616263"}, + {"%x", "xyz", "78797a"}, + {"%X", "xyz", "78797A"}, + {"%q", "abc", `"abc"`}, // basic bytes - fmtTest{"%s", []byte("abc"), "abc"}, - fmtTest{"%x", []byte("abc"), "616263"}, - fmtTest{"% x", []byte("abc"), "61 62 63"}, - fmtTest{"%x", []byte("xyz"), "78797a"}, - fmtTest{"%X", []byte("xyz"), "78797A"}, - fmtTest{"%q", []byte("abc"), `"abc"`}, + {"%s", []byte("abc"), "abc"}, + {"%x", []byte("abc"), "616263"}, + {"% x", []byte("abc\xff"), "61 62 63 ff"}, + {"% X", []byte("abc\xff"), "61 62 63 FF"}, + {"%x", []byte("xyz"), "78797a"}, + {"%X", []byte("xyz"), "78797A"}, + {"%q", []byte("abc"), `"abc"`}, // escaped strings - fmtTest{"%#q", `abc`, "`abc`"}, - fmtTest{"%#q", `"`, "`\"`"}, - fmtTest{"1 %#q", `\n`, "1 `\\n`"}, - fmtTest{"2 %#q", "\n", `2 "\n"`}, - fmtTest{"%q", `"`, `"\""`}, - fmtTest{"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`}, - fmtTest{"%q", "abc\xffdef", `"abc\xffdef"`}, - fmtTest{"%q", "\u263a", `"\u263a"`}, - fmtTest{"%q", "\U0010ffff", `"\U0010ffff"`}, + {"%#q", `abc`, "`abc`"}, + {"%#q", `"`, "`\"`"}, + {"1 %#q", `\n`, "1 `\\n`"}, + {"2 %#q", "\n", `2 "\n"`}, + {"%q", `"`, `"\""`}, + {"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`}, + {"%q", "abc\xffdef", `"abc\xffdef"`}, + {"%q", "\u263a", `"\u263a"`}, + {"%q", "\U0010ffff", `"\U0010ffff"`}, // width - fmtTest{"%5s", "abc", " abc"}, - fmtTest{"%2s", "\u263a", " \u263a"}, - fmtTest{"%-5s", "abc", "abc "}, - fmtTest{"%05s", "abc", "00abc"}, + {"%5s", "abc", " abc"}, + {"%2s", "\u263a", " \u263a"}, + {"%-5s", "abc", "abc "}, + {"%05s", "abc", "00abc"}, // integers - fmtTest{"%d", 12345, "12345"}, - fmtTest{"%d", -12345, "-12345"}, - fmtTest{"%10d", 12345, " 12345"}, - fmtTest{"%10d", -12345, " -12345"}, - fmtTest{"%+10d", 12345, " +12345"}, - fmtTest{"%010d", 12345, "0000012345"}, - fmtTest{"%010d", -12345, "-000012345"}, - fmtTest{"%-10d", 12345, "12345 "}, - fmtTest{"%010.3d", 1, " 001"}, - fmtTest{"%010.3d", -1, " -001"}, - fmtTest{"%+d", 12345, "+12345"}, - fmtTest{"%+d", -12345, "-12345"}, - fmtTest{"%+d", 0, "+0"}, - fmtTest{"% d", 0, " 0"}, - fmtTest{"% d", 12345, " 12345"}, + {"%d", 12345, "12345"}, + {"%d", -12345, "-12345"}, + {"%10d", 12345, " 12345"}, + {"%10d", -12345, " -12345"}, + {"%+10d", 12345, " +12345"}, + {"%010d", 12345, "0000012345"}, + {"%010d", -12345, "-000012345"}, + {"%-10d", 12345, "12345 "}, + {"%010.3d", 1, " 001"}, + {"%010.3d", -1, " -001"}, + {"%+d", 12345, "+12345"}, + {"%+d", -12345, "-12345"}, + {"%+d", 0, "+0"}, + {"% d", 0, " 0"}, + {"% d", 12345, " 12345"}, + + // unicode format + {"%U", 0x1, "U+0001"}, + {"%.8U", 0x2, "U+00000002"}, + {"%U", 0x1234, "U+1234"}, + {"%U", 0x12345, "U+12345"}, + {"%10.6U", 0xABC, " U+000ABC"}, + {"%-10.6U", 0xABC, "U+000ABC "}, // floats - fmtTest{"%+.3e", 0.0, "+0.000e+00"}, - fmtTest{"%+.3e", 1.0, "+1.000e+00"}, - fmtTest{"%+.3f", -1.0, "-1.000"}, - fmtTest{"% .3E", -1.0, "-1.000E+00"}, - fmtTest{"% .3e", 1.0, " 1.000e+00"}, - fmtTest{"%+.3g", 0.0, "+0"}, - fmtTest{"%+.3g", 1.0, "+1"}, - fmtTest{"%+.3g", -1.0, "-1"}, - fmtTest{"% .3g", -1.0, "-1"}, - fmtTest{"% .3g", 1.0, " 1"}, + {"%+.3e", 0.0, "+0.000e+00"}, + {"%+.3e", 1.0, "+1.000e+00"}, + {"%+.3f", -1.0, "-1.000"}, + {"% .3E", -1.0, "-1.000E+00"}, + {"% .3e", 1.0, " 1.000e+00"}, + {"%+.3g", 0.0, "+0"}, + {"%+.3g", 1.0, "+1"}, + {"%+.3g", -1.0, "-1"}, + {"% .3g", -1.0, "-1"}, + {"% .3g", 1.0, " 1"}, // complex values - fmtTest{"%+.3e", 0i, "(+0.000e+00+0.000e+00i)"}, - fmtTest{"%+.3f", 0i, "(+0.000+0.000i)"}, - fmtTest{"%+.3g", 0i, "(+0+0i)"}, - fmtTest{"%+.3e", 1 + 2i, "(+1.000e+00+2.000e+00i)"}, - fmtTest{"%+.3f", 1 + 2i, "(+1.000+2.000i)"}, - fmtTest{"%+.3g", 1 + 2i, "(+1+2i)"}, - fmtTest{"%.3e", 0i, "(0.000e+00+0.000e+00i)"}, - fmtTest{"%.3f", 0i, "(0.000+0.000i)"}, - fmtTest{"%.3g", 0i, "(0+0i)"}, - fmtTest{"%.3e", 1 + 2i, "(1.000e+00+2.000e+00i)"}, - fmtTest{"%.3f", 1 + 2i, "(1.000+2.000i)"}, - fmtTest{"%.3g", 1 + 2i, "(1+2i)"}, - fmtTest{"%.3e", -1 - 2i, "(-1.000e+00-2.000e+00i)"}, - fmtTest{"%.3f", -1 - 2i, "(-1.000-2.000i)"}, - fmtTest{"%.3g", -1 - 2i, "(-1-2i)"}, - fmtTest{"% .3E", -1 - 2i, "(-1.000E+00-2.000E+00i)"}, - fmtTest{"%+.3g", complex64(1 + 2i), "(+1+2i)"}, - fmtTest{"%+.3g", complex128(1 + 2i), "(+1+2i)"}, + {"%+.3e", 0i, "(+0.000e+00+0.000e+00i)"}, + {"%+.3f", 0i, "(+0.000+0.000i)"}, + {"%+.3g", 0i, "(+0+0i)"}, + {"%+.3e", 1 + 2i, "(+1.000e+00+2.000e+00i)"}, + {"%+.3f", 1 + 2i, "(+1.000+2.000i)"}, + {"%+.3g", 1 + 2i, "(+1+2i)"}, + {"%.3e", 0i, "(0.000e+00+0.000e+00i)"}, + {"%.3f", 0i, "(0.000+0.000i)"}, + {"%.3g", 0i, "(0+0i)"}, + {"%.3e", 1 + 2i, "(1.000e+00+2.000e+00i)"}, + {"%.3f", 1 + 2i, "(1.000+2.000i)"}, + {"%.3g", 1 + 2i, "(1+2i)"}, + {"%.3e", -1 - 2i, "(-1.000e+00-2.000e+00i)"}, + {"%.3f", -1 - 2i, "(-1.000-2.000i)"}, + {"%.3g", -1 - 2i, "(-1-2i)"}, + {"% .3E", -1 - 2i, "(-1.000E+00-2.000E+00i)"}, + {"%+.3g", complex64(1 + 2i), "(+1+2i)"}, + {"%+.3g", complex128(1 + 2i), "(+1+2i)"}, // erroneous formats - fmtTest{"", 2, "?(extra int=2)"}, - fmtTest{"%d", "hello", "%d(string=hello)"}, + {"", 2, "%!(EXTRA int=2)"}, + {"%d", "hello", "%!d(string=hello)"}, // old test/fmt_test.go - fmtTest{"%d", 1234, "1234"}, - fmtTest{"%d", -1234, "-1234"}, - fmtTest{"%d", uint(1234), "1234"}, - fmtTest{"%d", uint32(b32), "4294967295"}, - fmtTest{"%d", uint64(b64), "18446744073709551615"}, - fmtTest{"%o", 01234, "1234"}, - fmtTest{"%#o", 01234, "01234"}, - fmtTest{"%o", uint32(b32), "37777777777"}, - fmtTest{"%o", uint64(b64), "1777777777777777777777"}, - fmtTest{"%x", 0x1234abcd, "1234abcd"}, - fmtTest{"%#x", 0x1234abcd, "0x1234abcd"}, - fmtTest{"%x", b32 - 0x1234567, "fedcba98"}, - fmtTest{"%X", 0x1234abcd, "1234ABCD"}, - fmtTest{"%X", b32 - 0x1234567, "FEDCBA98"}, - fmtTest{"%#X", 0, "0X0"}, - fmtTest{"%x", b64, "ffffffffffffffff"}, - fmtTest{"%b", 7, "111"}, - fmtTest{"%b", b64, "1111111111111111111111111111111111111111111111111111111111111111"}, - fmtTest{"%b", -6, "-110"}, - 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{"%f", float64(1234.5678e3), "1234567.800000"}, - fmtTest{"%f", float64(1234.5678e-8), "0.000012"}, - fmtTest{"%f", float64(-7), "-7.000000"}, - fmtTest{"%f", float64(-1e-9), "-0.000000"}, - 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{"%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, "本"}, - fmtTest{"%c", '日', "日"}, - fmtTest{"%20.8d", 1234, " 00001234"}, - fmtTest{"%20.8d", -1234, " -00001234"}, - fmtTest{"%20d", 1234, " 1234"}, - fmtTest{"%-20.8d", 1234, "00001234 "}, - fmtTest{"%-20.8d", -1234, "-00001234 "}, - fmtTest{"%-#20.8x", 0x1234abc, "0x01234abc "}, - fmtTest{"%-#20.8X", 0x1234abc, "0X01234ABC "}, - fmtTest{"%-#20.8o", 01234, "00001234 "}, - fmtTest{"%.20b", 7, "00000000000000000111"}, - fmtTest{"%20.5s", "qwertyuiop", " qwert"}, - fmtTest{"%.5s", "qwertyuiop", "qwert"}, - fmtTest{"%-20.5s", "qwertyuiop", "qwert "}, - fmtTest{"%20c", 'x', " x"}, - fmtTest{"%-20c", 'x', "x "}, - fmtTest{"%20.6e", 1.2345e3, " 1.234500e+03"}, - fmtTest{"%20.6e", 1.2345e-3, " 1.234500e-03"}, - fmtTest{"%20e", 1.2345e3, " 1.234500e+03"}, - fmtTest{"%20e", 1.2345e-3, " 1.234500e-03"}, - fmtTest{"%20.8e", 1.2345e3, " 1.23450000e+03"}, - fmtTest{"%20f", float64(1.23456789e3), " 1234.567890"}, - fmtTest{"%20f", float64(1.23456789e-3), " 0.001235"}, - fmtTest{"%20f", float64(12345678901.23456789), " 12345678901.234568"}, - fmtTest{"%-20f", float64(1.23456789e3), "1234.567890 "}, - fmtTest{"%20.8f", float64(1.23456789e3), " 1234.56789000"}, - fmtTest{"%20.8f", float64(1.23456789e-3), " 0.00123457"}, - fmtTest{"%g", float64(1.23456789e3), "1234.56789"}, - fmtTest{"%g", float64(1.23456789e-3), "0.00123456789"}, - fmtTest{"%g", float64(1.23456789e20), "1.23456789e+20"}, - fmtTest{"%20e", math.Inf(1), " +Inf"}, - fmtTest{"%-20f", math.Inf(-1), "-Inf "}, - fmtTest{"%20g", math.NaN(), " NaN"}, + {"%d", 1234, "1234"}, + {"%d", -1234, "-1234"}, + {"%d", uint(1234), "1234"}, + {"%d", uint32(b32), "4294967295"}, + {"%d", uint64(b64), "18446744073709551615"}, + {"%o", 01234, "1234"}, + {"%#o", 01234, "01234"}, + {"%o", uint32(b32), "37777777777"}, + {"%o", uint64(b64), "1777777777777777777777"}, + {"%x", 0x1234abcd, "1234abcd"}, + {"%#x", 0x1234abcd, "0x1234abcd"}, + {"%x", b32 - 0x1234567, "fedcba98"}, + {"%X", 0x1234abcd, "1234ABCD"}, + {"%X", b32 - 0x1234567, "FEDCBA98"}, + {"%#X", 0, "0X0"}, + {"%x", b64, "ffffffffffffffff"}, + {"%b", 7, "111"}, + {"%b", b64, "1111111111111111111111111111111111111111111111111111111111111111"}, + {"%b", -6, "-110"}, + {"%e", float64(1), "1.000000e+00"}, + {"%e", float64(1234.5678e3), "1.234568e+06"}, + {"%e", float64(1234.5678e-8), "1.234568e-05"}, + {"%e", float64(-7), "-7.000000e+00"}, + {"%e", float64(-1e-9), "-1.000000e-09"}, + {"%f", float64(1234.5678e3), "1234567.800000"}, + {"%f", float64(1234.5678e-8), "0.000012"}, + {"%f", float64(-7), "-7.000000"}, + {"%f", float64(-1e-9), "-0.000000"}, + {"%g", float64(1234.5678e3), "1.2345678e+06"}, + {"%g", float32(1234.5678e3), "1.2345678e+06"}, + {"%g", float64(1234.5678e-8), "1.2345678e-05"}, + {"%g", float64(-7), "-7"}, + {"%g", float64(-1e-9), "-1e-09"}, + {"%g", float32(-1e-9), "-1e-09"}, + {"%E", float64(1), "1.000000E+00"}, + {"%E", float64(1234.5678e3), "1.234568E+06"}, + {"%E", float64(1234.5678e-8), "1.234568E-05"}, + {"%E", float64(-7), "-7.000000E+00"}, + {"%E", float64(-1e-9), "-1.000000E-09"}, + {"%G", float64(1234.5678e3), "1.2345678E+06"}, + {"%G", float32(1234.5678e3), "1.2345678E+06"}, + {"%G", float64(1234.5678e-8), "1.2345678E-05"}, + {"%G", float64(-7), "-7"}, + {"%G", float64(-1e-9), "-1E-09"}, + {"%G", float32(-1e-9), "-1E-09"}, + {"%c", 'x', "x"}, + {"%c", 0xe4, "ä"}, + {"%c", 0x672c, "本"}, + {"%c", '日', "日"}, + {"%20.8d", 1234, " 00001234"}, + {"%20.8d", -1234, " -00001234"}, + {"%20d", 1234, " 1234"}, + {"%-20.8d", 1234, "00001234 "}, + {"%-20.8d", -1234, "-00001234 "}, + {"%-#20.8x", 0x1234abc, "0x01234abc "}, + {"%-#20.8X", 0x1234abc, "0X01234ABC "}, + {"%-#20.8o", 01234, "00001234 "}, + {"%.20b", 7, "00000000000000000111"}, + {"%20.5s", "qwertyuiop", " qwert"}, + {"%.5s", "qwertyuiop", "qwert"}, + {"%-20.5s", "qwertyuiop", "qwert "}, + {"%20c", 'x', " x"}, + {"%-20c", 'x', "x "}, + {"%20.6e", 1.2345e3, " 1.234500e+03"}, + {"%20.6e", 1.2345e-3, " 1.234500e-03"}, + {"%20e", 1.2345e3, " 1.234500e+03"}, + {"%20e", 1.2345e-3, " 1.234500e-03"}, + {"%20.8e", 1.2345e3, " 1.23450000e+03"}, + {"%20f", float64(1.23456789e3), " 1234.567890"}, + {"%20f", float64(1.23456789e-3), " 0.001235"}, + {"%20f", float64(12345678901.23456789), " 12345678901.234568"}, + {"%-20f", float64(1.23456789e3), "1234.567890 "}, + {"%20.8f", float64(1.23456789e3), " 1234.56789000"}, + {"%20.8f", float64(1.23456789e-3), " 0.00123457"}, + {"%g", float64(1.23456789e3), "1234.56789"}, + {"%g", float64(1.23456789e-3), "0.00123456789"}, + {"%g", float64(1.23456789e20), "1.23456789e+20"}, + {"%20e", math.Inf(1), " +Inf"}, + {"%-20f", math.Inf(-1), "-Inf "}, + {"%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>]"}, + {"%v", array, "[1 2 3 4 5]"}, + {"%v", iarray, "[1 hello 2.5 <nil>]"}, + {"%v", &array, "&[1 2 3 4 5]"}, + {"%v", &iarray, "&[1 hello 2.5 <nil>]"}, // complexes with %v - fmtTest{"%v", 1 + 2i, "(1+2i)"}, - fmtTest{"%v", complex64(1 + 2i), "(1+2i)"}, - fmtTest{"%v", complex128(1 + 2i), "(1+2i)"}, + {"%v", 1 + 2i, "(1+2i)"}, + {"%v", complex64(1 + 2i), "(1+2i)"}, + {"%v", complex128(1 + 2i), "(1+2i)"}, // 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]}`}, + {"%v", A{1, 2, "a", []int{1, 2}}, `{1 2 a [1 2]}`}, + {"%+v", A{1, 2, "a", []int{1, 2}}, `{i:1 j:2 s:a x:[1 2]}`}, // +v on structs with Stringable items - fmtTest{"%+v", B{1, 2}, `{i:<1> j:2}`}, - fmtTest{"%+v", C{1, B{2, 3}}, `{i:1 B:{i:<2> j:3}}`}, + {"%+v", B{1, 2}, `{i:<1> j:2}`}, + {"%+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"}, - fmtTest{"%p", make(map[int]int), "PTR"}, - fmtTest{"%p", make([]int, 1), "PTR"}, + {"%s", I(23), `<23>`}, + {"%q", I(23), `"<23>"`}, + {"%x", I(23), `3c32333e`}, + {"%d", I(23), `%!d(string=<23>)`}, // 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"}, - fmtTest{"%#v", map[string]int{"a": 1, "b": 2}, `map[string] int{"a":1, "b":2}`}, - fmtTest{"%#v", map[string]B{"a": B{1, 2}, "b": B{3, 4}}, `map[string] fmt_test.B{"a":fmt_test.B{i:1, j:2}, "b":fmt_test.B{i:3, j:4}}`}, - fmtTest{"%#v", []string{"a", "b"}, `[]string{"a", "b"}`}, + {"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`}, + {"%#v", &b, "(*uint8)(PTR)"}, + {"%#v", TestFmtInterface, "(func(*testing.T))(PTR)"}, + {"%#v", make(chan int), "(chan int)(PTR)"}, + {"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"}, + {"%#v", 1000000000, "1000000000"}, + {"%#v", map[string]int{"a": 1, "b": 2}, `map[string] int{"a":1, "b":2}`}, + {"%#v", map[string]B{"a": {1, 2}, "b": {3, 4}}, `map[string] fmt_test.B{"a":fmt_test.B{i:1, j:2}, "b":fmt_test.B{i:3, j:4}}`}, + {"%#v", []string{"a", "b"}, `[]string{"a", "b"}`}, // slices with other formats - fmtTest{"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`}, - fmtTest{"%x", []int{1, 2, 15}, `[1 2 f]`}, - fmtTest{"%q", []string{"a", "b"}, `["a" "b"]`}, + {"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`}, + {"%x", []int{1, 2, 15}, `[1 2 f]`}, + {"%d", []int{1, 2, 15}, `[1 2 15]`}, + {"%d", []byte{1, 2, 15}, `[1 2 15]`}, + {"%q", []string{"a", "b"}, `["a" "b"]`}, // 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)"}, + {"%v", renamedBool(true), "true"}, + {"%d", renamedBool(true), "%!d(fmt_test.renamedBool=true)"}, + {"%o", renamedInt(8), "10"}, + {"%d", renamedInt8(-9), "-9"}, + {"%v", renamedInt16(10), "10"}, + {"%v", renamedInt32(-11), "-11"}, + {"%X", renamedInt64(255), "FF"}, + {"%v", renamedUint(13), "13"}, + {"%o", renamedUint8(14), "16"}, + {"%X", renamedUint16(15), "F"}, + {"%d", renamedUint32(16), "16"}, + {"%X", renamedUint64(17), "11"}, + {"%o", renamedUintptr(18), "22"}, + {"%x", renamedString("thing"), "7468696e67"}, + {"%d", renamedBytes([]byte{1, 2, 15}), `[1 2 15]`}, + {"%q", renamedBytes([]byte("hello")), `"hello"`}, + {"%v", renamedFloat(11), "11"}, + {"%v", renamedFloat32(22), "22"}, + {"%v", renamedFloat64(33), "33"}, + {"%v", renamedComplex(7 + .2i), "(7+0.2i)"}, + {"%v", renamedComplex64(3 + 4i), "(3+4i)"}, + {"%v", renamedComplex128(4 - 3i), "(4-3i)"}, + + // Formatter + {"%x", F(1), "<x=F(1)>"}, + {"%x", G(2), "2"}, + {"%+v", S{F(4), G(5)}, "{f:<v=F(4)> g:5}"}, + + // GoStringer + {"%#v", G(6), "GoString(6)"}, + {"%#v", S{F(7), G(8)}, "fmt_test.S{f:<v=F(7)>, g:GoString(8)}"}, // %T - fmtTest{"%T", (4 - 3i), "complex"}, - fmtTest{"%T", renamedComplex128(4 - 3i), "fmt_test.renamedComplex128"}, - fmtTest{"%T", intVal, "int"}, - fmtTest{"%6T", &intVal, " *int"}, + {"%T", (4 - 3i), "complex"}, + {"%T", renamedComplex128(4 - 3i), "fmt_test.renamedComplex128"}, + {"%T", intVal, "int"}, + {"%6T", &intVal, " *int"}, + + // %p + {"p0=%p", new(int), "p0=PTR"}, + {"p1=%s", &pValue, "p1=String(p)"}, // String method... + {"p2=%p", &pValue, "p2=PTR"}, // ... not called with %p + + // %p on non-pointers + {"%p", make(chan int), "PTR"}, + {"%p", make(map[int]int), "PTR"}, + {"%p", make([]int, 1), "PTR"}, + {"%p", 27, "%!p(int=27)"}, // not a pointer at all // erroneous things - fmtTest{"%d", "hello", "%d(string=hello)"}, - fmtTest{"no args", "hello", "no args?(extra string=hello)"}, - fmtTest{"%s", nil, "%s(<nil>)"}, - fmtTest{"%T", nil, "<nil>"}, - fmtTest{"%-1", 100, "%1(int=100)"}, + {"%s %", "hello", "hello %!(NOVERB)"}, + {"%s %.2", "hello", "hello %!(NOVERB)"}, + {"%d", "hello", "%!d(string=hello)"}, + {"no args", "hello", "no args%!(EXTRA string=hello)"}, + {"%s", nil, "%!s(<nil>)"}, + {"%T", nil, "<nil>"}, + {"%-1", 100, "%!(NOVERB)%!(EXTRA int=100)"}, } func TestSprintf(t *testing.T) { for _, tt := range fmttests { s := Sprintf(tt.fmt, tt.val) - if i := strings.Index(s, "0x"); i >= 0 && strings.Index(tt.out, "PTR") >= 0 { + if i := strings.Index(s, "0x"); i >= 0 && strings.Contains(tt.out, "PTR") { j := i + 2 for ; j < len(s); j++ { c := s[j] @@ -385,6 +438,12 @@ func BenchmarkSprintfIntInt(b *testing.B) { } } +func BenchmarkSprintfPrefixedInt(b *testing.B) { + for i := 0; i < b.N; i++ { + Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6) + } +} + func TestCountMallocs(t *testing.T) { mallocs := 0 - runtime.MemStats.Mallocs for i := 0; i < 100; i++ { @@ -431,24 +490,22 @@ func (*flagPrinter) Format(f State, c int) { io.WriteString(f, "["+s+"]") } -type flagTest struct { +var flagtests = []struct { in string out string -} - -var flagtests = []flagTest{ - flagTest{"%a", "[%a]"}, - flagTest{"%-a", "[%-a]"}, - flagTest{"%+a", "[%+a]"}, - flagTest{"%#a", "[%#a]"}, - flagTest{"% a", "[% a]"}, - flagTest{"%0a", "[%0a]"}, - flagTest{"%1.2a", "[%1.2a]"}, - flagTest{"%-1.2a", "[%-1.2a]"}, - flagTest{"%+1.2a", "[%+1.2a]"}, - flagTest{"%-+1.2a", "[%+-1.2a]"}, - flagTest{"%-+1.2abc", "[%+-1.2a]bc"}, - flagTest{"%-1.2abc", "[%-1.2a]bc"}, +}{ + {"%a", "[%a]"}, + {"%-a", "[%-a]"}, + {"%+a", "[%+a]"}, + {"%#a", "[%#a]"}, + {"% a", "[% a]"}, + {"%0a", "[%0a]"}, + {"%1.2a", "[%1.2a]"}, + {"%-1.2a", "[%-1.2a]"}, + {"%+1.2a", "[%+1.2a]"}, + {"%-+1.2a", "[%+-1.2a]"}, + {"%-+1.2abc", "[%+-1.2a]bc"}, + {"%-1.2abc", "[%-1.2a]bc"}, } func TestFlagParser(t *testing.T) { @@ -470,13 +527,12 @@ func TestStructPrinter(t *testing.T) { s.a = "abc" s.b = "def" s.c = 123 - type Test struct { + var tests = []struct { fmt string out string - } - var tests = []Test{ - Test{"%v", "{abc def 123}"}, - Test{"%+v", "{a:abc b:def c:123}"}, + }{ + {"%v", "{abc def 123}"}, + {"%+v", "{a:abc b:def c:123}"}, } for _, tt := range tests { out := Sprintf(tt.fmt, s) @@ -526,3 +582,73 @@ func TestEmptyMap(t *testing.T) { t.Errorf("empty map printed as %q not %q", s, emptyMapStr) } } + +// Check that Sprint (and hence Print, Fprint) puts spaces in the right places, +// that is, between arg pairs in which neither is a string. +func TestBlank(t *testing.T) { + got := Sprint("<", 1, ">:", 1, 2, 3, "!") + expect := "<1>:1 2 3!" + if got != expect { + t.Errorf("got %q expected %q", got, expect) + } +} + +// Check that Sprintln (and hence Println, Fprintln) puts spaces in the right places, +// that is, between all arg pairs. +func TestBlankln(t *testing.T) { + got := Sprintln("<", 1, ">:", 1, 2, 3, "!") + expect := "< 1 >: 1 2 3 !\n" + if got != expect { + t.Errorf("got %q expected %q", got, expect) + } +} + + +// Check Formatter with Sprint, Sprintln, Sprintf +func TestFormatterPrintln(t *testing.T) { + f := F(1) + expect := "<v=F(1)>\n" + s := Sprint(f, "\n") + if s != expect { + t.Errorf("Sprint wrong with Formatter: expected %q got %q", expect, s) + } + s = Sprintln(f) + if s != expect { + t.Errorf("Sprintln wrong with Formatter: expected %q got %q", expect, s) + } + s = Sprintf("%v\n", f) + if s != expect { + t.Errorf("Sprintf wrong with Formatter: expected %q got %q", expect, s) + } +} + +func args(a ...interface{}) []interface{} { return a } + +var startests = []struct { + fmt string + in []interface{} + out string +}{ + {"%*d", args(4, 42), " 42"}, + {"%.*d", args(4, 42), "0042"}, + {"%*.*d", args(8, 4, 42), " 0042"}, + {"%0*d", args(4, 42), "0042"}, + {"%-*d", args(4, 42), "42 "}, + + // erroneous + {"%*d", args(nil, 42), "%!(BADWIDTH)42"}, + {"%.*d", args(nil, 42), "%!(BADPREC)42"}, + {"%*d", args(5, "foo"), "%!d(string= foo)"}, + {"%*% %d", args(20, 5), "% 5"}, + {"%*", args(4), "%!(NOVERB)"}, + {"%*d", args(int32(4), 42), "%!(BADWIDTH)42"}, +} + +func TestWidthAndPrecision(t *testing.T) { + for _, tt := range startests { + s := Sprintf(tt.fmt, tt.in...) + if s != tt.out { + t.Errorf("%q: got %q expected %q", tt.fmt, s, tt.out) + } + } +} diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go index 3ec1cf139..0121dda31 100644 --- a/src/pkg/fmt/format.go +++ b/src/pkg/fmt/format.go @@ -49,6 +49,7 @@ type fmt struct { plus bool sharp bool space bool + unicode bool zero bool } @@ -61,6 +62,7 @@ func (f *fmt) clearflags() { f.plus = false f.sharp = false f.space = false + f.unicode = false f.zero = false } @@ -213,6 +215,12 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) { buf[i] = '0' } } + if f.unicode { + i-- + buf[i] = '+' + i-- + buf[i] = 'U' + } if negative { i-- @@ -255,6 +263,9 @@ func (f *fmt) fmt_sx(s string) { func (f *fmt) fmt_sX(s string) { t := "" for i := 0; i < len(s); i++ { + if i > 0 && f.space { + t += " " + } v := s[i] t += string(udigits[v>>4]) t += string(udigits[v&0xF]) diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index 20bfa9107..412260441 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -2,132 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -/* - Package fmt implements formatted I/O with functions analogous - to C's printf and scanf. The format 'verbs' are derived from C's but - are simpler. - - Printing: - - The verbs: - - General: - %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: - %b base 2 - %c the character represented by the corresponding Unicode code point - %d base 10 - %o base 8 - %x base 16, with lower-case letters for a-f - %X base 16, with upper-case letters for A-F - Floating-point and complex constituents: - %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 - - 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). - - For numeric values, the width and precision flags control - formatting; width sets the width of the field, precision the - number of places after the decimal, if appropriate. The - format %6.2f prints 123.45. The width of a field is the number - of Unicode code points in the string. This differs from C's printf where - the field width is the number of bytes. - - Other flags: - + always print a sign for numeric values - - pad with spaces on the right rather than the left (left-justify the field) - # alternate format: add leading 0 for octal (%#o), 0x for hex (%#x); - suppress 0x for %p (%#p); - print a raw (backquoted) string if possible for %q (%#q) - ' ' (space) leave a space for elided sign in numbers (% d); - put spaces between bytes printing strings or slices in hex (% x) - 0 pad with leading zeros rather than spaces - - For each Printf-like function, there is also a Print function - that takes no format and is equivalent to saying %v for every - operand. Another variant Println inserts blanks between - operands and appends a newline. - - Regardless of the verb, if an operand is an interface value, - the internal concrete value is used, not the interface itself. - Thus: - var i interface{} = 23; - fmt.Printf("%v\n", i); - will print 23. - - If an operand implements interface Formatter, that interface - can be used for fine control of formatting. - - If an operand implements method String() string that method - will be used to conver the object to a string, which will then - be formatted as required by the verb (if any). To avoid - recursion in cases such as - type X int - func (x X) String() string { return Sprintf("%d", x) } - cast the value before recurring: - func (x X) String() string { return Sprintf("%d", int(x)) } - - Scanning: - - An analogous set of functions scans formatted text to yield - values. Scan, Scanf and Scanln read from os.Stdin; Fscan, - Fscanf and Fscanln read from a specified os.Reader; Sscan, - Sscanf and Sscanln read from an argument string. Sscanln, - Fscanln and Sscanln stop scanning at a newline and require that - the items be followed by one; the other routines treat newlines - as spaces. - - Scanf, Fscanf, and Sscanf parse the arguments according to a - format string, analogous to that of Printf. For example, "%x" - will scan an integer as a hexadecimal number, and %v will scan - the default representation format for the value. - - The formats behave analogously to those of Printf with the - following exceptions: - - %p is not implemented - %T is not implemented - %e %E %f %F %g %g are all equivalent and scan any floating - point or complex value - %s and %v on strings scan a space-delimited token - - Width is interpreted in the input text (%5s means at most - five runes of input will be read to scan a string) but there - is no syntax for scanning with a precision (no %5.2f, just - %5f). - - When scanning with a format, all non-empty runs of space - characters (including newline) are equivalent to a single - space in both the format and the input. With that proviso, - text in the format string must match the input text; scanning - stops if it does not, with the return value of the function - indicating the number of arguments scanned. - - In all the scanning functions, if an operand implements method - Scan (that is, it implements the Scanner interface) that - method will be used to scan the text for that operand. Also, - if the number of arguments scanned is less than the number of - arguments provided, an error is returned. - - All arguments to be scanned must be either pointers to basic - types or implementations of the Scanner interface. -*/ package fmt import ( @@ -146,10 +20,13 @@ var ( nilParenBytes = []byte("(nil)") nilBytes = []byte("nil") mapBytes = []byte("map[") - missingBytes = []byte("missing") - extraBytes = []byte("?(extra ") + missingBytes = []byte("(MISSING)") + extraBytes = []byte("%!(EXTRA ") irparenBytes = []byte("i)") bytesBytes = []byte("[]byte{") + widthBytes = []byte("%!(BADWIDTH)") + precBytes = []byte("%!(BADPREC)") + noVerbBytes = []byte("%!(NOVERB)") ) // State represents the printer state passed to custom formatters. @@ -241,12 +118,7 @@ func (p *pp) Flag(b int) bool { } func (p *pp) add(c int) { - if c < utf8.RuneSelf { - p.buf.WriteByte(byte(c)) - } else { - w := utf8.EncodeRune(c, p.runeBuf[0:]) - p.buf.Write(p.runeBuf[0:w]) - } + p.buf.WriteRune(c) } // Implement Write so we can call Fprintf on a pp (through State), for @@ -258,6 +130,7 @@ func (p *pp) Write(b []byte) (ret int, err os.Error) { // These routines end in 'f' and take a format string. // Fprintf formats according to a format specifier and writes to w. +// It returns the number of bytes written and any write error encountered. func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error os.Error) { p := newPrinter() p.doPrintf(format, a) @@ -267,8 +140,9 @@ func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error os.Erro } // Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. func Printf(format string, a ...interface{}) (n int, errno os.Error) { - n, errno = Fprintf(os.Stdout, format, a) + n, errno = Fprintf(os.Stdout, format, a...) return n, errno } @@ -281,10 +155,17 @@ func Sprintf(format string, a ...interface{}) string { return s } +// Errorf formats according to a format specifier and returns the string +// converted to an os.ErrorString, which satisfies the os.Error interface. +func Errorf(format string, a ...interface{}) os.Error { + return os.ErrorString(Sprintf(format, a...)) +} + // These routines do not take a format string // Fprint formats using the default formats for its operands and writes to w. // Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) { p := newPrinter() p.doPrint(a, false, false) @@ -295,8 +176,9 @@ func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) { // Print formats using the default formats for its operands and writes to standard output. // Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. func Print(a ...interface{}) (n int, errno os.Error) { - n, errno = Fprint(os.Stdout, a) + n, errno = Fprint(os.Stdout, a...) return n, errno } @@ -316,6 +198,7 @@ func Sprint(a ...interface{}) string { // Fprintln formats using the default formats for its operands and writes to w. // Spaces are always added between operands and a newline is appended. +// It returns the number of bytes written and any write error encountered. func Fprintln(w io.Writer, a ...interface{}) (n int, error os.Error) { p := newPrinter() p.doPrint(a, true, true) @@ -326,8 +209,9 @@ func Fprintln(w io.Writer, a ...interface{}) (n int, error os.Error) { // Println formats using the default formats for its operands and writes to standard output. // Spaces are always added between operands and a newline is appended. +// It returns the number of bytes written and any write error encountered. func Println(a ...interface{}) (n int, errno os.Error) { - n, errno = Fprintln(os.Stdout, a) + n, errno = Fprintln(os.Stdout, a...) return n, errno } @@ -384,6 +268,7 @@ func (p *pp) unknownType(v interface{}) { func (p *pp) badVerb(verb int, val interface{}) { p.add('%') + p.add('!') p.add(verb) p.add('(') if val == nil { @@ -411,7 +296,7 @@ func (p *pp) fmtC(c int64) { if int64(rune) != c { rune = utf8.RuneError } - w := utf8.EncodeRune(rune, p.runeBuf[0:utf8.UTFMax]) + w := utf8.EncodeRune(p.runeBuf[0:utf8.UTFMax], rune) p.fmt.pad(p.runeBuf[0:w]) } @@ -427,6 +312,8 @@ func (p *pp) fmtInt64(v int64, verb int, value interface{}) { p.fmt.integer(v, 8, signed, ldigits) case 'x': p.fmt.integer(v, 16, signed, ldigits) + case 'U': + p.fmtUnicode(v) case 'X': p.fmt.integer(v, 16, signed, udigits) default: @@ -434,7 +321,7 @@ func (p *pp) fmtInt64(v int64, verb int, value interface{}) { } } -// fmt_sharpHex64 formats a uint64 in hexadecimal and prefixes it with 0x by +// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x by // temporarily turning on the sharp flag. func (p *pp) fmt0x64(v uint64) { sharp := p.fmt.sharp @@ -443,6 +330,23 @@ func (p *pp) fmt0x64(v uint64) { p.fmt.sharp = sharp } +// fmtUnicode formats a uint64 in U+1234 form by +// temporarily turning on the unicode flag and tweaking the precision. +func (p *pp) fmtUnicode(v int64) { + precPresent := p.fmt.precPresent + prec := p.fmt.prec + if !precPresent { + // If prec is already set, leave it alone; otherwise 4 is minimum. + p.fmt.prec = 4 + p.fmt.precPresent = true + } + p.fmt.unicode = true // turn on U+ + p.fmt.integer(int64(v), 16, unsigned, udigits) + p.fmt.unicode = false + p.fmt.prec = prec + p.fmt.precPresent = precPresent +} + func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) { switch verb { case 'b': @@ -550,7 +454,7 @@ func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}) { } func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interface{}) { - if verb == 'v' { + if verb == 'v' || verb == 'd' { if goSyntax { p.buf.Write(bytesBytes) } else { @@ -588,13 +492,14 @@ func (p *pp) fmtBytes(v []byte, verb int, goSyntax bool, depth int, value interf } } -func (p *pp) fmtUintptrGetter(field interface{}, value reflect.Value, verb int, sharp bool) bool { +func (p *pp) fmtPointer(field interface{}, value reflect.Value, verb int, goSyntax bool) { v, ok := value.(uintptrGetter) - if !ok { - return false + if !ok { // reflect.PtrValue is a uintptrGetter, so failure means it's not a pointer at all. + p.badVerb(verb, field) + return } u := v.Get() - if sharp { + if goSyntax { p.add('(') p.buf.WriteString(reflect.Typeof(field).String()) p.add(')') @@ -608,7 +513,6 @@ func (p *pp) fmtUintptrGetter(field interface{}, value reflect.Value, verb int, } else { p.fmt0x64(uint64(u)) } - return true } var ( @@ -618,19 +522,49 @@ var ( uintptrBits = reflect.Typeof(uintptr(0)).Bits() ) -func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (was_string bool) { - if field != nil { - switch { - default: - if stringer, ok := field.(Stringer); ok { - p.printField(stringer.String(), verb, plus, goSyntax, depth) - return false // this value is not a string - } - case goSyntax: - if stringer, ok := field.(GoStringer); ok { - p.printField(stringer.GoString(), verb, plus, goSyntax, depth) - return false // this value is not a string - } +func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString bool) { + if field == nil { + if verb == 'T' || verb == 'v' { + p.buf.Write(nilAngleBytes) + } else { + p.badVerb(verb, field) + } + return false + } + + // Special processing considerations. + // %T (the value's type) and %p (its address) are special; we always do them first. + switch verb { + case 'T': + p.printField(reflect.Typeof(field).String(), 's', false, false, 0) + return false + case 'p': + p.fmtPointer(field, reflect.NewValue(field), verb, goSyntax) + return false + } + // Is it a Formatter? + if formatter, ok := field.(Formatter); ok { + formatter.Format(p, verb) + return false // this value is not a string + + } + // Must not touch flags before Formatter looks at them. + if plus { + p.fmt.plus = false + } + // If we're doing Go syntax and the field knows how to supply it, take care of it now. + if goSyntax { + p.fmt.sharp = false + if stringer, ok := field.(GoStringer); ok { + // Print the result of GoString unadorned. + p.fmtString(stringer.GoString(), 's', false, field) + return false // this value is not a string + } + } else { + // Is it a Stringer? + if stringer, ok := field.(Stringer); ok { + p.printField(stringer.String(), verb, plus, false, depth) + return false // this value is not a string } } @@ -706,21 +640,8 @@ func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth return verb == 's' } - if field == nil { - if verb == 'v' { - p.buf.Write(nilAngleBytes) - } else { - p.badVerb(verb, field) - } - return false - } - - value := reflect.NewValue(field) // Need to use reflection - // Special case for reflection values that know how to print with %p. - if verb == 'p' && p.fmtUintptrGetter(field, value, verb, goSyntax) { // TODO: is this goSyntax right? - return false - } + value := reflect.NewValue(field) BigSwitch: switch f := value.(type) { @@ -806,6 +727,22 @@ BigSwitch: return p.printField(value.Interface(), verb, plus, goSyntax, depth+1) } case reflect.ArrayOrSliceValue: + // Byte slices are special. + if f.Type().(reflect.ArrayOrSliceType).Elem().Kind() == reflect.Uint8 { + // We know it's a slice of bytes, but we also know it does not have static type + // []byte, or it would have been caught above. Therefore we cannot convert + // it directly in the (slightly) obvious way: f.Interface().([]byte); it doesn't have + // that type, and we can't write an expression of the right type and do a + // conversion because we don't have a static way to write the right type. + // So we build a slice by hand. This is a rare case but it would be nice + // if reflection could help a little more. + bytes := make([]byte, f.Len()) + for i := range bytes { + bytes[i] = byte(f.Elem(i).(*reflect.UintValue).Get()) + } + p.fmtBytes(bytes, verb, goSyntax, depth, field) + return verb == 's' + } if goSyntax { p.buf.WriteString(reflect.Typeof(field).String()) p.buf.WriteByte('{') @@ -862,30 +799,40 @@ BigSwitch: } p.fmt0x64(uint64(v)) case uintptrGetter: - if p.fmtUintptrGetter(field, value, verb, goSyntax) { - break - } - p.unknownType(f) + p.fmtPointer(field, value, verb, goSyntax) default: p.unknownType(f) } return false } +// intFromArg gets the fieldnumth element of a. On return, isInt reports whether the argument has type int. +func intFromArg(a []interface{}, end, i, fieldnum int) (num int, isInt bool, newi, newfieldnum int) { + newi, newfieldnum = end, fieldnum + if i < end && fieldnum < len(a) { + num, isInt = a[fieldnum].(int) + newi, newfieldnum = i+1, fieldnum+1 + } + return +} + func (p *pp) doPrintf(format string, a []interface{}) { - end := len(format) - 1 + end := len(format) fieldnum := 0 // we process one field per non-trivial format - for i := 0; i <= end; { - c, w := utf8.DecodeRuneInString(format[i:]) - if c != '%' || i == end { - if w == 1 { - p.buf.WriteByte(byte(c)) - } else { - p.buf.WriteString(format[i : i+w]) - } - i += w - continue + for i := 0; i < end; { + lasti := i + for i < end && format[i] != '%' { + i++ } + if i > lasti { + p.buf.WriteString(format[lasti:i]) + } + if i >= end { + // done processing format string + break + } + + // Process one verb i++ // flags and widths p.fmt.clearflags() @@ -906,17 +853,35 @@ func (p *pp) doPrintf(format string, a []interface{}) { break F } } - // do we have 20 (width)? - p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) - // do we have .20 (precision)? + // do we have width? + if i < end && format[i] == '*' { + p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum) + if !p.fmt.widPresent { + p.buf.Write(widthBytes) + } + } else { + p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) + } + // do we have precision? if i < end && format[i] == '.' { - p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end) + if format[i+1] == '*' { + p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum) + if !p.fmt.precPresent { + p.buf.Write(precBytes) + } + } else { + p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end) + } } - c, w = utf8.DecodeRuneInString(format[i:]) + if i >= end { + p.buf.Write(noVerbBytes) + continue + } + c, w := utf8.DecodeRuneInString(format[i:]) i += w // percent is special - absorbs no operand if c == '%' { - p.buf.WriteByte('%') // TODO: should we bother with width & prec? + p.buf.WriteByte('%') // We ignore width and prec. continue } if fieldnum >= len(a) { // out of operands @@ -928,33 +893,8 @@ func (p *pp) doPrintf(format string, a []interface{}) { field := a[fieldnum] fieldnum++ - // %T is special; we always do it here. - if c == 'T' { - // the value's type - if field == nil { - p.buf.Write(nilAngleBytes) - break - } - p.printField(reflect.Typeof(field).String(), 's', false, false, 0) - continue - } - - // Try Formatter (except for %T). - if field != nil { - if formatter, ok := field.(Formatter); ok { - formatter.Format(p, c) - continue - } - } - goSyntax := c == 'v' && p.fmt.sharp - if goSyntax { - p.fmt.sharp = false - } plus := c == 'v' && p.fmt.plus - if plus { - p.fmt.plus = false - } p.printField(field, c, plus, goSyntax, 0) } @@ -976,18 +916,18 @@ func (p *pp) doPrintf(format string, a []interface{}) { } func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) { - prev_string := false + prevString := false for fieldnum := 0; fieldnum < len(a); fieldnum++ { p.fmt.clearflags() // always add spaces if we're doing println field := a[fieldnum] if fieldnum > 0 { - _, is_string := field.(*reflect.StringValue) - if addspace || !is_string && !prev_string { + isString := field != nil && reflect.Typeof(field).Kind() == reflect.String + if addspace || !isString && !prevString { p.buf.WriteByte(' ') } } - prev_string = p.printField(field, 'v', false, false, 0) + prevString = p.printField(field, 'v', false, false, 0) } if addnewline { p.buf.WriteByte('\n') diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go index ded1f7719..dcc42bc92 100644 --- a/src/pkg/fmt/scan.go +++ b/src/pkg/fmt/scan.go @@ -22,6 +22,14 @@ type readRuner interface { ReadRune() (rune int, size int, err os.Error) } +// unreadRuner is the interface to something that can unread runes. +// If the object provided to Scan does not satisfy this interface, +// a local buffer will be used to back up the input, but its contents +// will be lost when Scan returns. +type unreadRuner interface { + UnreadRune() os.Error +} + // ScanState represents the scanner state passed to custom scanners. // Scanners may do rune-at-a-time scanning or ask the ScanState // to discover the next space-delimited token. @@ -29,7 +37,7 @@ type ScanState interface { // GetRune reads the next rune (Unicode code point) from the input. GetRune() (rune int, err os.Error) // UngetRune causes the next call to GetRune to return the rune. - UngetRune(rune int) + UngetRune() // Width returns the value of the width option and whether it has been set. // The unit is Unicode code points. Width() (wid int, ok bool) @@ -52,20 +60,20 @@ type Scanner interface { // as space. It returns the number of items successfully scanned. // If that is less than the number of arguments, err will report why. func Scan(a ...interface{}) (n int, err os.Error) { - return Fscan(os.Stdin, a) + return Fscan(os.Stdin, a...) } // Scanln is similar to Scan, but stops scanning at a newline and // after the final item there must be a newline or EOF. func Scanln(a ...interface{}) (n int, err os.Error) { - return Fscanln(os.Stdin, a) + return Fscanln(os.Stdin, a...) } // Scanf scans text read from standard input, storing successive // space-separated values into successive arguments as determined by // the format. It returns the number of items successfully scanned. func Scanf(format string, a ...interface{}) (n int, err os.Error) { - return Fscanf(os.Stdin, format, a) + return Fscanf(os.Stdin, format, a...) } // Sscan scans the argument string, storing successive space-separated @@ -73,20 +81,20 @@ func Scanf(format string, a ...interface{}) (n int, err os.Error) { // returns the number of items successfully scanned. If that is less // than the number of arguments, err will report why. func Sscan(str string, a ...interface{}) (n int, err os.Error) { - return Fscan(strings.NewReader(str), a) + return Fscan(strings.NewReader(str), a...) } // Sscanln is similar to Sscan, but stops scanning at a newline and // after the final item there must be a newline or EOF. func Sscanln(str string, a ...interface{}) (n int, err os.Error) { - return Fscanln(strings.NewReader(str), a) + return Fscanln(strings.NewReader(str), a...) } // Sscanf scans the argument string, storing successive space-separated // values into successive arguments as determined by the format. It // returns the number of items successfully parsed. func Sscanf(str string, format string, a ...interface{}) (n int, err os.Error) { - return Fscanf(strings.NewReader(str), format, a) + return Fscanf(strings.NewReader(str), format, a...) } // Fscan scans text read from r, storing successive space-separated @@ -133,6 +141,7 @@ type ss struct { buf bytes.Buffer // token accumulator nlIsSpace bool // whether newline counts as white space peekRune int // one-rune lookahead + prevRune int // last rune returned by GetRune atEOF bool // already read EOF maxWid int // max width of field, in runes widPresent bool // width was specified @@ -142,10 +151,14 @@ type ss struct { func (s *ss) GetRune() (rune int, err os.Error) { if s.peekRune >= 0 { rune = s.peekRune + s.prevRune = rune s.peekRune = -1 return } rune, _, err = s.rr.ReadRune() + if err == nil { + s.prevRune = rune + } return } @@ -161,11 +174,14 @@ func (s *ss) getRune() (rune int) { } if s.peekRune >= 0 { rune = s.peekRune + s.prevRune = rune s.peekRune = -1 return } rune, _, err := s.rr.ReadRune() - if err != nil { + if err == nil { + s.prevRune = rune + } else if err != nil { if err == os.EOF { s.atEOF = true return EOF @@ -198,8 +214,12 @@ func (s *ss) mustGetRune() (rune int) { } -func (s *ss) UngetRune(rune int) { - s.peekRune = rune +func (s *ss) UngetRune() { + if u, ok := s.rr.(unreadRuner); ok { + u.UnreadRune() + } else { + s.peekRune = s.prevRune + } } func (s *ss) error(err os.Error) { @@ -316,14 +336,17 @@ func (s *ss) free() { _ = ssFree <- s } -// skipSpace skips spaces and maybe newlines -func (s *ss) skipSpace() { +// skipSpace skips spaces and maybe newlines. +func (s *ss) skipSpace(stopAtNewline bool) { for { rune := s.getRune() if rune == EOF { return } if rune == '\n' { + if stopAtNewline { + break + } if s.nlIsSpace { continue } @@ -331,7 +354,7 @@ func (s *ss) skipSpace() { return } if !unicode.IsSpace(rune) { - s.UngetRune(rune) + s.UngetRune() break } } @@ -341,7 +364,7 @@ func (s *ss) skipSpace() { // skips white space. For Scanln, it stops at newlines. For Scan, // newlines are treated as spaces. func (s *ss) token() string { - s.skipSpace() + s.skipSpace(false) // read until white space or newline for nrunes := 0; !s.widPresent || nrunes < s.maxWid; nrunes++ { rune := s.getRune() @@ -349,7 +372,7 @@ func (s *ss) token() string { break } if unicode.IsSpace(rune) { - s.UngetRune(rune) + s.UngetRune() break } s.buf.WriteRune(rune) @@ -365,9 +388,9 @@ func (s *ss) typeError(field interface{}, expected string) { var complexError = os.ErrorString("syntax error scanning complex number") var boolError = os.ErrorString("syntax error scanning boolean") -// accepts checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the -// buffer and returns true. Otherwise it return false. -func (s *ss) accept(ok string) bool { +// consume reads the next rune in the input and reports whether it is in the ok string. +// If accept is true, it puts the character into the input token. +func (s *ss) consume(ok string, accept bool) bool { if s.wid >= s.maxWid { return false } @@ -377,17 +400,25 @@ func (s *ss) accept(ok string) bool { } for i := 0; i < len(ok); i++ { if int(ok[i]) == rune { - s.buf.WriteRune(rune) - s.wid++ + if accept { + s.buf.WriteRune(rune) + s.wid++ + } return true } } - if rune != EOF { - s.UngetRune(rune) + if rune != EOF && accept { + s.UngetRune() } return false } +// accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the +// buffer and returns true. Otherwise it return false. +func (s *ss) accept(ok string) bool { + return s.consume(ok, true) +} + // okVerb verifies that the verb is present in the list, setting s.err appropriately if not. func (s *ss) okVerb(verb int, okVerbs, typ string) bool { for _, v := range okVerbs { @@ -437,7 +468,7 @@ const ( // getBase returns the numeric base represented by the verb and its digit string. func (s *ss) getBase(verb int) (base int, digits string) { - s.okVerb(verb, "bdoxXv", "integer") // sets s.err + s.okVerb(verb, "bdoUxXv", "integer") // sets s.err base = 10 digits = decimalDigits switch verb { @@ -447,7 +478,7 @@ func (s *ss) getBase(verb int) (base int, digits string) { case 'o': base = 8 digits = octalDigits - case 'x', 'X': + case 'x', 'X', 'U': base = 16 digits = hexadecimalDigits } @@ -482,8 +513,14 @@ func (s *ss) scanInt(verb int, bitSize int) int64 { return s.scanRune(bitSize) } base, digits := s.getBase(verb) - s.skipSpace() - s.accept(sign) // If there's a sign, it will be left in the token buffer. + s.skipSpace(false) + if verb == 'U' { + if !s.consume("U", false) || !s.consume("+", false) { + s.errorString("bad unicode format ") + } + } else { + s.accept(sign) // If there's a sign, it will be left in the token buffer. + } tok := s.scanNumber(digits) i, err := strconv.Btoi64(tok, base) if err != nil { @@ -504,7 +541,12 @@ func (s *ss) scanUint(verb int, bitSize int) uint64 { return uint64(s.scanRune(bitSize)) } base, digits := s.getBase(verb) - s.skipSpace() + s.skipSpace(false) + if verb == 'U' { + if !s.consume("U", false) || !s.consume("+", false) { + s.errorString("bad unicode format ") + } + } tok := s.scanNumber(digits) i, err := strconv.Btoui64(tok, base) if err != nil { @@ -523,8 +565,16 @@ func (s *ss) scanUint(verb int, bitSize int) uint64 { // we have at least some digits, but Atof will do that. func (s *ss) floatToken() string { s.buf.Reset() + // NaN? + if s.accept("nN") && s.accept("aA") && s.accept("nN") { + return s.buf.String() + } // leading sign? s.accept(sign) + // Inf? + if s.accept("iI") && s.accept("nN") && s.accept("fF") { + return s.buf.String() + } // digits? for s.accept(decimalDigits) { } @@ -586,7 +636,7 @@ func (s *ss) scanComplex(verb int, n int) complex128 { if !s.okVerb(verb, floatVerbs, "complex") { return 0 } - s.skipSpace() + s.skipSpace(false) sreal, simag := s.complexTokens() real := s.convertFloat(sreal, n/2) imag := s.convertFloat(simag, n/2) @@ -595,18 +645,24 @@ func (s *ss) scanComplex(verb int, n int) complex128 { // convertString returns the string represented by the next input characters. // The format of the input is determined by the verb. -func (s *ss) convertString(verb int) string { +func (s *ss) convertString(verb int) (str string) { if !s.okVerb(verb, "svqx", "string") { return "" } - s.skipSpace() + s.skipSpace(false) switch verb { case 'q': - return s.quotedString() + str = s.quotedString() case 'x': - return s.hexString() + str = s.hexString() + default: + str = s.token() // %s and %v just return the next word + } + // Empty strings other than with %q are not OK. + if len(str) == 0 && verb != 'q' && s.maxWid > 0 { + s.errorString("Scan: no data for string") } - return s.token() // %s and %v just return the next word + return } // quotedString returns the double- or back-quoted string represented by the next input characters. @@ -672,7 +728,7 @@ func (s *ss) hexByte() (b byte, ok bool) { return } if unicode.IsSpace(rune1) { - s.UngetRune(rune1) + s.UngetRune() return } rune2 := s.mustGetRune() @@ -731,7 +787,7 @@ func (s *ss) scanOne(verb int, field interface{}) { case *int32: *v = int32(s.scanInt(verb, 32)) case *int64: - *v = s.scanInt(verb, intBits) + *v = s.scanInt(verb, 64) case *uint: *v = uint(s.scanUint(verb, intBits)) case *uint8: @@ -748,17 +804,17 @@ func (s *ss) scanOne(verb int, field interface{}) { // scan in high precision and convert, in order to preserve the correct error condition. case *float: if s.okVerb(verb, floatVerbs, "float") { - s.skipSpace() + s.skipSpace(false) *v = float(s.convertFloat(s.floatToken(), int(floatBits))) } case *float32: if s.okVerb(verb, floatVerbs, "float32") { - s.skipSpace() + s.skipSpace(false) *v = float32(s.convertFloat(s.floatToken(), 32)) } case *float64: if s.okVerb(verb, floatVerbs, "float64") { - s.skipSpace() + s.skipSpace(false) *v = s.convertFloat(s.floatToken(), 64) } case *string: @@ -795,7 +851,7 @@ func (s *ss) scanOne(verb int, field interface{}) { v.Elem(i).(*reflect.UintValue).Set(uint64(str[i])) } case *reflect.FloatValue: - s.skipSpace() + s.skipSpace(false) v.Set(s.convertFloat(s.floatToken(), v.Type().Bits())) case *reflect.ComplexValue: v.Set(s.scanComplex(verb, v.Type().Bits())) @@ -878,12 +934,12 @@ func (s *ss) advance(format string) (i int) { // Space in format but not in input: error s.errorString("expected space in input to match format") } - s.skipSpace() + s.skipSpace(true) continue } inputc := s.mustGetRune() if fmtc != inputc { - s.UngetRune(inputc) + s.UngetRune() return -1 } i += w diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go index 1e0319836..fe5ee1d61 100644 --- a/src/pkg/fmt/scan_test.go +++ b/src/pkg/fmt/scan_test.go @@ -5,10 +5,13 @@ package fmt_test import ( + "bufio" . "fmt" "io" + "math" "os" "reflect" + "regexp" "strings" "testing" "utf8" @@ -78,6 +81,12 @@ var ( renamedComplex128Val renamedComplex128 ) +type FloatTest struct { + text string + in float64 + out float64 +} + // Xs accepts any non-empty run of the verb character type Xs string @@ -100,7 +109,7 @@ func (x *Xs) Scan(state ScanState, verb int) os.Error { if err != nil { return err } - if !testing.MustCompile("^" + string(verb) + "+$").MatchString(tok) { + if !regexp.MustCompile("^" + string(verb) + "+$").MatchString(tok) { return os.ErrorString("syntax error for xs") } *x = Xs(tok) @@ -125,154 +134,163 @@ func newReader(s string) *myStringReader { var scanTests = []ScanTest{ // Numbers - ScanTest{"T\n", &boolVal, true}, // boolean test vals toggle to be sure they are written - ScanTest{"F\n", &boolVal, false}, // restored to zero value - ScanTest{"21\n", &intVal, 21}, - ScanTest{"22\n", &int8Val, int8(22)}, - ScanTest{"23\n", &int16Val, int16(23)}, - ScanTest{"24\n", &int32Val, int32(24)}, - ScanTest{"25\n", &int64Val, int64(25)}, - ScanTest{"127\n", &int8Val, int8(127)}, - ScanTest{"-21\n", &intVal, -21}, - ScanTest{"-22\n", &int8Val, int8(-22)}, - ScanTest{"-23\n", &int16Val, int16(-23)}, - ScanTest{"-24\n", &int32Val, int32(-24)}, - ScanTest{"-25\n", &int64Val, int64(-25)}, - ScanTest{"-128\n", &int8Val, int8(-128)}, - ScanTest{"+21\n", &intVal, +21}, - ScanTest{"+22\n", &int8Val, int8(+22)}, - ScanTest{"+23\n", &int16Val, int16(+23)}, - ScanTest{"+24\n", &int32Val, int32(+24)}, - ScanTest{"+25\n", &int64Val, int64(+25)}, - ScanTest{"+127\n", &int8Val, int8(+127)}, - ScanTest{"26\n", &uintVal, uint(26)}, - ScanTest{"27\n", &uint8Val, uint8(27)}, - ScanTest{"28\n", &uint16Val, uint16(28)}, - ScanTest{"29\n", &uint32Val, uint32(29)}, - ScanTest{"30\n", &uint64Val, uint64(30)}, - ScanTest{"255\n", &uint8Val, uint8(255)}, - ScanTest{"32767\n", &int16Val, int16(32767)}, - ScanTest{"2.3\n", &floatVal, 2.3}, - ScanTest{"2.3e1\n", &float32Val, float32(2.3e1)}, - ScanTest{"2.3e2\n", &float64Val, float64(2.3e2)}, - ScanTest{"2.35\n", &stringVal, "2.35"}, - ScanTest{"2345678\n", &bytesVal, []byte("2345678")}, - ScanTest{"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i}, - ScanTest{"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)}, - ScanTest{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)}, - ScanTest{"hello\n", &stringVal, "hello"}, + {"T\n", &boolVal, true}, // boolean test vals toggle to be sure they are written + {"F\n", &boolVal, false}, // restored to zero value + {"21\n", &intVal, 21}, + {"22\n", &int8Val, int8(22)}, + {"23\n", &int16Val, int16(23)}, + {"24\n", &int32Val, int32(24)}, + {"25\n", &int64Val, int64(25)}, + {"127\n", &int8Val, int8(127)}, + {"-21\n", &intVal, -21}, + {"-22\n", &int8Val, int8(-22)}, + {"-23\n", &int16Val, int16(-23)}, + {"-24\n", &int32Val, int32(-24)}, + {"-25\n", &int64Val, int64(-25)}, + {"-128\n", &int8Val, int8(-128)}, + {"+21\n", &intVal, +21}, + {"+22\n", &int8Val, int8(+22)}, + {"+23\n", &int16Val, int16(+23)}, + {"+24\n", &int32Val, int32(+24)}, + {"+25\n", &int64Val, int64(+25)}, + {"+127\n", &int8Val, int8(+127)}, + {"26\n", &uintVal, uint(26)}, + {"27\n", &uint8Val, uint8(27)}, + {"28\n", &uint16Val, uint16(28)}, + {"29\n", &uint32Val, uint32(29)}, + {"30\n", &uint64Val, uint64(30)}, + {"255\n", &uint8Val, uint8(255)}, + {"32767\n", &int16Val, int16(32767)}, + {"2.3\n", &floatVal, 2.3}, + {"2.3e1\n", &float32Val, float32(2.3e1)}, + {"2.3e2\n", &float64Val, float64(2.3e2)}, + {"2.35\n", &stringVal, "2.35"}, + {"2345678\n", &bytesVal, []byte("2345678")}, + {"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i}, + {"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)}, + {"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)}, + {"hello\n", &stringVal, "hello"}, // Renamed types - ScanTest{"true\n", &renamedBoolVal, renamedBool(true)}, - ScanTest{"F\n", &renamedBoolVal, renamedBool(false)}, - ScanTest{"101\n", &renamedIntVal, renamedInt(101)}, - ScanTest{"102\n", &renamedIntVal, renamedInt(102)}, - ScanTest{"103\n", &renamedUintVal, renamedUint(103)}, - ScanTest{"104\n", &renamedUintVal, renamedUint(104)}, - ScanTest{"105\n", &renamedInt8Val, renamedInt8(105)}, - ScanTest{"106\n", &renamedInt16Val, renamedInt16(106)}, - ScanTest{"107\n", &renamedInt32Val, renamedInt32(107)}, - ScanTest{"108\n", &renamedInt64Val, renamedInt64(108)}, - ScanTest{"109\n", &renamedUint8Val, renamedUint8(109)}, - ScanTest{"110\n", &renamedUint16Val, renamedUint16(110)}, - ScanTest{"111\n", &renamedUint32Val, renamedUint32(111)}, - ScanTest{"112\n", &renamedUint64Val, renamedUint64(112)}, - ScanTest{"113\n", &renamedUintptrVal, renamedUintptr(113)}, - ScanTest{"114\n", &renamedStringVal, renamedString("114")}, - ScanTest{"115\n", &renamedBytesVal, renamedBytes([]byte("115"))}, + {"true\n", &renamedBoolVal, renamedBool(true)}, + {"F\n", &renamedBoolVal, renamedBool(false)}, + {"101\n", &renamedIntVal, renamedInt(101)}, + {"102\n", &renamedIntVal, renamedInt(102)}, + {"103\n", &renamedUintVal, renamedUint(103)}, + {"104\n", &renamedUintVal, renamedUint(104)}, + {"105\n", &renamedInt8Val, renamedInt8(105)}, + {"106\n", &renamedInt16Val, renamedInt16(106)}, + {"107\n", &renamedInt32Val, renamedInt32(107)}, + {"108\n", &renamedInt64Val, renamedInt64(108)}, + {"109\n", &renamedUint8Val, renamedUint8(109)}, + {"110\n", &renamedUint16Val, renamedUint16(110)}, + {"111\n", &renamedUint32Val, renamedUint32(111)}, + {"112\n", &renamedUint64Val, renamedUint64(112)}, + {"113\n", &renamedUintptrVal, renamedUintptr(113)}, + {"114\n", &renamedStringVal, renamedString("114")}, + {"115\n", &renamedBytesVal, renamedBytes([]byte("115"))}, // Custom scanner. - ScanTest{" vvv ", &xVal, Xs("vvv")}, + {" vvv ", &xVal, Xs("vvv")}, + + // Fixed bugs + {"2147483648\n", &int64Val, int64(2147483648)}, // was: integer overflow } var scanfTests = []ScanfTest{ - ScanfTest{"%v", "TRUE\n", &boolVal, true}, - ScanfTest{"%t", "false\n", &boolVal, false}, - ScanfTest{"%v", "-71\n", &intVal, -71}, - ScanfTest{"%d", "72\n", &intVal, 72}, - ScanfTest{"%c", "a\n", &intVal, 'a'}, - ScanfTest{"%c", "\u5072\n", &intVal, 0x5072}, - ScanfTest{"%c", "\u1234\n", &intVal, '\u1234'}, - ScanfTest{"%d", "73\n", &int8Val, int8(73)}, - ScanfTest{"%d", "+74\n", &int16Val, int16(74)}, - ScanfTest{"%d", "75\n", &int32Val, int32(75)}, - ScanfTest{"%d", "76\n", &int64Val, int64(76)}, - ScanfTest{"%b", "1001001\n", &intVal, 73}, - ScanfTest{"%o", "075\n", &intVal, 075}, - ScanfTest{"%x", "a75\n", &intVal, 0xa75}, - ScanfTest{"%v", "71\n", &uintVal, uint(71)}, - ScanfTest{"%d", "72\n", &uintVal, uint(72)}, - ScanfTest{"%d", "73\n", &uint8Val, uint8(73)}, - ScanfTest{"%d", "74\n", &uint16Val, uint16(74)}, - ScanfTest{"%d", "75\n", &uint32Val, uint32(75)}, - ScanfTest{"%d", "76\n", &uint64Val, uint64(76)}, - ScanfTest{"%b", "1001001\n", &uintVal, uint(73)}, - ScanfTest{"%o", "075\n", &uintVal, uint(075)}, - ScanfTest{"%x", "a75\n", &uintVal, uint(0xa75)}, - ScanfTest{"%x", "A75\n", &uintVal, uint(0xa75)}, + {"%v", "TRUE\n", &boolVal, true}, + {"%t", "false\n", &boolVal, false}, + {"%v", "-71\n", &intVal, -71}, + {"%d", "72\n", &intVal, 72}, + {"%c", "a\n", &intVal, 'a'}, + {"%c", "\u5072\n", &intVal, 0x5072}, + {"%c", "\u1234\n", &intVal, '\u1234'}, + {"%d", "73\n", &int8Val, int8(73)}, + {"%d", "+74\n", &int16Val, int16(74)}, + {"%d", "75\n", &int32Val, int32(75)}, + {"%d", "76\n", &int64Val, int64(76)}, + {"%b", "1001001\n", &intVal, 73}, + {"%o", "075\n", &intVal, 075}, + {"%x", "a75\n", &intVal, 0xa75}, + {"%v", "71\n", &uintVal, uint(71)}, + {"%d", "72\n", &uintVal, uint(72)}, + {"%d", "73\n", &uint8Val, uint8(73)}, + {"%d", "74\n", &uint16Val, uint16(74)}, + {"%d", "75\n", &uint32Val, uint32(75)}, + {"%d", "76\n", &uint64Val, uint64(76)}, + {"%b", "1001001\n", &uintVal, uint(73)}, + {"%o", "075\n", &uintVal, uint(075)}, + {"%x", "a75\n", &uintVal, uint(0xa75)}, + {"%x", "A75\n", &uintVal, uint(0xa75)}, + {"%U", "U+1234\n", &intVal, int(0x1234)}, + {"%U", "U+4567\n", &uintVal, uint(0x4567)}, // Strings - ScanfTest{"%s", "using-%s\n", &stringVal, "using-%s"}, - ScanfTest{"%x", "7573696e672d2578\n", &stringVal, "using-%x"}, - ScanfTest{"%q", `"quoted\twith\\do\u0075bl\x65s"` + "\n", &stringVal, "quoted\twith\\doubles"}, - ScanfTest{"%q", "`quoted with backs`\n", &stringVal, "quoted with backs"}, + {"%s", "using-%s\n", &stringVal, "using-%s"}, + {"%x", "7573696e672d2578\n", &stringVal, "using-%x"}, + {"%q", `"quoted\twith\\do\u0075bl\x65s"` + "\n", &stringVal, "quoted\twith\\doubles"}, + {"%q", "`quoted with backs`\n", &stringVal, "quoted with backs"}, // Byte slices - ScanfTest{"%s", "bytes-%s\n", &bytesVal, []byte("bytes-%s")}, - ScanfTest{"%x", "62797465732d2578\n", &bytesVal, []byte("bytes-%x")}, - ScanfTest{"%q", `"bytes\rwith\vdo\u0075bl\x65s"` + "\n", &bytesVal, []byte("bytes\rwith\vdoubles")}, - ScanfTest{"%q", "`bytes with backs`\n", &bytesVal, []byte("bytes with backs")}, + {"%s", "bytes-%s\n", &bytesVal, []byte("bytes-%s")}, + {"%x", "62797465732d2578\n", &bytesVal, []byte("bytes-%x")}, + {"%q", `"bytes\rwith\vdo\u0075bl\x65s"` + "\n", &bytesVal, []byte("bytes\rwith\vdoubles")}, + {"%q", "`bytes with backs`\n", &bytesVal, []byte("bytes with backs")}, // Renamed types - ScanfTest{"%v\n", "true\n", &renamedBoolVal, renamedBool(true)}, - ScanfTest{"%t\n", "F\n", &renamedBoolVal, renamedBool(false)}, - ScanfTest{"%v", "101\n", &renamedIntVal, renamedInt(101)}, - ScanfTest{"%c", "\u0101\n", &renamedIntVal, renamedInt('\u0101')}, - ScanfTest{"%o", "0146\n", &renamedIntVal, renamedInt(102)}, - ScanfTest{"%v", "103\n", &renamedUintVal, renamedUint(103)}, - ScanfTest{"%d", "104\n", &renamedUintVal, renamedUint(104)}, - ScanfTest{"%d", "105\n", &renamedInt8Val, renamedInt8(105)}, - ScanfTest{"%d", "106\n", &renamedInt16Val, renamedInt16(106)}, - ScanfTest{"%d", "107\n", &renamedInt32Val, renamedInt32(107)}, - ScanfTest{"%d", "108\n", &renamedInt64Val, renamedInt64(108)}, - ScanfTest{"%x", "6D\n", &renamedUint8Val, renamedUint8(109)}, - ScanfTest{"%o", "0156\n", &renamedUint16Val, renamedUint16(110)}, - ScanfTest{"%d", "111\n", &renamedUint32Val, renamedUint32(111)}, - ScanfTest{"%d", "112\n", &renamedUint64Val, renamedUint64(112)}, - ScanfTest{"%d", "113\n", &renamedUintptrVal, renamedUintptr(113)}, - ScanfTest{"%s", "114\n", &renamedStringVal, renamedString("114")}, - ScanfTest{"%q", "\"1155\"\n", &renamedBytesVal, renamedBytes([]byte("1155"))}, - ScanfTest{"%g", "115.1\n", &renamedFloatVal, renamedFloat(115.1)}, - ScanfTest{"%g", "116e1\n", &renamedFloat32Val, renamedFloat32(116e1)}, - ScanfTest{"%g", "-11.7e+1", &renamedFloat64Val, renamedFloat64(-11.7e+1)}, - ScanfTest{"%g", "11+5.1i\n", &renamedComplexVal, renamedComplex(11 + 5.1i)}, - ScanfTest{"%g", "11+6e1i\n", &renamedComplex64Val, renamedComplex64(11 + 6e1i)}, - ScanfTest{"%g", "-11.+7e+1i", &renamedComplex128Val, renamedComplex128(-11. + 7e+1i)}, + {"%v\n", "true\n", &renamedBoolVal, renamedBool(true)}, + {"%t\n", "F\n", &renamedBoolVal, renamedBool(false)}, + {"%v", "101\n", &renamedIntVal, renamedInt(101)}, + {"%c", "\u0101\n", &renamedIntVal, renamedInt('\u0101')}, + {"%o", "0146\n", &renamedIntVal, renamedInt(102)}, + {"%v", "103\n", &renamedUintVal, renamedUint(103)}, + {"%d", "104\n", &renamedUintVal, renamedUint(104)}, + {"%d", "105\n", &renamedInt8Val, renamedInt8(105)}, + {"%d", "106\n", &renamedInt16Val, renamedInt16(106)}, + {"%d", "107\n", &renamedInt32Val, renamedInt32(107)}, + {"%d", "108\n", &renamedInt64Val, renamedInt64(108)}, + {"%x", "6D\n", &renamedUint8Val, renamedUint8(109)}, + {"%o", "0156\n", &renamedUint16Val, renamedUint16(110)}, + {"%d", "111\n", &renamedUint32Val, renamedUint32(111)}, + {"%d", "112\n", &renamedUint64Val, renamedUint64(112)}, + {"%d", "113\n", &renamedUintptrVal, renamedUintptr(113)}, + {"%s", "114\n", &renamedStringVal, renamedString("114")}, + {"%q", "\"1155\"\n", &renamedBytesVal, renamedBytes([]byte("1155"))}, + {"%g", "115.1\n", &renamedFloatVal, renamedFloat(115.1)}, + {"%g", "116e1\n", &renamedFloat32Val, renamedFloat32(116e1)}, + {"%g", "-11.7e+1", &renamedFloat64Val, renamedFloat64(-11.7e+1)}, + {"%g", "11+5.1i\n", &renamedComplexVal, renamedComplex(11 + 5.1i)}, + {"%g", "11+6e1i\n", &renamedComplex64Val, renamedComplex64(11 + 6e1i)}, + {"%g", "-11.+7e+1i", &renamedComplex128Val, renamedComplex128(-11. + 7e+1i)}, // Interesting formats - ScanfTest{"here is\tthe value:%d", "here is the\tvalue:118\n", &intVal, 118}, - ScanfTest{"%% %%:%d", "% %:119\n", &intVal, 119}, + {"here is\tthe value:%d", "here is the\tvalue:118\n", &intVal, 118}, + {"%% %%:%d", "% %:119\n", &intVal, 119}, // Corner cases - ScanfTest{"%x", "FFFFFFFF\n", &uint32Val, uint32(0xFFFFFFFF)}, + {"%x", "FFFFFFFF\n", &uint32Val, uint32(0xFFFFFFFF)}, // Custom scanner. - ScanfTest{"%s", " sss ", &xVal, Xs("sss")}, - ScanfTest{"%2s", "sssss", &xVal, Xs("ss")}, + {"%s", " sss ", &xVal, Xs("sss")}, + {"%2s", "sssss", &xVal, Xs("ss")}, + + // Fixed bugs + {"%d\n", "27\n", &intVal, 27}, // ok + {"%d\n", "28 \n", &intVal, 28}, // was: "unexpected newline" } var overflowTests = []ScanTest{ - ScanTest{"128", &int8Val, 0}, - ScanTest{"32768", &int16Val, 0}, - ScanTest{"-129", &int8Val, 0}, - ScanTest{"-32769", &int16Val, 0}, - ScanTest{"256", &uint8Val, 0}, - ScanTest{"65536", &uint16Val, 0}, - ScanTest{"1e100", &float32Val, 0}, - ScanTest{"1e500", &float64Val, 0}, - ScanTest{"(1e100+0i)", &complexVal, 0}, - ScanTest{"(1+1e100i)", &complex64Val, 0}, - ScanTest{"(1-1e500i)", &complex128Val, 0}, + {"128", &int8Val, 0}, + {"32768", &int16Val, 0}, + {"-129", &int8Val, 0}, + {"-32769", &int16Val, 0}, + {"256", &uint8Val, 0}, + {"65536", &uint16Val, 0}, + {"1e100", &float32Val, 0}, + {"1e500", &float64Val, 0}, + {"(1e100+0i)", &complexVal, 0}, + {"(1+1e100i)", &complex64Val, 0}, + {"(1-1e500i)", &complex128Val, 0}, } var i, j, k int @@ -281,32 +299,30 @@ var s, t string var c complex var x, y Xs -func args(a ...interface{}) []interface{} { return a } - var multiTests = []ScanfMultiTest{ - ScanfMultiTest{"", "", nil, nil, ""}, - ScanfMultiTest{"%d", "23", args(&i), args(23), ""}, - ScanfMultiTest{"%2s%3s", "22333", args(&s, &t), args("22", "333"), ""}, - ScanfMultiTest{"%2d%3d", "44555", args(&i, &j), args(44, 555), ""}, - ScanfMultiTest{"%2d.%3d", "66.777", args(&i, &j), args(66, 777), ""}, - ScanfMultiTest{"%d, %d", "23, 18", args(&i, &j), args(23, 18), ""}, - ScanfMultiTest{"%3d22%3d", "33322333", args(&i, &j), args(333, 333), ""}, - ScanfMultiTest{"%6vX=%3fY", "3+2iX=2.5Y", args(&c, &f), args((3 + 2i), float(2.5)), ""}, - ScanfMultiTest{"%d%s", "123abc", args(&i, &s), args(123, "abc"), ""}, - ScanfMultiTest{"%c%c%c", "2\u50c2X", args(&i, &j, &k), args('2', '\u50c2', 'X'), ""}, + {"", "", nil, nil, ""}, + {"%d", "23", args(&i), args(23), ""}, + {"%2s%3s", "22333", args(&s, &t), args("22", "333"), ""}, + {"%2d%3d", "44555", args(&i, &j), args(44, 555), ""}, + {"%2d.%3d", "66.777", args(&i, &j), args(66, 777), ""}, + {"%d, %d", "23, 18", args(&i, &j), args(23, 18), ""}, + {"%3d22%3d", "33322333", args(&i, &j), args(333, 333), ""}, + {"%6vX=%3fY", "3+2iX=2.5Y", args(&c, &f), args((3 + 2i), float(2.5)), ""}, + {"%d%s", "123abc", args(&i, &s), args(123, "abc"), ""}, + {"%c%c%c", "2\u50c2X", args(&i, &j, &k), args('2', '\u50c2', 'X'), ""}, // Custom scanner. - ScanfMultiTest{"%2e%f", "eefffff", args(&x, &y), args(Xs("ee"), Xs("fffff")), ""}, + {"%2e%f", "eefffff", args(&x, &y), args(Xs("ee"), Xs("fffff")), ""}, // Errors - ScanfMultiTest{"%t", "23 18", args(&i), nil, "bad verb"}, - ScanfMultiTest{"%d %d %d", "23 18", args(&i, &j), args(23, 18), "too few operands"}, - ScanfMultiTest{"%d %d", "23 18 27", args(&i, &j, &k), args(23, 18), "too many operands"}, - ScanfMultiTest{"%c", "\u0100", args(&int8Val), nil, "overflow"}, - ScanfMultiTest{"X%d", "10X", args(&intVal), nil, "input does not match format"}, + {"%t", "23 18", args(&i), nil, "bad verb"}, + {"%d %d %d", "23 18", args(&i, &j), args(23, 18), "too few operands"}, + {"%d %d", "23 18 27", args(&i, &j, &k), args(23, 18), "too many operands"}, + {"%c", "\u0100", args(&int8Val), nil, "overflow"}, + {"X%d", "10X", args(&intVal), nil, "input does not match format"}, // Bad UTF-8: should see every byte. - ScanfMultiTest{"%c%c%c", "\xc2X\xc2", args(&i, &j, &k), args(utf8.RuneError, 'X', utf8.RuneError), ""}, + {"%c%c%c", "\xc2X\xc2", args(&i, &j, &k), args(utf8.RuneError, 'X', utf8.RuneError), ""}, } func testScan(name string, t *testing.T, scan func(r io.Reader, a ...interface{}) (int, os.Error)) { @@ -379,7 +395,7 @@ func TestScanf(t *testing.T) { func TestScanOverflow(t *testing.T) { // different machines and different types report errors with different strings. - re := testing.MustCompile("overflow|too large|out of range|not representable") + re := regexp.MustCompile("overflow|too large|out of range|not representable") for _, test := range overflowTests { _, err := Sscan(test.text, test.in) if err == nil { @@ -392,6 +408,57 @@ func TestScanOverflow(t *testing.T) { } } +func verifyNaN(str string, t *testing.T) { + var f float + var f32 float32 + var f64 float64 + text := str + " " + str + " " + str + n, err := Fscan(strings.NewReader(text), &f, &f32, &f64) + if err != nil { + t.Errorf("got error scanning %q: %s", text, err) + } + if n != 3 { + t.Errorf("count error scanning %q: got %d", text, n) + } + if !math.IsNaN(float64(f)) || !math.IsNaN(float64(f32)) || !math.IsNaN(f64) { + t.Errorf("didn't get NaNs scanning %q: got %g %g %g", text, f, f32, f64) + } +} + +func TestNaN(t *testing.T) { + for _, s := range []string{"nan", "NAN", "NaN"} { + verifyNaN(s, t) + } +} + +func verifyInf(str string, t *testing.T) { + var f float + var f32 float32 + var f64 float64 + text := str + " " + str + " " + str + n, err := Fscan(strings.NewReader(text), &f, &f32, &f64) + if err != nil { + t.Errorf("got error scanning %q: %s", text, err) + } + if n != 3 { + t.Errorf("count error scanning %q: got %d", text, n) + } + sign := 1 + if str[0] == '-' { + sign = -1 + } + if !math.IsInf(float64(f), sign) || !math.IsInf(float64(f32), sign) || !math.IsInf(f64, sign) { + t.Errorf("didn't get right Infs scanning %q: got %g %g %g", text, f, f32, f64) + } +} + + +func TestInf(t *testing.T) { + for _, s := range []string{"inf", "+inf", "-inf", "INF", "-INF", "+INF", "Inf", "-Inf", "+Inf"} { + verifyInf(s, t) + } +} + // TODO: there's no conversion from []T to ...T, but we can fake it. These // functions do the faking. We index the table by the length of the param list. var fscanf = []func(io.Reader, string, []interface{}) (int, os.Error){ @@ -460,6 +527,46 @@ func TestScanMultiple(t *testing.T) { if a != 123 || s != "abc" { t.Errorf("Sscan wrong values: got (%d %q) expected (123 \"abc\")", a, s) } + n, err = Sscan("asdf", &s, &a) + if n != 1 { + t.Errorf("Sscan count error: expected 1: got %d", n) + } + if err == nil { + t.Errorf("Sscan expected error; got none: %s", err) + } + if s != "asdf" { + t.Errorf("Sscan wrong values: got %q expected \"asdf\"", s) + } +} + +// Empty strings are not valid input when scanning a string. +func TestScanEmpty(t *testing.T) { + var s1, s2 string + n, err := Sscan("abc", &s1, &s2) + if n != 1 { + t.Errorf("Sscan count error: expected 1: got %d", n) + } + if err == nil { + t.Error("Sscan <one item> expected error; got none") + } + if s1 != "abc" { + t.Errorf("Sscan wrong values: got %q expected \"abc\"", s1) + } + n, err = Sscan("", &s1, &s2) + if n != 0 { + t.Errorf("Sscan count error: expected 0: got %d", n) + } + if err == nil { + t.Error("Sscan <empty> expected error; got none") + } + // Quoted empty string is OK. + n, err = Sscanf(`""`, "%q", &s1) + if n != 1 { + t.Errorf("Sscanf count error: expected 1: got %d", n) + } + if err != nil { + t.Errorf("Sscanf <empty> expected no error with quoted string; got %s", err) + } } func TestScanNotPointer(t *testing.T) { @@ -535,3 +642,24 @@ func TestEOF(t *testing.T) { t.Error("expected one EOF, got", ec.eofCount) } } + +// Verify that, at least when using bufio, successive calls to Fscan do not lose runes. +func TestUnreadRuneWithBufio(t *testing.T) { + r := bufio.NewReader(strings.NewReader("123αb")) + var i int + var a string + n, err := Fscanf(r, "%d", &i) + if n != 1 || err != nil { + t.Errorf("reading int expected one item, no errors; got %d %q", n, err) + } + if i != 123 { + t.Errorf("expected 123; got %d", i) + } + n, err = Fscanf(r, "%s", &a) + if n != 1 || err != nil { + t.Errorf("reading string expected one item, no errors; got %d %q", n, err) + } + if a != "αb" { + t.Errorf("expected αb; got %q", a) + } +} |