diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-06-30 15:34:22 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-06-30 15:34:22 +0200 |
commit | d39f5aa373a4422f7a5f3ee764fb0f6b0b719d61 (patch) | |
tree | 1833f8b72a4b3a8f00d0d143b079a8fcad01c6ae /src/pkg/fmt | |
parent | 8652e6c371b8905498d3d314491d36c58d5f68d5 (diff) | |
download | golang-upstream/58.tar.gz |
Imported Upstream version 58upstream/58
Diffstat (limited to 'src/pkg/fmt')
-rw-r--r-- | src/pkg/fmt/doc.go | 11 | ||||
-rw-r--r-- | src/pkg/fmt/fmt_test.go | 21 | ||||
-rw-r--r-- | src/pkg/fmt/format.go | 7 | ||||
-rw-r--r-- | src/pkg/fmt/print.go | 15 | ||||
-rw-r--r-- | src/pkg/fmt/scan.go | 41 | ||||
-rw-r--r-- | src/pkg/fmt/scan_test.go | 62 |
6 files changed, 135 insertions, 22 deletions
diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go index e4d4f1844..79fe5758c 100644 --- a/src/pkg/fmt/doc.go +++ b/src/pkg/fmt/doc.go @@ -25,9 +25,10 @@ %c the character represented by the corresponding Unicode code point %d base 10 %o base 8 + %q a single-quoted character literal safely escaped with Go syntax. %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+%0.4X" + %U Unicode format: U+1234; same as "U+%04X" Floating-point and complex constituents: %b decimalless scientific notation with exponent a power of two, in the manner of strconv.Ftoa32, e.g. -123456p-78 @@ -134,10 +135,10 @@ 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 + %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 The familiar base-setting prefixes 0 (octal) and 0x (hexadecimal) are accepted when scanning integers without a diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index b3c0c5abe..122b9516b 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -132,12 +132,23 @@ var fmttests = []struct { {"%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", "\u263a", `"☺"`}, {"%q", "\U0010ffff", `"\U0010ffff"`}, + // escaped characters + {"%q", 'x', `'x'`}, + {"%q", 0, `'\x00'`}, + {"%q", '\n', `'\n'`}, + {"%q", '\u0e00', `'\u0e00'`}, // not a printable rune. + {"%q", '\U000c2345', `'\U000c2345'`}, // not a printable rune. + {"%q", int64(0x7FFFFFFF), `%!q(int64=2147483647)`}, + {"%q", uint64(0xFFFFFFFF), `%!q(uint64=4294967295)`}, + {"%q", '"', `'"'`}, + {"%q", '\'', `'\''`}, + // width {"%5s", "abc", " abc"}, - {"%2s", "\u263a", " \u263a"}, + {"%2s", "\u263a", " ☺"}, {"%-5s", "abc", "abc "}, {"%-8q", "abc", `"abc" `}, {"%05s", "abc", "00abc"}, @@ -147,9 +158,9 @@ var fmttests = []struct { {"%.5s", "日本語日本語", "日本語日本"}, {"%.5s", []byte("日本語日本語"), "日本語日本"}, {"%.5q", "abcdefghijklmnopqrstuvwxyz", `"abcde"`}, - {"%.3q", "日本語日本語", `"\u65e5\u672c\u8a9e"`}, - {"%.3q", []byte("日本語日本語"), `"\u65e5\u672c\u8a9e"`}, - {"%10.1q", "日本語日本語", ` "\u65e5"`}, + {"%.3q", "日本語日本語", `"日本語"`}, + {"%.3q", []byte("日本語日本語"), `"日本語"`}, + {"%10.1q", "日本語日本語", ` "日"`}, // integers {"%d", 12345, "12345"}, diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go index f9d2b4fca..5dcfb9677 100644 --- a/src/pkg/fmt/format.go +++ b/src/pkg/fmt/format.go @@ -296,6 +296,13 @@ func (f *fmt) fmt_q(s string) { f.padString(quoted) } +// fmt_qc formats the integer as a single-quoted, escaped Go character constant. +// If the character is not valid Unicode, it will print '\ufffd'. +func (f *fmt) fmt_qc(c int64) { + quoted := strconv.QuoteRune(int(c)) + f.padString(quoted) +} + // floating-point func doPrec(f *fmt, def int) int { diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index 10e0fe7c8..c18a8ea38 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -9,6 +9,7 @@ import ( "io" "os" "reflect" + "unicode" "utf8" ) @@ -41,7 +42,7 @@ type State interface { Precision() (prec int, ok bool) // Flag returns whether the flag c, a character, has been set. - Flag(int) bool + Flag(c int) bool } // Formatter is the interface implemented by values with a custom formatter. @@ -332,6 +333,12 @@ func (p *pp) fmtInt64(v int64, verb int, value interface{}) { p.fmt.integer(v, 10, signed, ldigits) case 'o': p.fmt.integer(v, 8, signed, ldigits) + case 'q': + if 0 <= v && v <= unicode.MaxRune { + p.fmt.fmt_qc(v) + } else { + p.badVerb(verb, value) + } case 'x': p.fmt.integer(v, 16, signed, ldigits) case 'U': @@ -385,6 +392,12 @@ func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) { } case 'o': p.fmt.integer(int64(v), 8, unsigned, ldigits) + case 'q': + if 0 <= v && v <= unicode.MaxRune { + p.fmt.fmt_qc(int64(v)) + } else { + p.badVerb(verb, value) + } case 'x': p.fmt.integer(int64(v), 16, unsigned, ldigits) case 'X': diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go index 42bc52c92..dd8548ceb 100644 --- a/src/pkg/fmt/scan.go +++ b/src/pkg/fmt/scan.go @@ -231,6 +231,7 @@ func (s *ss) UnreadRune() os.Error { } else { s.peekRune = s.prevRune } + s.prevRune = -1 s.count-- return nil } @@ -457,6 +458,14 @@ func (s *ss) peek(ok string) bool { return strings.IndexRune(ok, rune) >= 0 } +func (s *ss) notEOF() { + // Guarantee there is data to be read. + if rune := s.getRune(); rune == eof { + panic(os.EOF) + } + s.UnreadRune() +} + // 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 { @@ -476,11 +485,13 @@ func (s *ss) okVerb(verb int, okVerbs, typ string) bool { // scanBool returns the value of the boolean represented by the next token. func (s *ss) scanBool(verb int) bool { + s.skipSpace(false) + s.notEOF() if !s.okVerb(verb, "tv", "boolean") { return false } // Syntax-checking a boolean is annoying. We're not fastidious about case. - switch s.mustReadRune() { + switch s.getRune() { case '0': return false case '1': @@ -531,6 +542,7 @@ func (s *ss) getBase(verb int) (base int, digits string) { // scanNumber returns the numerical string with specified digits starting here. func (s *ss) scanNumber(digits string, haveDigits bool) string { + s.notEOF() if !haveDigits && !s.accept(digits) { s.errorString("expected integer") } @@ -541,7 +553,8 @@ func (s *ss) scanNumber(digits string, haveDigits bool) string { // scanRune returns the next rune value in the input. func (s *ss) scanRune(bitSize int) int64 { - rune := int64(s.mustReadRune()) + s.notEOF() + rune := int64(s.getRune()) n := uint(bitSize) x := (rune << (64 - n)) >> (64 - n) if x != rune { @@ -575,6 +588,7 @@ func (s *ss) scanInt(verb int, bitSize int) int64 { return s.scanRune(bitSize) } s.skipSpace(false) + s.notEOF() base, digits := s.getBase(verb) haveDigits := false if verb == 'U' { @@ -607,6 +621,7 @@ func (s *ss) scanUint(verb int, bitSize int) uint64 { return uint64(s.scanRune(bitSize)) } s.skipSpace(false) + s.notEOF() base, digits := s.getBase(verb) haveDigits := false if verb == 'U' { @@ -727,6 +742,7 @@ func (s *ss) scanComplex(verb int, n int) complex128 { return 0 } s.skipSpace(false) + s.notEOF() sreal, simag := s.complexTokens() real := s.convertFloat(sreal, n/2) imag := s.convertFloat(simag, n/2) @@ -740,6 +756,7 @@ func (s *ss) convertString(verb int) (str string) { return "" } s.skipSpace(false) + s.notEOF() switch verb { case 'q': str = s.quotedString() @@ -748,16 +765,13 @@ func (s *ss) convertString(verb int) (str string) { default: str = string(s.token(true, notSpace)) // %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 } // quotedString returns the double- or back-quoted string represented by the next input characters. func (s *ss) quotedString() string { - quote := s.mustReadRune() + s.notEOF() + quote := s.getRune() switch quote { case '`': // Back-quoted: Anything goes until EOF or back quote. @@ -827,6 +841,7 @@ func (s *ss) hexByte() (b byte, ok bool) { // hexString returns the space-delimited hexpair-encoded string. func (s *ss) hexString() string { + s.notEOF() for { b, ok := s.hexByte() if !ok { @@ -860,6 +875,7 @@ func (s *ss) scanOne(verb int, field interface{}) { } return } + switch v := field.(type) { case *bool: *v = s.scanBool(verb) @@ -894,11 +910,13 @@ func (s *ss) scanOne(verb int, field interface{}) { case *float32: if s.okVerb(verb, floatVerbs, "float32") { s.skipSpace(false) + s.notEOF() *v = float32(s.convertFloat(s.floatToken(), 32)) } case *float64: if s.okVerb(verb, floatVerbs, "float64") { s.skipSpace(false) + s.notEOF() *v = s.convertFloat(s.floatToken(), 64) } case *string: @@ -936,6 +954,7 @@ func (s *ss) scanOne(verb int, field interface{}) { } case reflect.Float32, reflect.Float64: s.skipSpace(false) + s.notEOF() v.SetFloat(s.convertFloat(s.floatToken(), v.Type().Bits())) case reflect.Complex64, reflect.Complex128: v.SetComplex(s.scanComplex(verb, v.Type().Bits())) @@ -946,13 +965,13 @@ func (s *ss) scanOne(verb int, field interface{}) { } } -// errorHandler turns local panics into error returns. EOFs are benign. +// errorHandler turns local panics into error returns. func errorHandler(errp *os.Error) { if e := recover(); e != nil { if se, ok := e.(scanError); ok { // catch local error - if se.err != os.EOF { - *errp = se.err - } + *errp = se.err + } else if eof, ok := e.(os.Error); ok && eof == os.EOF { // out of input + *errp = eof } else { panic(e) } diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go index da13eb2d1..a4de8adb1 100644 --- a/src/pkg/fmt/scan_test.go +++ b/src/pkg/fmt/scan_test.go @@ -660,6 +660,68 @@ func TestEOF(t *testing.T) { } } +// Verify that we see an EOF error if we run out of input. +// This was a buglet: we used to get "expected integer". +func TestEOFAtEndOfInput(t *testing.T) { + var i, j int + n, err := Sscanf("23", "%d %d", &i, &j) + if n != 1 || i != 23 { + t.Errorf("Sscanf expected one value of 23; got %d %d", n, i) + } + if err != os.EOF { + t.Errorf("Sscanf expected EOF; got %q", err) + } + n, err = Sscan("234", &i, &j) + if n != 1 || i != 234 { + t.Errorf("Sscan expected one value of 234; got %d %d", n, i) + } + if err != os.EOF { + t.Errorf("Sscan expected EOF; got %q", err) + } + // Trailing space is tougher. + n, err = Sscan("234 ", &i, &j) + if n != 1 || i != 234 { + t.Errorf("Sscan expected one value of 234; got %d %d", n, i) + } + if err != os.EOF { + t.Errorf("Sscan expected EOF; got %q", err) + } +} + +var eofTests = []struct { + format string + v interface{} +}{ + {"%s", &stringVal}, + {"%q", &stringVal}, + {"%x", &stringVal}, + {"%v", &stringVal}, + {"%v", &bytesVal}, + {"%v", &intVal}, + {"%v", &uintVal}, + {"%v", &boolVal}, + {"%v", &float32Val}, + {"%v", &complex64Val}, + {"%v", &renamedStringVal}, + {"%v", &renamedBytesVal}, + {"%v", &renamedIntVal}, + {"%v", &renamedUintVal}, + {"%v", &renamedBoolVal}, + {"%v", &renamedFloat32Val}, + {"%v", &renamedComplex64Val}, +} + +func TestEOFAllTypes(t *testing.T) { + for i, test := range eofTests { + if _, err := Sscanf("", test.format, test.v); err != os.EOF { + t.Errorf("#%d: %s %T not eof on empty string: %s", i, test.format, test.v, err) + } + if _, err := Sscanf(" ", test.format, test.v); err != os.EOF { + t.Errorf("#%d: %s %T not eof on trailing blanks: %s", i, test.format, test.v, err) + } + } +} + // 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")) |