mirror of
https://github.com/golang/go
synced 2024-11-21 19:34:46 -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:
parent
cd016d7558
commit
b8a89972ee
@ -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 {
|
||||
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)
|
||||
if s.err != nil {
|
||||
return 0
|
||||
}
|
||||
var real, imag float64
|
||||
real = atof(s, sreal)
|
||||
if s.err != nil {
|
||||
return 0
|
||||
}
|
||||
imag = atof(s, simag)
|
||||
if s.err != nil {
|
||||
return 0
|
||||
}
|
||||
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 ""
|
||||
}
|
||||
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"
|
||||
|
||||
// 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
|
||||
}
|
||||
|
@ -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)},
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user