summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2010-06-01 15:21:21 -0700
committerRob Pike <r@golang.org>2010-06-01 15:21:21 -0700
commitfa0c7819085d9c58540429a5fbe78e4d015d660f (patch)
treea328dacad893c6e4ae38b0e91c9468f9a01d3c1d /src
parent5c8dc2166a65a9f362bb9ba4fde715224e87affb (diff)
downloadgolang-fa0c7819085d9c58540429a5fbe78e4d015d660f.tar.gz
fmt.Scan: renamings, strings, errors
- implement scanning for all renamed types (compiler bug stops complex from being renamable, so it can't be tested but the code is there) - %q %x for strings - error handling now done with panic/recover R=rsc CC=golang-dev http://codereview.appspot.com/1458041
Diffstat (limited to 'src')
-rw-r--r--src/pkg/fmt/scan.go478
-rw-r--r--src/pkg/fmt/scan_test.go123
2 files changed, 409 insertions, 192 deletions
diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go
index 9851d4d29..17a08226e 100644
--- a/src/pkg/fmt/scan.go
+++ b/src/pkg/fmt/scan.go
@@ -68,8 +68,7 @@ func Scanln(a ...interface{}) (n int, err os.Error) {
// is less than the number of arguments, err will report why.
func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) {
s := newScanState(r, true)
- n = s.doScan(a)
- err = s.err
+ n, err = s.doScan(a)
s.free()
return
}
@@ -82,8 +81,7 @@ func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) {
// number of arguments, err will report why.
func Fscanln(r io.Reader, a ...interface{}) (n int, err os.Error) {
s := newScanState(r, false)
- n = s.doScan(a)
- err = s.err
+ n, err = s.doScan(a)
s.free()
return
}
@@ -96,19 +94,23 @@ func XXXScanf(format string, a ...interface{}) (n int, err os.Error) {
// XXXFscanf is incomplete, do not use.
func XXXFscanf(r io.Reader, format string, a ...interface{}) (n int, err os.Error) {
s := newScanState(r, false)
- n = s.doScanf(format, a)
- err = s.err
+ n, err = s.doScanf(format, a)
s.free()
return
}
+// scanError represents an error generated by the scanning software.
+// It's used as a unique signature to identify such errors when recovering.
+type scanError struct {
+ err os.Error
+}
+
// ss is the internal implementation of ScanState.
type ss struct {
rr readRuner // where to read input
buf bytes.Buffer // token accumulator
nlIsSpace bool // whether newline counts as white space
peekRune int // one-rune lookahead
- err os.Error
}
func (s *ss) GetRune() (rune int, err os.Error) {
@@ -121,13 +123,69 @@ func (s *ss) GetRune() (rune int, err os.Error) {
return
}
+const EOF = -1
+
+// The public method returns an error; this private one panics.
+// If getRune reaches EOF, the return value is EOF (-1).
+func (s *ss) getRune() (rune int) {
+ if s.peekRune >= 0 {
+ rune = s.peekRune
+ s.peekRune = -1
+ return
+ }
+ rune, _, err := s.rr.ReadRune()
+ if err != nil {
+ if err == os.EOF {
+ return EOF
+ }
+ s.error(err)
+ }
+ return
+}
+
+// mustGetRune turns os.EOF into a panic(io.ErrUnexpectedEOF).
+// It is called in cases such as string scanning where an EOF is a
+// syntax error.
+func (s *ss) mustGetRune() (rune int) {
+ if s.peekRune >= 0 {
+ rune = s.peekRune
+ s.peekRune = -1
+ return
+ }
+ rune, _, err := s.rr.ReadRune()
+ if err != nil {
+ if err == os.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ s.error(err)
+ }
+ return
+}
+
+
func (s *ss) UngetRune(rune int) {
s.peekRune = rune
}
+func (s *ss) error(err os.Error) {
+ panic(scanError{err})
+}
+
+func (s *ss) errorString(err string) {
+ panic(scanError{os.ErrorString(err)})
+}
+
func (s *ss) Token() (tok string, err os.Error) {
+ defer func() {
+ if e := recover(); e != nil {
+ if se, ok := e.(scanError); ok {
+ err = se.err
+ } else {
+ panic(e)
+ }
+ }
+ }()
tok = s.token()
- err = s.err
return
}
@@ -184,7 +242,6 @@ func newScanState(r io.Reader, nlIsSpace bool) *ss {
}
s.nlIsSpace = nlIsSpace
s.peekRune = -1
- s.err = nil
return s
}
@@ -199,39 +256,38 @@ func (s *ss) free() {
_ = ssFree <- s
}
-// token returns the next space-delimited string from the input.
-// For Scanln, it stops at newlines. For Scan, newlines are treated as
-// spaces.
-func (s *ss) token() string {
+// skipSpace skips spaces and maybe newlines
+func (s *ss) skipSpace() {
s.buf.Reset()
- // skip white space and maybe newline
for {
- rune, err := s.GetRune()
- if err != nil {
- s.err = err
- return ""
+ rune := s.getRune()
+ if rune == EOF {
+ return
}
if rune == '\n' {
if s.nlIsSpace {
continue
}
- s.err = os.ErrorString("unexpected newline")
- return ""
+ s.errorString("unexpected newline")
+ return
}
if !unicode.IsSpace(rune) {
- s.buf.WriteRune(rune)
+ s.UngetRune(rune)
break
}
}
+}
+
+// token returns the next space-delimited string from the input.
+// For Scanln, it stops at newlines. For Scan, newlines are treated as
+// spaces.
+func (s *ss) token() string {
+ s.skipSpace()
// read until white space or newline
for {
- rune, err := s.GetRune()
- if err != nil {
- if err == os.EOF {
- break
- }
- s.err = err
- return ""
+ rune := s.getRune()
+ if rune == EOF {
+ break
}
if unicode.IsSpace(rune) {
s.UngetRune(rune)
@@ -242,9 +298,9 @@ func (s *ss) token() string {
return s.buf.String()
}
-// typeError sets the error string to an indication that the type of the operand did not match the format
+// typeError indicates that the type of the operand did not match the format
func (s *ss) typeError(field interface{}, expected string) {
- s.err = os.ErrorString("expected field of type pointer to " + expected + "; found " + reflect.Typeof(field).String())
+ s.errorString("expected field of type pointer to " + expected + "; found " + reflect.Typeof(field).String())
}
var intBits = uint(reflect.Typeof(int(0)).Size() * 8)
@@ -253,28 +309,29 @@ var complexError = os.ErrorString("syntax error scanning complex number")
// 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 {
- if s.err != nil { // don't overwrite error
- return false
- }
for _, v := range okVerbs {
if v == verb {
return true
}
}
- s.err = os.ErrorString("bad verb %" + string(verb) + " for " + typ)
+ s.errorString("bad verb %" + string(verb) + " for " + typ)
return false
}
-// scanBool converts the token to a boolean value.
-func (s *ss) scanBool(verb int, tok string) bool {
+// scanBool returns the value of the boolean represented by the next token.
+func (s *ss) scanBool(verb int) bool {
if !s.okVerb(verb, "tv", "boolean") {
return false
}
- var b bool
- b, s.err = strconv.Atob(tok)
+ tok := s.token()
+ b, err := strconv.Atob(tok)
+ if err != nil {
+ s.error(err)
+ }
return b
}
+// getBase returns the numeric base represented by the verb.
func (s *ss) getBase(verb int) int {
s.okVerb(verb, "bdoxXv", "integer") // sets s.err
base := 10
@@ -289,32 +346,34 @@ func (s *ss) getBase(verb int) int {
return base
}
-// convertInt returns the value of the integer
-// stored in the token, checking for overflow. Any error is stored in s.err.
-func (s *ss) convertInt(verb int, tok string, bitSize uint) (i int64) {
+// scanInt returns the value of the integer represented by the next
+// token, checking for overflow. Any error is stored in s.err.
+func (s *ss) scanInt(verb int, bitSize uint) int64 {
base := s.getBase(verb)
- if s.err != nil {
- return 0
+ tok := s.token()
+ i, err := strconv.Btoi64(tok, base)
+ if err != nil {
+ s.error(err)
}
- i, s.err = strconv.Btoi64(tok, base)
x := (i << (64 - bitSize)) >> (64 - bitSize)
if x != i {
- s.err = os.ErrorString("integer overflow on token " + tok)
+ s.errorString("integer overflow on token " + tok)
}
return i
}
-// convertUint returns the value of the unsigned integer
-// stored in the token, checking for overflow. Any error is stored in s.err.
-func (s *ss) convertUint(verb int, tok string, bitSize uint) (i uint64) {
+// scanUint returns the value of the unsigned integer represented
+// by the next token, checking for overflow. Any error is stored in s.err.
+func (s *ss) scanUint(verb int, bitSize uint) uint64 {
base := s.getBase(verb)
- if s.err != nil {
- return 0
+ tok := s.token()
+ i, err := strconv.Btoui64(tok, base)
+ if err != nil {
+ s.error(err)
}
- i, s.err = strconv.Btoui64(tok, base)
x := (i << (64 - bitSize)) >> (64 - bitSize)
if x != i {
- s.err = os.ErrorString("unsigned integer overflow on token " + tok)
+ s.errorString("unsigned integer overflow on token " + tok)
}
return i
}
@@ -327,13 +386,11 @@ func (s *ss) complexParts(str string) (real, imag string) {
real, str = floatPart(str)
// Must now have a sign.
if len(str) == 0 || (str[0] != '+' && str[0] != '-') {
- s.err = complexError
- return "", ""
+ s.error(complexError)
}
imag, str = floatPart(str)
if str != "i" {
- s.err = complexError
- return "", ""
+ s.error(complexError)
}
return real, imag
}
@@ -343,11 +400,11 @@ func (s *ss) complexParts(str string) (real, imag string) {
func floatPart(str string) (first, last string) {
i := 0
// leading sign?
- if len(str) > 0 && (str[0] == '+' || str[0] == '-') {
+ if len(str) > i && (str[0] == '+' || str[0] == '-') {
i++
}
// digits?
- for len(str) > 0 && '0' <= str[i] && str[i] <= '9' {
+ for len(str) > i && '0' <= str[i] && str[i] <= '9' {
i++
}
// period?
@@ -355,197 +412,323 @@ func floatPart(str string) (first, last string) {
i++
}
// fraction?
- for len(str) > 0 && '0' <= str[i] && str[i] <= '9' {
+ for len(str) > i && '0' <= str[i] && str[i] <= '9' {
i++
}
// exponent?
- if len(str) > 0 && (str[i] == 'e' || str[i] == 'E') {
+ if len(str) > i && (str[i] == 'e' || str[i] == 'E') {
i++
// leading sign?
- if str[0] == '+' || str[0] == '-' {
+ if str[i] == '+' || str[i] == '-' {
i++
}
// digits?
- for len(str) > 0 && '0' <= str[i] && str[i] <= '9' {
+ for len(str) > i && '0' <= str[i] && str[i] <= '9' {
i++
}
}
return str[0:i], str[i:]
}
-// scanFloat converts the string to a float value.
-func (s *ss) scanFloat(str string) float64 {
- var f float
- f, s.err = strconv.Atof(str)
+// convertFloat converts the string to a float value.
+func (s *ss) convertFloat(str string) float64 {
+ f, err := strconv.Atof(str)
+ if err != nil {
+ s.error(err)
+ }
return float64(f)
}
-// scanFloat32 converts the string to a float32 value.
-func (s *ss) scanFloat32(str string) float64 {
- var f float32
- f, s.err = strconv.Atof32(str)
+// convertFloat32 converts the string to a float32 value.
+func (s *ss) convertFloat32(str string) float64 {
+ f, err := strconv.Atof32(str)
+ if err != nil {
+ s.error(err)
+ }
return float64(f)
}
-// scanFloat64 converts the string to a float64 value.
-func (s *ss) scanFloat64(str string) float64 {
- var f float64
- f, s.err = strconv.Atof64(str)
+// convertFloat64 converts the string to a float64 value.
+func (s *ss) convertFloat64(str string) float64 {
+ f, err := strconv.Atof64(str)
+ if err != nil {
+ s.error(err)
+ }
return f
}
-// scanComplex converts the token to a complex128 value.
+// convertComplex converts the next token to a complex128 value.
// The atof argument is a type-specific reader for the underlying type.
// If we're reading complex64, atof will parse float32s and convert them
// to float64's to avoid reproducing this code for each complex type.
-func (s *ss) scanComplex(tok string, atof func(*ss, string) float64) complex128 {
- sreal, simag := s.complexParts(tok)
- if s.err != nil {
+func (s *ss) scanComplex(verb int, atof func(*ss, string) float64) complex128 {
+ if !s.okVerb(verb, floatVerbs, "complex") {
return 0
}
- var real, imag float64
- real = atof(s, sreal)
- if s.err != nil {
- return 0
+ tok := s.token()
+ sreal, simag := s.complexParts(tok)
+ real := atof(s, sreal)
+ imag := atof(s, simag)
+ return cmplx(real, imag)
+}
+
+// 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 {
+ if !s.okVerb(verb, "svqx", "string") {
+ return ""
}
- imag = atof(s, simag)
- if s.err != nil {
- return 0
+ s.skipSpace()
+ switch verb {
+ case 'q':
+ return s.quotedString()
+ case 'x':
+ return s.hexString()
}
- return cmplx(real, imag)
+ return s.token() // %s and %v just return the next word
+}
+
+// quotedString returns the double- or back-quoted string.
+func (s *ss) quotedString() string {
+ quote := s.mustGetRune()
+ switch quote {
+ case '`':
+ // Back-quoted: Anything goes until EOF or back quote.
+ for {
+ rune := s.mustGetRune()
+ if rune == quote {
+ break
+ }
+ s.buf.WriteRune(rune)
+ }
+ return s.buf.String()
+ case '"':
+ // Double-quoted: Include the quotes and let strconv.Unquote do the backslash escapes.
+ s.buf.WriteRune(quote)
+ for {
+ rune := s.mustGetRune()
+ s.buf.WriteRune(rune)
+ if rune == '\\' {
+ // In a legal backslash escape, no matter how long, only the character
+ // immediately after the escape can itself be a backslash or quote.
+ // Thus we only need to protect the first character after the backslash.
+ rune := s.mustGetRune()
+ s.buf.WriteRune(rune)
+ } else if rune == '"' {
+ break
+ }
+ }
+ result, err := strconv.Unquote(s.buf.String())
+ if err != nil {
+ s.error(err)
+ }
+ return result
+ default:
+ s.errorString("expected quoted string")
+ }
+ return ""
+}
+
+// hexDigit returns the value of the hexadecimal digit
+func (s *ss) hexDigit(digit int) int {
+ switch digit {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return digit - '0'
+ case 'a', 'b', 'c', 'd', 'e', 'f':
+ return 10 + digit - 'a'
+ case 'A', 'B', 'C', 'D', 'E', 'F':
+ return 10 + digit - 'A'
+ }
+ s.errorString("Scan: illegal hex digit")
+ return 0
+}
+
+// hexByte returns the next hex-encoded (two-character) byte from the input.
+// There must be either two hexadecimal digits or a space character in the input.
+func (s *ss) hexByte() (b byte, ok bool) {
+ rune1 := s.getRune()
+ if rune1 == EOF {
+ return
+ }
+ if unicode.IsSpace(rune1) {
+ s.UngetRune(rune1)
+ return
+ }
+ rune2 := s.mustGetRune()
+ return byte(s.hexDigit(rune1)<<4 | s.hexDigit(rune2)), true
+}
+
+// hexString returns the space-delimited hexpair-encoded string.
+func (s *ss) hexString() string {
+ for {
+ b, ok := s.hexByte()
+ if !ok {
+ break
+ }
+ s.buf.WriteByte(b)
+ }
+ if s.buf.Len() == 0 {
+ s.errorString("Scan: no hex data for %x string")
+ return ""
+ }
+ return s.buf.String()
}
const floatVerbs = "eEfFgGv"
// scanOne scans a single value, deriving the scanner from the type of the argument.
func (s *ss) scanOne(verb int, field interface{}) {
+ var err os.Error
// If the parameter has its own Scan method, use that.
if v, ok := field.(Scanner); ok {
- s.err = v.Scan(s)
- return
- }
- tok := s.token()
- if s.err != nil {
+ err = v.Scan(s)
+ if err != nil {
+ s.error(err)
+ }
return
}
switch v := field.(type) {
case *bool:
- *v = s.scanBool(verb, tok)
+ *v = s.scanBool(verb)
case *complex:
- *v = complex(s.scanComplex(tok, (*ss).scanFloat))
+ *v = complex(s.scanComplex(verb, (*ss).convertFloat))
case *complex64:
- *v = complex64(s.scanComplex(tok, (*ss).scanFloat32))
+ *v = complex64(s.scanComplex(verb, (*ss).convertFloat32))
case *complex128:
- *v = s.scanComplex(tok, (*ss).scanFloat64)
+ *v = s.scanComplex(verb, (*ss).convertFloat64)
case *int:
- *v = int(s.convertInt(verb, tok, intBits))
+ *v = int(s.scanInt(verb, intBits))
case *int8:
- *v = int8(s.convertInt(verb, tok, 8))
+ *v = int8(s.scanInt(verb, 8))
case *int16:
- *v = int16(s.convertInt(verb, tok, 16))
+ *v = int16(s.scanInt(verb, 16))
case *int32:
- *v = int32(s.convertInt(verb, tok, 32))
+ *v = int32(s.scanInt(verb, 32))
case *int64:
- *v = s.convertInt(verb, tok, intBits)
+ *v = s.scanInt(verb, intBits)
case *uint:
- *v = uint(s.convertUint(verb, tok, intBits))
+ *v = uint(s.scanUint(verb, intBits))
case *uint8:
- *v = uint8(s.convertUint(verb, tok, 8))
+ *v = uint8(s.scanUint(verb, 8))
case *uint16:
- *v = uint16(s.convertUint(verb, tok, 16))
+ *v = uint16(s.scanUint(verb, 16))
case *uint32:
- *v = uint32(s.convertUint(verb, tok, 32))
+ *v = uint32(s.scanUint(verb, 32))
case *uint64:
- *v = s.convertUint(verb, tok, 64)
+ *v = s.scanUint(verb, 64)
case *uintptr:
- *v = uintptr(s.convertUint(verb, tok, uintptrBits))
+ *v = uintptr(s.scanUint(verb, uintptrBits))
+ // Floats are tricky because you want to scan in the precision of the result, not
+ // scan in high precision and convert, in order to preserve the correct error condition.
case *float:
if s.okVerb(verb, floatVerbs, "float") {
- *v, s.err = strconv.Atof(tok)
+ *v = float(s.convertFloat(s.token()))
}
case *float32:
if s.okVerb(verb, floatVerbs, "float32") {
- *v, s.err = strconv.Atof32(tok)
+ *v = float32(s.convertFloat32(s.token()))
}
case *float64:
if s.okVerb(verb, floatVerbs, "float64") {
- *v, s.err = strconv.Atof64(tok)
+ *v = s.convertFloat64(s.token())
}
case *string:
- *v = tok
+ *v = s.convertString(verb)
default:
val := reflect.NewValue(v)
ptr, ok := val.(*reflect.PtrValue)
if !ok {
- s.err = os.ErrorString("Scan: type not a pointer: " + val.Type().String())
+ s.errorString("Scan: type not a pointer: " + val.Type().String())
return
}
switch v := ptr.Elem().(type) {
+ case *reflect.BoolValue:
+ v.Set(s.scanBool(verb))
case *reflect.IntValue:
- v.Set(int(s.convertInt(verb, tok, intBits)))
+ v.Set(int(s.scanInt(verb, intBits)))
case *reflect.Int8Value:
- v.Set(int8(s.convertInt(verb, tok, 8)))
+ v.Set(int8(s.scanInt(verb, 8)))
case *reflect.Int16Value:
- v.Set(int16(s.convertInt(verb, tok, 16)))
+ v.Set(int16(s.scanInt(verb, 16)))
case *reflect.Int32Value:
- v.Set(int32(s.convertInt(verb, tok, 32)))
+ v.Set(int32(s.scanInt(verb, 32)))
case *reflect.Int64Value:
- v.Set(s.convertInt(verb, tok, 64))
+ v.Set(s.scanInt(verb, 64))
case *reflect.UintValue:
- v.Set(uint(s.convertUint(verb, tok, intBits)))
+ v.Set(uint(s.scanUint(verb, intBits)))
case *reflect.Uint8Value:
- v.Set(uint8(s.convertUint(verb, tok, 8)))
+ v.Set(uint8(s.scanUint(verb, 8)))
case *reflect.Uint16Value:
- v.Set(uint16(s.convertUint(verb, tok, 16)))
+ v.Set(uint16(s.scanUint(verb, 16)))
case *reflect.Uint32Value:
- v.Set(uint32(s.convertUint(verb, tok, 32)))
+ v.Set(uint32(s.scanUint(verb, 32)))
case *reflect.Uint64Value:
- v.Set(s.convertUint(verb, tok, 64))
+ v.Set(s.scanUint(verb, 64))
case *reflect.UintptrValue:
- v.Set(uintptr(s.convertUint(verb, tok, uintptrBits)))
+ v.Set(uintptr(s.scanUint(verb, uintptrBits)))
+ case *reflect.StringValue:
+ v.Set(s.convertString(verb))
+ case *reflect.FloatValue:
+ v.Set(float(s.convertFloat(s.token())))
+ case *reflect.Float32Value:
+ v.Set(float32(s.convertFloat(s.token())))
+ case *reflect.Float64Value:
+ v.Set(s.convertFloat(s.token()))
+ case *reflect.ComplexValue:
+ v.Set(complex(s.scanComplex(verb, (*ss).convertFloat)))
+ case *reflect.Complex64Value:
+ v.Set(complex64(s.scanComplex(verb, (*ss).convertFloat32)))
+ case *reflect.Complex128Value:
+ v.Set(s.scanComplex(verb, (*ss).convertFloat64))
default:
- s.err = os.ErrorString("Scan: can't handle type: " + val.Type().String())
+ s.errorString("Scan: can't handle type: " + val.Type().String())
+ }
+ }
+}
+
+// errorHandler turns local panics into error returns. EOFs are benign.
+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
+ }
+ } else {
+ panic(e)
}
}
}
// doScan does the real work for scanning without a format string.
// At the moment, it handles only pointers to basic types.
-func (s *ss) doScan(a []interface{}) int {
- for fieldnum, field := range a {
+func (s *ss) doScan(a []interface{}) (numProcessed int, err os.Error) {
+ defer errorHandler(&err)
+ for _, field := range a {
s.scanOne('v', field)
- if s.err != nil {
- return fieldnum
- }
+ numProcessed++
}
// Check for newline if required.
if !s.nlIsSpace {
for {
- rune, err := s.GetRune()
- if err != nil {
- if err == os.EOF {
- break
- }
- s.err = err
- break
- }
- if rune == '\n' {
+ rune := s.getRune()
+ if rune == '\n' || rune == EOF {
break
}
if !unicode.IsSpace(rune) {
- s.err = os.ErrorString("Scan: expected newline")
+ s.errorString("Scan: expected newline")
break
}
}
}
- return len(a)
+ return
}
// doScanf does the real work when scanning with a format string.
// At the moment, it handles only pointers to basic types.
-func (s *ss) doScanf(format string, a []interface{}) int {
+func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err os.Error) {
+ defer errorHandler(&err)
end := len(format) - 1
- fieldnum := 0 // we process one item per non-trivial format
+ // We process one item per non-trivial format
for i := 0; i <= end; {
c, w := utf8.DecodeRuneInString(format[i:])
if c != '%' || i == end {
@@ -563,17 +746,14 @@ func (s *ss) doScanf(format string, a []interface{}) int {
continue
}
- if fieldnum >= len(a) { // out of operands
- s.err = os.ErrorString("too few operands for format %" + format[i-w:])
+ if numProcessed >= len(a) { // out of operands
+ s.errorString("too few operands for format %" + format[i-w:])
break
}
- field := a[fieldnum]
+ field := a[numProcessed]
s.scanOne(c, field)
- if s.err != nil {
- break
- }
- fieldnum++
+ numProcessed++
}
- return fieldnum
+ return
}
diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go
index 55808e964..353aa7e74 100644
--- a/src/pkg/fmt/scan_test.go
+++ b/src/pkg/fmt/scan_test.go
@@ -27,49 +27,65 @@ type ScanfTest struct {
}
type (
- renamedInt int
- renamedInt8 int8
- renamedInt16 int16
- renamedInt32 int32
- renamedInt64 int64
- renamedUint uint
- renamedUint8 uint8
- renamedUint16 uint16
- renamedUint32 uint32
- renamedUint64 uint64
- renamedUintptr uintptr
+ renamedBool bool
+ renamedInt int
+ renamedInt8 int8
+ renamedInt16 int16
+ renamedInt32 int32
+ renamedInt64 int64
+ renamedUint uint
+ renamedUint8 uint8
+ renamedUint16 uint16
+ renamedUint32 uint32
+ renamedUint64 uint64
+ renamedUintptr uintptr
+ renamedString string
+ renamedFloat float
+ renamedFloat32 float32
+ renamedFloat64 float64
+ renamedComplex complex
+ renamedComplex64 complex64
+ renamedComplex128 complex128
)
var (
- boolVal bool
- intVal int
- int8Val int8
- int16Val int16
- int32Val int32
- int64Val int64
- uintVal uint
- uint8Val uint8
- uint16Val uint16
- uint32Val uint32
- uint64Val uint64
- floatVal float
- float32Val float32
- float64Val float64
- stringVal string
- complexVal complex
- complex64Val complex64
- complex128Val complex128
- renamedIntVal renamedInt
- renamedInt8Val renamedInt8
- renamedInt16Val renamedInt16
- renamedInt32Val renamedInt32
- renamedInt64Val renamedInt64
- renamedUintVal renamedUint
- renamedUint8Val renamedUint8
- renamedUint16Val renamedUint16
- renamedUint32Val renamedUint32
- renamedUint64Val renamedUint64
- renamedUintptrVal renamedUintptr
+ boolVal bool
+ intVal int
+ int8Val int8
+ int16Val int16
+ int32Val int32
+ int64Val int64
+ uintVal uint
+ uint8Val uint8
+ uint16Val uint16
+ uint32Val uint32
+ uint64Val uint64
+ floatVal float
+ float32Val float32
+ float64Val float64
+ stringVal string
+ complexVal complex
+ complex64Val complex64
+ complex128Val complex128
+ renamedBoolVal renamedBool
+ renamedIntVal renamedInt
+ renamedInt8Val renamedInt8
+ renamedInt16Val renamedInt16
+ renamedInt32Val renamedInt32
+ renamedInt64Val renamedInt64
+ renamedUintVal renamedUint
+ renamedUint8Val renamedUint8
+ renamedUint16Val renamedUint16
+ renamedUint32Val renamedUint32
+ renamedUint64Val renamedUint64
+ renamedUintptrVal renamedUintptr
+ renamedStringVal renamedString
+ renamedFloatVal renamedFloat
+ renamedFloat32Val renamedFloat32
+ renamedFloat64Val renamedFloat64
+ renamedComplexVal renamedComplex
+ renamedComplex64Val renamedComplex64
+ renamedComplex128Val renamedComplex128
)
// Xs accepts any non-empty run of x's.
@@ -92,7 +108,9 @@ func (x *Xs) Scan(state ScanState) os.Error {
var xVal Xs
var scanTests = []ScanTest{
- ScanTest{"T\n", &boolVal, true},
+ // 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)},
@@ -125,8 +143,11 @@ var scanTests = []ScanTest{
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"},
// 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)},
@@ -140,14 +161,15 @@ var scanTests = []ScanTest{
ScanTest{"111\n", &renamedUint32Val, renamedUint32(111)},
ScanTest{"112\n", &renamedUint64Val, renamedUint64(112)},
ScanTest{"113\n", &renamedUintptrVal, renamedUintptr(113)},
+ ScanTest{"114\n", &renamedStringVal, renamedString("114")},
// Custom scanner.
ScanTest{" xxx ", &xVal, Xs("xxx")},
}
var scanfTests = []ScanfTest{
- ScanfTest{"%v", "FALSE\n", &boolVal, false},
- ScanfTest{"%t", "true\n", &boolVal, true},
+ 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{"%d", "73\n", &int8Val, int8(73)},
@@ -168,7 +190,15 @@ var scanfTests = []ScanfTest{
ScanfTest{"%x", "a75\n", &uintVal, uint(0xa75)},
ScanfTest{"%x", "A75\n", &uintVal, uint(0xa75)},
+ // 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"},
+
// 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{"%o", "0146\n", &renamedIntVal, renamedInt(102)},
ScanfTest{"%v", "103\n", &renamedUintVal, renamedUint(103)},
@@ -182,6 +212,13 @@ var scanfTests = []ScanfTest{
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{"%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)},
ScanfTest{"%x", "FFFFFFFF\n", &uint32Val, uint32(0xFFFFFFFF)},
}