]> Cypherpunks repositories - gostls13.git/commitdiff
fmt.Scan: renamings, strings, errors
authorRob Pike <r@golang.org>
Tue, 1 Jun 2010 22:21:21 +0000 (15:21 -0700)
committerRob Pike <r@golang.org>
Tue, 1 Jun 2010 22:21:21 +0000 (15:21 -0700)
- 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
https://golang.org/cl/1458041

src/pkg/fmt/scan.go
src/pkg/fmt/scan_test.go

index 9851d4d2951b92906fa08b4db76ac6b028e64820..17a08226eaab5ab5a1ef8ff01e54e1270c0c0e81 100644 (file)
@@ -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
 }
index 55808e964dcba42c0457554b77a412cb83357291..353aa7e747a02d3c8ced0aee1b361e74221e8cda 100644 (file)
@@ -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)},
 }