1
0
mirror of https://github.com/golang/go synced 2024-11-20 05:54:43 -07:00

fmt.Scan: refactor the implementation so format-driven and normal scanning use the same function.

simplifies the code significantly.
Still TODO:
	- proper format handling
	- strings

R=rsc
CC=golang-dev
https://golang.org/cl/1432041
This commit is contained in:
Rob Pike 2010-05-31 14:53:15 -07:00
parent e6600ea17d
commit 8af4acf59d
2 changed files with 100 additions and 160 deletions

View File

@ -251,9 +251,23 @@ var intBits = uint(reflect.Typeof(int(0)).Size() * 8)
var uintptrBits = uint(reflect.Typeof(int(0)).Size() * 8) var uintptrBits = uint(reflect.Typeof(int(0)).Size() * 8)
var complexError = os.ErrorString("syntax error scanning complex number") 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)
return false
}
// scanBool converts the token to a boolean value. // scanBool converts the token to a boolean value.
func (s *ss) scanBool(tok string) bool { func (s *ss) scanBool(verb int, tok string) bool {
if s.err != nil { if !s.okVerb(verb, "tv", "boolean") {
return false return false
} }
var b bool var b bool
@ -261,9 +275,27 @@ func (s *ss) scanBool(tok string) bool {
return b return b
} }
func (s *ss) getBase(verb int) int {
s.okVerb(verb, "bdoxXv", "integer") // sets s.err
base := 10
switch verb {
case 'b':
base = 2
case 'o':
base = 8
case 'x', 'X':
base = 16
}
return base
}
// convertInt returns the value of the integer // convertInt returns the value of the integer
// stored in the token, checking for overflow. Any error is stored in s.err. // stored in the token, checking for overflow. Any error is stored in s.err.
func (s *ss) convertInt(tok string, bitSize uint, base int) (i int64) { func (s *ss) convertInt(verb int, tok string, bitSize uint) (i int64) {
base := s.getBase(verb)
if s.err != nil {
return 0
}
i, s.err = strconv.Btoi64(tok, base) 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 {
@ -274,7 +306,11 @@ func (s *ss) convertInt(tok string, bitSize uint, base int) (i int64) {
// convertUint returns the value of the unsigned integer // convertUint returns the value of the unsigned integer
// stored in the token, checking for overflow. Any error is stored in s.err. // stored in the token, checking for overflow. Any error is stored in s.err.
func (s *ss) convertUint(tok string, bitSize uint, base int) (i uint64) { func (s *ss) convertUint(verb int, tok string, bitSize uint) (i uint64) {
base := s.getBase(verb)
if s.err != nil {
return 0
}
i, s.err = strconv.Btoui64(tok, base) 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 {
@ -283,79 +319,6 @@ func (s *ss) convertUint(tok string, bitSize uint, base int) (i uint64) {
return i return i
} }
// scanInteger converts the token to an integer in the appropriate base
// and stores the result according to the type of the field.
func (s *ss) scanInteger(tok string, field interface{}, base int) {
switch v := field.(type) {
case *int:
*v = int(s.convertInt(tok, intBits, base))
return
case *int8:
*v = int8(s.convertInt(tok, 8, base))
return
case *int16:
*v = int16(s.convertInt(tok, 16, base))
return
case *int32:
*v = int32(s.convertInt(tok, 32, base))
return
case *int64:
*v = s.convertInt(tok, 64, base)
return
case *uint:
*v = uint(s.convertUint(tok, intBits, base))
return
case *uint8:
*v = uint8(s.convertUint(tok, 8, base))
return
case *uint16:
*v = uint16(s.convertUint(tok, 16, base))
return
case *uint32:
*v = uint32(s.convertUint(tok, 32, base))
return
case *uint64:
*v = uint64(s.convertUint(tok, 64, base))
return
case *uintptr:
*v = uintptr(s.convertUint(tok, uintptrBits, base))
return
}
// Not a basic type; probably a renamed type. We need to use reflection.
v := reflect.NewValue(field)
ptr, ok := v.(*reflect.PtrValue)
if !ok {
s.typeError(field, "integer")
return
}
switch v := ptr.Elem().(type) {
case *reflect.IntValue:
v.Set(int(s.convertInt(tok, intBits, base)))
case *reflect.Int8Value:
v.Set(int8(s.convertInt(tok, 8, base)))
case *reflect.Int16Value:
v.Set(int16(s.convertInt(tok, 16, base)))
case *reflect.Int32Value:
v.Set(int32(s.convertInt(tok, 32, base)))
case *reflect.Int64Value:
v.Set(s.convertInt(tok, 64, base))
case *reflect.UintValue:
v.Set(uint(s.convertUint(tok, intBits, base)))
case *reflect.Uint8Value:
v.Set(uint8(s.convertUint(tok, 8, base)))
case *reflect.Uint16Value:
v.Set(uint16(s.convertUint(tok, 16, base)))
case *reflect.Uint32Value:
v.Set(uint32(s.convertUint(tok, 32, base)))
case *reflect.Uint64Value:
v.Set(s.convertUint(tok, 64, base))
case *reflect.UintptrValue:
v.Set(uintptr(s.convertUint(tok, uintptrBits, base)))
default:
s.err = os.ErrorString("internal error: unknown int type")
}
}
// complexParts returns the strings representing the real and imaginary parts of the string. // complexParts returns the strings representing the real and imaginary parts of the string.
func (s *ss) complexParts(str string) (real, imag string) { func (s *ss) complexParts(str string) (real, imag string) {
if len(str) > 2 && str[0] == '(' && str[len(str)-1] == ')' { if len(str) > 2 && str[0] == '(' && str[len(str)-1] == ')' {
@ -436,9 +399,6 @@ func (s *ss) scanFloat64(str string) float64 {
// 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(tok string, atof func(*ss, string) float64) complex128 {
if s.err != nil {
return 0
}
sreal, simag := s.complexParts(tok) sreal, simag := s.complexParts(tok)
if s.err != nil { if s.err != nil {
return 0 return 0
@ -455,15 +415,22 @@ func (s *ss) scanComplex(tok string, atof func(*ss, string) float64) complex128
return cmplx(real, imag) return cmplx(real, imag)
} }
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(field interface{}) { func (s *ss) scanOne(verb int, field interface{}) {
// 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() tok := s.token()
if s.err != nil { if s.err != nil {
return return
} }
switch v := field.(type) { switch v := field.(type) {
case *bool: case *bool:
*v = s.scanBool(tok) *v = s.scanBool(verb, tok)
case *complex: case *complex:
*v = complex(s.scanComplex(tok, (*ss).scanFloat)) *v = complex(s.scanComplex(tok, (*ss).scanFloat))
case *complex64: case *complex64:
@ -471,62 +438,73 @@ func (s *ss) scanOne(field interface{}) {
case *complex128: case *complex128:
*v = s.scanComplex(tok, (*ss).scanFloat64) *v = s.scanComplex(tok, (*ss).scanFloat64)
case *int: case *int:
*v = int(s.convertInt(tok, intBits, 10)) *v = int(s.convertInt(verb, tok, intBits))
case *int8: case *int8:
*v = int8(s.convertInt(tok, 8, 10)) *v = int8(s.convertInt(verb, tok, 8))
case *int16: case *int16:
*v = int16(s.convertInt(tok, 16, 10)) *v = int16(s.convertInt(verb, tok, 16))
case *int32: case *int32:
*v = int32(s.convertInt(tok, 32, 10)) *v = int32(s.convertInt(verb, tok, 32))
case *int64: case *int64:
*v = s.convertInt(tok, intBits, 10) *v = s.convertInt(verb, tok, intBits)
case *uint: case *uint:
*v = uint(s.convertUint(tok, intBits, 10)) *v = uint(s.convertUint(verb, tok, intBits))
case *uint8: case *uint8:
*v = uint8(s.convertUint(tok, 8, 10)) *v = uint8(s.convertUint(verb, tok, 8))
case *uint16: case *uint16:
*v = uint16(s.convertUint(tok, 16, 10)) *v = uint16(s.convertUint(verb, tok, 16))
case *uint32: case *uint32:
*v = uint32(s.convertUint(tok, 32, 10)) *v = uint32(s.convertUint(verb, tok, 32))
case *uint64: case *uint64:
*v = s.convertUint(tok, 64, 10) *v = s.convertUint(verb, tok, 64)
case *uintptr: case *uintptr:
*v = uintptr(s.convertUint(tok, uintptrBits, 10)) *v = uintptr(s.convertUint(verb, tok, uintptrBits))
case *float: case *float:
if s.err == nil { if s.okVerb(verb, floatVerbs, "float") {
*v, s.err = strconv.Atof(tok) *v, s.err = strconv.Atof(tok)
} else {
*v = 0
} }
case *float32: case *float32:
if s.err == nil { if s.okVerb(verb, floatVerbs, "float32") {
*v, s.err = strconv.Atof32(tok) *v, s.err = strconv.Atof32(tok)
} else {
*v = 0
} }
case *float64: case *float64:
if s.err == nil { if s.okVerb(verb, floatVerbs, "float64") {
*v, s.err = strconv.Atof64(tok) *v, s.err = strconv.Atof64(tok)
} else {
*v = 0
} }
case *string: case *string:
*v = tok *v = tok
default: default:
t := reflect.Typeof(v) val := reflect.NewValue(v)
str := t.String() ptr, ok := val.(*reflect.PtrValue)
ptr, ok := t.(*reflect.PtrType)
if !ok { if !ok {
s.err = os.ErrorString("Scan: type not a pointer: " + str) s.err = os.ErrorString("Scan: type not a pointer: " + val.Type().String())
return return
} }
switch ptr.Elem().(type) { switch v := ptr.Elem().(type) {
case *reflect.IntType, *reflect.Int8Type, *reflect.Int16Type, *reflect.Int32Type, *reflect.Int64Type: case *reflect.IntValue:
s.scanInteger(tok, v, 10) v.Set(int(s.convertInt(verb, tok, intBits)))
case *reflect.UintType, *reflect.Uint8Type, *reflect.Uint16Type, *reflect.Uint32Type, *reflect.Uint64Type, *reflect.UintptrType: case *reflect.Int8Value:
s.scanInteger(tok, v, 10) v.Set(int8(s.convertInt(verb, tok, 8)))
case *reflect.Int16Value:
v.Set(int16(s.convertInt(verb, tok, 16)))
case *reflect.Int32Value:
v.Set(int32(s.convertInt(verb, tok, 32)))
case *reflect.Int64Value:
v.Set(s.convertInt(verb, tok, 64))
case *reflect.UintValue:
v.Set(uint(s.convertUint(verb, tok, intBits)))
case *reflect.Uint8Value:
v.Set(uint8(s.convertUint(verb, tok, 8)))
case *reflect.Uint16Value:
v.Set(uint16(s.convertUint(verb, tok, 16)))
case *reflect.Uint32Value:
v.Set(uint32(s.convertUint(verb, tok, 32)))
case *reflect.Uint64Value:
v.Set(s.convertUint(verb, tok, 64))
case *reflect.UintptrValue:
v.Set(uintptr(s.convertUint(verb, tok, uintptrBits)))
default: default:
s.err = os.ErrorString("Scan: can't handle type: " + t.String()) s.err = os.ErrorString("Scan: can't handle type: " + val.Type().String())
} }
} }
} }
@ -535,15 +513,7 @@ func (s *ss) scanOne(field interface{}) {
// 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{}) int {
for fieldnum, field := range a { for fieldnum, field := range a {
// If the parameter has its own Scan method, use that. s.scanOne('v', field)
if v, ok := field.(Scanner); ok {
s.err = v.Scan(s)
if s.err != nil {
return fieldnum
}
continue
}
s.scanOne(field)
if s.err != nil { if s.err != nil {
return fieldnum return fieldnum
} }
@ -592,48 +562,18 @@ func (s *ss) doScanf(format string, a []interface{}) int {
// TODO: WHAT NOW? // TODO: WHAT NOW?
continue continue
} }
if fieldnum >= len(a) { // out of operands if fieldnum >= len(a) { // out of operands
s.err = os.ErrorString("too few operands for format %" + format[i-w:]) s.err = os.ErrorString("too few operands for format %" + format[i-w:])
return fieldnum break
} }
field := a[fieldnum] field := a[fieldnum]
fieldnum++
// If the parameter has its own Scan method, use that. s.scanOne(c, field)
if v, ok := field.(Scanner); ok { if s.err != nil {
s.err = v.Scan(s) break
if s.err != nil {
return fieldnum - 1
}
continue
}
if c == 'v' {
// Default format works; just call doScan, but note that it will scan for the token
s.scanOne(field)
} else {
tok := s.token()
switch c {
case 't':
if v, ok := field.(*bool); ok {
*v = s.scanBool(tok)
} else {
s.typeError(field, "boolean")
}
case 'b':
s.scanInteger(tok, field, 2)
case 'o':
s.scanInteger(tok, field, 8)
case 'd':
s.scanInteger(tok, field, 10)
case 'x', 'X':
s.scanInteger(tok, field, 16)
default:
s.err = os.ErrorString("unknown scanning verb %" + format[i-w:])
}
if s.err != nil {
return fieldnum - 1
}
} }
fieldnum++
} }
return fieldnum return fieldnum
} }

View File

@ -170,15 +170,15 @@ var scanfTests = []ScanfTest{
// Renamed types // Renamed types
ScanfTest{"%v", "101\n", &renamedIntVal, renamedInt(101)}, ScanfTest{"%v", "101\n", &renamedIntVal, renamedInt(101)},
ScanfTest{"%d", "102\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)},
ScanfTest{"%d", "104\n", &renamedUintVal, renamedUint(104)}, ScanfTest{"%d", "104\n", &renamedUintVal, renamedUint(104)},
ScanfTest{"%d", "105\n", &renamedInt8Val, renamedInt8(105)}, ScanfTest{"%d", "105\n", &renamedInt8Val, renamedInt8(105)},
ScanfTest{"%d", "106\n", &renamedInt16Val, renamedInt16(106)}, ScanfTest{"%d", "106\n", &renamedInt16Val, renamedInt16(106)},
ScanfTest{"%d", "107\n", &renamedInt32Val, renamedInt32(107)}, ScanfTest{"%d", "107\n", &renamedInt32Val, renamedInt32(107)},
ScanfTest{"%d", "108\n", &renamedInt64Val, renamedInt64(108)}, ScanfTest{"%d", "108\n", &renamedInt64Val, renamedInt64(108)},
ScanfTest{"%d", "109\n", &renamedUint8Val, renamedUint8(109)}, ScanfTest{"%x", "6D\n", &renamedUint8Val, renamedUint8(109)},
ScanfTest{"%d", "110\n", &renamedUint16Val, renamedUint16(110)}, ScanfTest{"%o", "0156\n", &renamedUint16Val, renamedUint16(110)},
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)},