1
0
mirror of https://github.com/golang/go synced 2024-11-22 06:54:39 -07:00

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
https://golang.org/cl/1458041
This commit is contained in:
Rob Pike 2010-06-01 15:21:21 -07:00
parent cd016d7558
commit b8a89972ee
2 changed files with 411 additions and 194 deletions

View 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. // is less than the number of arguments, err will report why.
func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) { func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) {
s := newScanState(r, true) s := newScanState(r, true)
n = s.doScan(a) n, err = s.doScan(a)
err = s.err
s.free() s.free()
return return
} }
@ -82,8 +81,7 @@ func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) {
// number of arguments, err will report why. // number of arguments, err will report why.
func Fscanln(r io.Reader, a ...interface{}) (n int, err os.Error) { func Fscanln(r io.Reader, a ...interface{}) (n int, err os.Error) {
s := newScanState(r, false) s := newScanState(r, false)
n = s.doScan(a) n, err = s.doScan(a)
err = s.err
s.free() s.free()
return return
} }
@ -96,19 +94,23 @@ func XXXScanf(format string, a ...interface{}) (n int, err os.Error) {
// XXXFscanf is incomplete, do not use. // XXXFscanf is incomplete, do not use.
func XXXFscanf(r io.Reader, format string, a ...interface{}) (n int, err os.Error) { func XXXFscanf(r io.Reader, format string, a ...interface{}) (n int, err os.Error) {
s := newScanState(r, false) s := newScanState(r, false)
n = s.doScanf(format, a) n, err = s.doScanf(format, a)
err = s.err
s.free() s.free()
return 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. // ss is the internal implementation of ScanState.
type ss struct { type ss struct {
rr readRuner // where to read input rr readRuner // where to read input
buf bytes.Buffer // token accumulator buf bytes.Buffer // token accumulator
nlIsSpace bool // whether newline counts as white space nlIsSpace bool // whether newline counts as white space
peekRune int // one-rune lookahead peekRune int // one-rune lookahead
err os.Error
} }
func (s *ss) GetRune() (rune int, 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 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) { func (s *ss) UngetRune(rune int) {
s.peekRune = rune 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) { 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() tok = s.token()
err = s.err
return return
} }
@ -184,7 +242,6 @@ func newScanState(r io.Reader, nlIsSpace bool) *ss {
} }
s.nlIsSpace = nlIsSpace s.nlIsSpace = nlIsSpace
s.peekRune = -1 s.peekRune = -1
s.err = nil
return s return s
} }
@ -199,40 +256,39 @@ func (s *ss) free() {
_ = ssFree <- s _ = ssFree <- s
} }
// token returns the next space-delimited string from the input. // skipSpace skips spaces and maybe newlines
// For Scanln, it stops at newlines. For Scan, newlines are treated as func (s *ss) skipSpace() {
// spaces.
func (s *ss) token() string {
s.buf.Reset() s.buf.Reset()
// skip white space and maybe newline
for { for {
rune, err := s.GetRune() rune := s.getRune()
if err != nil { if rune == EOF {
s.err = err return
return ""
} }
if rune == '\n' { if rune == '\n' {
if s.nlIsSpace { if s.nlIsSpace {
continue continue
} }
s.err = os.ErrorString("unexpected newline") s.errorString("unexpected newline")
return "" return
} }
if !unicode.IsSpace(rune) { if !unicode.IsSpace(rune) {
s.buf.WriteRune(rune) s.UngetRune(rune)
break 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 // read until white space or newline
for { for {
rune, err := s.GetRune() rune := s.getRune()
if err != nil { if rune == EOF {
if err == os.EOF {
break break
} }
s.err = err
return ""
}
if unicode.IsSpace(rune) { if unicode.IsSpace(rune) {
s.UngetRune(rune) s.UngetRune(rune)
break break
@ -242,9 +298,9 @@ func (s *ss) token() string {
return s.buf.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) { 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) 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. // 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 { func (s *ss) okVerb(verb int, okVerbs, typ string) bool {
if s.err != nil { // don't overwrite error
return false
}
for _, v := range okVerbs { for _, v := range okVerbs {
if v == verb { if v == verb {
return true return true
} }
} }
s.err = os.ErrorString("bad verb %" + string(verb) + " for " + typ) s.errorString("bad verb %" + string(verb) + " for " + typ)
return false return false
} }
// scanBool converts the token to a boolean value. // scanBool returns the value of the boolean represented by the next token.
func (s *ss) scanBool(verb int, tok string) bool { func (s *ss) scanBool(verb int) bool {
if !s.okVerb(verb, "tv", "boolean") { if !s.okVerb(verb, "tv", "boolean") {
return false return false
} }
var b bool tok := s.token()
b, s.err = strconv.Atob(tok) b, err := strconv.Atob(tok)
if err != nil {
s.error(err)
}
return b return b
} }
// getBase returns the numeric base represented by the verb.
func (s *ss) getBase(verb int) int { func (s *ss) getBase(verb int) int {
s.okVerb(verb, "bdoxXv", "integer") // sets s.err s.okVerb(verb, "bdoxXv", "integer") // sets s.err
base := 10 base := 10
@ -289,32 +346,34 @@ func (s *ss) getBase(verb int) int {
return base return base
} }
// convertInt returns the value of the integer // scanInt returns the value of the integer represented by the next
// stored in the token, checking for overflow. Any error is stored in s.err. // token, checking for overflow. Any error is stored in s.err.
func (s *ss) convertInt(verb int, tok string, bitSize uint) (i int64) { func (s *ss) scanInt(verb int, bitSize uint) int64 {
base := s.getBase(verb) base := s.getBase(verb)
if s.err != nil { tok := s.token()
return 0 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) x := (i << (64 - bitSize)) >> (64 - bitSize)
if x != i { if x != i {
s.err = os.ErrorString("integer overflow on token " + tok) s.errorString("integer overflow on token " + tok)
} }
return i return i
} }
// convertUint returns the value of the unsigned integer // scanUint returns the value of the unsigned integer represented
// stored in the token, checking for overflow. Any error is stored in s.err. // by the next token, checking for overflow. Any error is stored in s.err.
func (s *ss) convertUint(verb int, tok string, bitSize uint) (i uint64) { func (s *ss) scanUint(verb int, bitSize uint) uint64 {
base := s.getBase(verb) base := s.getBase(verb)
if s.err != nil { tok := s.token()
return 0 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) x := (i << (64 - bitSize)) >> (64 - bitSize)
if x != i { if x != i {
s.err = os.ErrorString("unsigned integer overflow on token " + tok) s.errorString("unsigned integer overflow on token " + tok)
} }
return i return i
} }
@ -327,13 +386,11 @@ func (s *ss) complexParts(str string) (real, imag string) {
real, str = floatPart(str) real, str = floatPart(str)
// Must now have a sign. // Must now have a sign.
if len(str) == 0 || (str[0] != '+' && str[0] != '-') { if len(str) == 0 || (str[0] != '+' && str[0] != '-') {
s.err = complexError s.error(complexError)
return "", ""
} }
imag, str = floatPart(str) imag, str = floatPart(str)
if str != "i" { if str != "i" {
s.err = complexError s.error(complexError)
return "", ""
} }
return real, imag return real, imag
} }
@ -343,11 +400,11 @@ func (s *ss) complexParts(str string) (real, imag string) {
func floatPart(str string) (first, last string) { func floatPart(str string) (first, last string) {
i := 0 i := 0
// leading sign? // leading sign?
if len(str) > 0 && (str[0] == '+' || str[0] == '-') { if len(str) > i && (str[0] == '+' || str[0] == '-') {
i++ i++
} }
// digits? // digits?
for len(str) > 0 && '0' <= str[i] && str[i] <= '9' { for len(str) > i && '0' <= str[i] && str[i] <= '9' {
i++ i++
} }
// period? // period?
@ -355,197 +412,323 @@ func floatPart(str string) (first, last string) {
i++ i++
} }
// fraction? // fraction?
for len(str) > 0 && '0' <= str[i] && str[i] <= '9' { for len(str) > i && '0' <= str[i] && str[i] <= '9' {
i++ i++
} }
// exponent? // exponent?
if len(str) > 0 && (str[i] == 'e' || str[i] == 'E') { if len(str) > i && (str[i] == 'e' || str[i] == 'E') {
i++ i++
// leading sign? // leading sign?
if str[0] == '+' || str[0] == '-' { if str[i] == '+' || str[i] == '-' {
i++ i++
} }
// digits? // digits?
for len(str) > 0 && '0' <= str[i] && str[i] <= '9' { for len(str) > i && '0' <= str[i] && str[i] <= '9' {
i++ i++
} }
} }
return str[0:i], str[i:] return str[0:i], str[i:]
} }
// scanFloat converts the string to a float value. // convertFloat converts the string to a float value.
func (s *ss) scanFloat(str string) float64 { func (s *ss) convertFloat(str string) float64 {
var f float f, err := strconv.Atof(str)
f, s.err = strconv.Atof(str) if err != nil {
s.error(err)
}
return float64(f) return float64(f)
} }
// scanFloat32 converts the string to a float32 value. // convertFloat32 converts the string to a float32 value.
func (s *ss) scanFloat32(str string) float64 { func (s *ss) convertFloat32(str string) float64 {
var f float32 f, err := strconv.Atof32(str)
f, s.err = strconv.Atof32(str) if err != nil {
s.error(err)
}
return float64(f) return float64(f)
} }
// scanFloat64 converts the string to a float64 value. // convertFloat64 converts the string to a float64 value.
func (s *ss) scanFloat64(str string) float64 { func (s *ss) convertFloat64(str string) float64 {
var f float64 f, err := strconv.Atof64(str)
f, s.err = strconv.Atof64(str) if err != nil {
s.error(err)
}
return f 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. // The atof argument is a type-specific reader for the underlying type.
// If we're reading complex64, atof will parse float32s and convert them // If we're reading complex64, atof will parse float32s and convert them
// to float64's to avoid reproducing this code for each complex type. // to float64's to avoid reproducing this code for each complex type.
func (s *ss) scanComplex(tok string, atof func(*ss, string) float64) complex128 { func (s *ss) scanComplex(verb int, atof func(*ss, string) float64) complex128 {
if !s.okVerb(verb, floatVerbs, "complex") {
return 0
}
tok := s.token()
sreal, simag := s.complexParts(tok) sreal, simag := s.complexParts(tok)
if s.err != nil { real := atof(s, sreal)
return 0 imag := atof(s, simag)
}
var real, imag float64
real = atof(s, sreal)
if s.err != nil {
return 0
}
imag = atof(s, simag)
if s.err != nil {
return 0
}
return cmplx(real, imag) 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 ""
}
s.skipSpace()
switch verb {
case 'q':
return s.quotedString()
case 'x':
return s.hexString()
}
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" const floatVerbs = "eEfFgGv"
// scanOne scans a single value, deriving the scanner from the type of the argument. // scanOne scans a single value, deriving the scanner from the type of the argument.
func (s *ss) scanOne(verb int, field interface{}) { func (s *ss) scanOne(verb int, field interface{}) {
var err os.Error
// If the parameter has its own Scan method, use that. // If the parameter has its own Scan method, use that.
if v, ok := field.(Scanner); ok { if v, ok := field.(Scanner); ok {
s.err = v.Scan(s) err = v.Scan(s)
return if err != nil {
s.error(err)
} }
tok := s.token()
if s.err != nil {
return return
} }
switch v := field.(type) { switch v := field.(type) {
case *bool: case *bool:
*v = s.scanBool(verb, tok) *v = s.scanBool(verb)
case *complex: case *complex:
*v = complex(s.scanComplex(tok, (*ss).scanFloat)) *v = complex(s.scanComplex(verb, (*ss).convertFloat))
case *complex64: case *complex64:
*v = complex64(s.scanComplex(tok, (*ss).scanFloat32)) *v = complex64(s.scanComplex(verb, (*ss).convertFloat32))
case *complex128: case *complex128:
*v = s.scanComplex(tok, (*ss).scanFloat64) *v = s.scanComplex(verb, (*ss).convertFloat64)
case *int: case *int:
*v = int(s.convertInt(verb, tok, intBits)) *v = int(s.scanInt(verb, intBits))
case *int8: case *int8:
*v = int8(s.convertInt(verb, tok, 8)) *v = int8(s.scanInt(verb, 8))
case *int16: case *int16:
*v = int16(s.convertInt(verb, tok, 16)) *v = int16(s.scanInt(verb, 16))
case *int32: case *int32:
*v = int32(s.convertInt(verb, tok, 32)) *v = int32(s.scanInt(verb, 32))
case *int64: case *int64:
*v = s.convertInt(verb, tok, intBits) *v = s.scanInt(verb, intBits)
case *uint: case *uint:
*v = uint(s.convertUint(verb, tok, intBits)) *v = uint(s.scanUint(verb, intBits))
case *uint8: case *uint8:
*v = uint8(s.convertUint(verb, tok, 8)) *v = uint8(s.scanUint(verb, 8))
case *uint16: case *uint16:
*v = uint16(s.convertUint(verb, tok, 16)) *v = uint16(s.scanUint(verb, 16))
case *uint32: case *uint32:
*v = uint32(s.convertUint(verb, tok, 32)) *v = uint32(s.scanUint(verb, 32))
case *uint64: case *uint64:
*v = s.convertUint(verb, tok, 64) *v = s.scanUint(verb, 64)
case *uintptr: 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: case *float:
if s.okVerb(verb, floatVerbs, "float") { if s.okVerb(verb, floatVerbs, "float") {
*v, s.err = strconv.Atof(tok) *v = float(s.convertFloat(s.token()))
} }
case *float32: case *float32:
if s.okVerb(verb, floatVerbs, "float32") { if s.okVerb(verb, floatVerbs, "float32") {
*v, s.err = strconv.Atof32(tok) *v = float32(s.convertFloat32(s.token()))
} }
case *float64: case *float64:
if s.okVerb(verb, floatVerbs, "float64") { if s.okVerb(verb, floatVerbs, "float64") {
*v, s.err = strconv.Atof64(tok) *v = s.convertFloat64(s.token())
} }
case *string: case *string:
*v = tok *v = s.convertString(verb)
default: default:
val := reflect.NewValue(v) val := reflect.NewValue(v)
ptr, ok := val.(*reflect.PtrValue) ptr, ok := val.(*reflect.PtrValue)
if !ok { 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 return
} }
switch v := ptr.Elem().(type) { switch v := ptr.Elem().(type) {
case *reflect.BoolValue:
v.Set(s.scanBool(verb))
case *reflect.IntValue: case *reflect.IntValue:
v.Set(int(s.convertInt(verb, tok, intBits))) v.Set(int(s.scanInt(verb, intBits)))
case *reflect.Int8Value: case *reflect.Int8Value:
v.Set(int8(s.convertInt(verb, tok, 8))) v.Set(int8(s.scanInt(verb, 8)))
case *reflect.Int16Value: case *reflect.Int16Value:
v.Set(int16(s.convertInt(verb, tok, 16))) v.Set(int16(s.scanInt(verb, 16)))
case *reflect.Int32Value: case *reflect.Int32Value:
v.Set(int32(s.convertInt(verb, tok, 32))) v.Set(int32(s.scanInt(verb, 32)))
case *reflect.Int64Value: case *reflect.Int64Value:
v.Set(s.convertInt(verb, tok, 64)) v.Set(s.scanInt(verb, 64))
case *reflect.UintValue: case *reflect.UintValue:
v.Set(uint(s.convertUint(verb, tok, intBits))) v.Set(uint(s.scanUint(verb, intBits)))
case *reflect.Uint8Value: case *reflect.Uint8Value:
v.Set(uint8(s.convertUint(verb, tok, 8))) v.Set(uint8(s.scanUint(verb, 8)))
case *reflect.Uint16Value: case *reflect.Uint16Value:
v.Set(uint16(s.convertUint(verb, tok, 16))) v.Set(uint16(s.scanUint(verb, 16)))
case *reflect.Uint32Value: case *reflect.Uint32Value:
v.Set(uint32(s.convertUint(verb, tok, 32))) v.Set(uint32(s.scanUint(verb, 32)))
case *reflect.Uint64Value: case *reflect.Uint64Value:
v.Set(s.convertUint(verb, tok, 64)) v.Set(s.scanUint(verb, 64))
case *reflect.UintptrValue: 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: 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. // doScan does the real work for scanning without a format string.
// At the moment, it handles only pointers to basic types. // At the moment, it handles only pointers to basic types.
func (s *ss) doScan(a []interface{}) int { func (s *ss) doScan(a []interface{}) (numProcessed int, err os.Error) {
for fieldnum, field := range a { defer errorHandler(&err)
for _, field := range a {
s.scanOne('v', field) s.scanOne('v', field)
if s.err != nil { numProcessed++
return fieldnum
}
} }
// Check for newline if required. // Check for newline if required.
if !s.nlIsSpace { if !s.nlIsSpace {
for { for {
rune, err := s.GetRune() rune := s.getRune()
if err != nil { if rune == '\n' || rune == EOF {
if err == os.EOF {
break
}
s.err = err
break
}
if rune == '\n' {
break break
} }
if !unicode.IsSpace(rune) { if !unicode.IsSpace(rune) {
s.err = os.ErrorString("Scan: expected newline") s.errorString("Scan: expected newline")
break break
} }
} }
} }
return len(a) return
} }
// doScanf does the real work when scanning with a format string. // doScanf does the real work when scanning with a format string.
// At the moment, it handles only pointers to basic types. // 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 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; { for i := 0; i <= end; {
c, w := utf8.DecodeRuneInString(format[i:]) c, w := utf8.DecodeRuneInString(format[i:])
if c != '%' || i == end { if c != '%' || i == end {
@ -563,17 +746,14 @@ func (s *ss) doScanf(format string, a []interface{}) int {
continue continue
} }
if fieldnum >= len(a) { // out of operands if numProcessed >= len(a) { // out of operands
s.err = os.ErrorString("too few operands for format %" + format[i-w:]) s.errorString("too few operands for format %" + format[i-w:])
break break
} }
field := a[fieldnum] field := a[numProcessed]
s.scanOne(c, field) s.scanOne(c, field)
if s.err != nil { numProcessed++
break
} }
fieldnum++ return
}
return fieldnum
} }

View File

@ -27,6 +27,7 @@ type ScanfTest struct {
} }
type ( type (
renamedBool bool
renamedInt int renamedInt int
renamedInt8 int8 renamedInt8 int8
renamedInt16 int16 renamedInt16 int16
@ -38,6 +39,13 @@ type (
renamedUint32 uint32 renamedUint32 uint32
renamedUint64 uint64 renamedUint64 uint64
renamedUintptr uintptr renamedUintptr uintptr
renamedString string
renamedFloat float
renamedFloat32 float32
renamedFloat64 float64
renamedComplex complex
renamedComplex64 complex64
renamedComplex128 complex128
) )
var ( var (
@ -59,6 +67,7 @@ var (
complexVal complex complexVal complex
complex64Val complex64 complex64Val complex64
complex128Val complex128 complex128Val complex128
renamedBoolVal renamedBool
renamedIntVal renamedInt renamedIntVal renamedInt
renamedInt8Val renamedInt8 renamedInt8Val renamedInt8
renamedInt16Val renamedInt16 renamedInt16Val renamedInt16
@ -70,6 +79,13 @@ var (
renamedUint32Val renamedUint32 renamedUint32Val renamedUint32
renamedUint64Val renamedUint64 renamedUint64Val renamedUint64
renamedUintptrVal renamedUintptr 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. // 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 xVal Xs
var scanTests = []ScanTest{ 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{"21\n", &intVal, 21},
ScanTest{"22\n", &int8Val, int8(22)}, ScanTest{"22\n", &int8Val, int8(22)},
ScanTest{"23\n", &int16Val, int16(23)}, ScanTest{"23\n", &int16Val, int16(23)},
@ -125,8 +143,11 @@ var scanTests = []ScanTest{
ScanTest{"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i}, ScanTest{"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i},
ScanTest{"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)}, ScanTest{"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)},
ScanTest{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)}, ScanTest{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)},
ScanTest{"hello\n", &stringVal, "hello"},
// Renamed types // Renamed types
ScanTest{"true\n", &renamedBoolVal, renamedBool(true)},
ScanTest{"F\n", &renamedBoolVal, renamedBool(false)},
ScanTest{"101\n", &renamedIntVal, renamedInt(101)}, ScanTest{"101\n", &renamedIntVal, renamedInt(101)},
ScanTest{"102\n", &renamedIntVal, renamedInt(102)}, ScanTest{"102\n", &renamedIntVal, renamedInt(102)},
ScanTest{"103\n", &renamedUintVal, renamedUint(103)}, ScanTest{"103\n", &renamedUintVal, renamedUint(103)},
@ -140,14 +161,15 @@ var scanTests = []ScanTest{
ScanTest{"111\n", &renamedUint32Val, renamedUint32(111)}, ScanTest{"111\n", &renamedUint32Val, renamedUint32(111)},
ScanTest{"112\n", &renamedUint64Val, renamedUint64(112)}, ScanTest{"112\n", &renamedUint64Val, renamedUint64(112)},
ScanTest{"113\n", &renamedUintptrVal, renamedUintptr(113)}, ScanTest{"113\n", &renamedUintptrVal, renamedUintptr(113)},
ScanTest{"114\n", &renamedStringVal, renamedString("114")},
// Custom scanner. // Custom scanner.
ScanTest{" xxx ", &xVal, Xs("xxx")}, ScanTest{" xxx ", &xVal, Xs("xxx")},
} }
var scanfTests = []ScanfTest{ var scanfTests = []ScanfTest{
ScanfTest{"%v", "FALSE\n", &boolVal, false}, ScanfTest{"%v", "TRUE\n", &boolVal, true},
ScanfTest{"%t", "true\n", &boolVal, true}, ScanfTest{"%t", "false\n", &boolVal, false},
ScanfTest{"%v", "-71\n", &intVal, -71}, ScanfTest{"%v", "-71\n", &intVal, -71},
ScanfTest{"%d", "72\n", &intVal, 72}, ScanfTest{"%d", "72\n", &intVal, 72},
ScanfTest{"%d", "73\n", &int8Val, int8(73)}, 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)},
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 // 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{"%v", "101\n", &renamedIntVal, renamedInt(101)},
ScanfTest{"%o", "0146\n", &renamedIntVal, renamedInt(102)}, ScanfTest{"%o", "0146\n", &renamedIntVal, renamedInt(102)},
ScanfTest{"%v", "103\n", &renamedUintVal, renamedUint(103)}, ScanfTest{"%v", "103\n", &renamedUintVal, renamedUint(103)},
@ -182,6 +212,13 @@ var scanfTests = []ScanfTest{
ScanfTest{"%d", "111\n", &renamedUint32Val, renamedUint32(111)}, ScanfTest{"%d", "111\n", &renamedUint32Val, renamedUint32(111)},
ScanfTest{"%d", "112\n", &renamedUint64Val, renamedUint64(112)}, ScanfTest{"%d", "112\n", &renamedUint64Val, renamedUint64(112)},
ScanfTest{"%d", "113\n", &renamedUintptrVal, renamedUintptr(113)}, 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)}, ScanfTest{"%x", "FFFFFFFF\n", &uint32Val, uint32(0xFFFFFFFF)},
} }