summaryrefslogtreecommitdiff
path: root/src/pkg/fmt
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/fmt')
-rw-r--r--src/pkg/fmt/Makefile3
-rw-r--r--src/pkg/fmt/doc.go163
-rw-r--r--src/pkg/fmt/fmt_test.go612
-rw-r--r--src/pkg/fmt/format.go11
-rw-r--r--src/pkg/fmt/print.go384
-rw-r--r--src/pkg/fmt/scan.go138
-rw-r--r--src/pkg/fmt/scan_test.go422
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)
+ }
+}