mirror of
https://github.com/golang/go
synced 2024-11-20 06:54:42 -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:
parent
e6600ea17d
commit
8af4acf59d
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)},
|
||||||
|
Loading…
Reference in New Issue
Block a user