1
0
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:
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.
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
}

View File

@ -27,49 +27,65 @@ type ScanfTest struct {
}
type (
renamedInt int
renamedInt8 int8
renamedInt16 int16
renamedInt32 int32
renamedInt64 int64
renamedUint uint
renamedUint8 uint8
renamedUint16 uint16
renamedUint32 uint32
renamedUint64 uint64
renamedUintptr uintptr
renamedBool bool
renamedInt int
renamedInt8 int8
renamedInt16 int16
renamedInt32 int32
renamedInt64 int64
renamedUint uint
renamedUint8 uint8
renamedUint16 uint16
renamedUint32 uint32
renamedUint64 uint64
renamedUintptr uintptr
renamedString string
renamedFloat float
renamedFloat32 float32
renamedFloat64 float64
renamedComplex complex
renamedComplex64 complex64
renamedComplex128 complex128
)
var (
boolVal bool
intVal int
int8Val int8
int16Val int16
int32Val int32
int64Val int64
uintVal uint
uint8Val uint8
uint16Val uint16
uint32Val uint32
uint64Val uint64
floatVal float
float32Val float32
float64Val float64
stringVal string
complexVal complex
complex64Val complex64
complex128Val complex128
renamedIntVal renamedInt
renamedInt8Val renamedInt8
renamedInt16Val renamedInt16
renamedInt32Val renamedInt32
renamedInt64Val renamedInt64
renamedUintVal renamedUint
renamedUint8Val renamedUint8
renamedUint16Val renamedUint16
renamedUint32Val renamedUint32
renamedUint64Val renamedUint64
renamedUintptrVal renamedUintptr
boolVal bool
intVal int
int8Val int8
int16Val int16
int32Val int32
int64Val int64
uintVal uint
uint8Val uint8
uint16Val uint16
uint32Val uint32
uint64Val uint64
floatVal float
float32Val float32
float64Val float64
stringVal string
complexVal complex
complex64Val complex64
complex128Val complex128
renamedBoolVal renamedBool
renamedIntVal renamedInt
renamedInt8Val renamedInt8
renamedInt16Val renamedInt16
renamedInt32Val renamedInt32
renamedInt64Val renamedInt64
renamedUintVal renamedUint
renamedUint8Val renamedUint8
renamedUint16Val renamedUint16
renamedUint32Val renamedUint32
renamedUint64Val renamedUint64
renamedUintptrVal renamedUintptr
renamedStringVal renamedString
renamedFloatVal renamedFloat
renamedFloat32Val renamedFloat32
renamedFloat64Val renamedFloat64
renamedComplexVal renamedComplex
renamedComplex64Val renamedComplex64
renamedComplex128Val renamedComplex128
)
// Xs accepts any non-empty run of x's.
@ -92,7 +108,9 @@ func (x *Xs) Scan(state ScanState) os.Error {
var xVal Xs
var scanTests = []ScanTest{
ScanTest{"T\n", &boolVal, true},
// Numbers
ScanTest{"T\n", &boolVal, true}, // boolean test vals toggle to be sure they are written
ScanTest{"F\n", &boolVal, false}, // restored to zero value
ScanTest{"21\n", &intVal, 21},
ScanTest{"22\n", &int8Val, int8(22)},
ScanTest{"23\n", &int16Val, int16(23)},
@ -125,8 +143,11 @@ var scanTests = []ScanTest{
ScanTest{"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i},
ScanTest{"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)},
ScanTest{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)},
ScanTest{"hello\n", &stringVal, "hello"},
// Renamed types
ScanTest{"true\n", &renamedBoolVal, renamedBool(true)},
ScanTest{"F\n", &renamedBoolVal, renamedBool(false)},
ScanTest{"101\n", &renamedIntVal, renamedInt(101)},
ScanTest{"102\n", &renamedIntVal, renamedInt(102)},
ScanTest{"103\n", &renamedUintVal, renamedUint(103)},
@ -140,14 +161,15 @@ var scanTests = []ScanTest{
ScanTest{"111\n", &renamedUint32Val, renamedUint32(111)},
ScanTest{"112\n", &renamedUint64Val, renamedUint64(112)},
ScanTest{"113\n", &renamedUintptrVal, renamedUintptr(113)},
ScanTest{"114\n", &renamedStringVal, renamedString("114")},
// Custom scanner.
ScanTest{" xxx ", &xVal, Xs("xxx")},
}
var scanfTests = []ScanfTest{
ScanfTest{"%v", "FALSE\n", &boolVal, false},
ScanfTest{"%t", "true\n", &boolVal, true},
ScanfTest{"%v", "TRUE\n", &boolVal, true},
ScanfTest{"%t", "false\n", &boolVal, false},
ScanfTest{"%v", "-71\n", &intVal, -71},
ScanfTest{"%d", "72\n", &intVal, 72},
ScanfTest{"%d", "73\n", &int8Val, int8(73)},
@ -168,7 +190,15 @@ var scanfTests = []ScanfTest{
ScanfTest{"%x", "a75\n", &uintVal, uint(0xa75)},
ScanfTest{"%x", "A75\n", &uintVal, uint(0xa75)},
// Strings
ScanfTest{"%s", "using-%s\n", &stringVal, "using-%s"},
ScanfTest{"%x", "7573696e672d2578\n", &stringVal, "using-%x"},
ScanfTest{"%q", `"quoted\twith\\do\u0075bl\x65s"` + "\n", &stringVal, "quoted\twith\\doubles"},
ScanfTest{"%q", "`quoted with backs`\n", &stringVal, "quoted with backs"},
// Renamed types
ScanfTest{"%v\n", "true\n", &renamedBoolVal, renamedBool(true)},
ScanfTest{"%t\n", "F\n", &renamedBoolVal, renamedBool(false)},
ScanfTest{"%v", "101\n", &renamedIntVal, renamedInt(101)},
ScanfTest{"%o", "0146\n", &renamedIntVal, renamedInt(102)},
ScanfTest{"%v", "103\n", &renamedUintVal, renamedUint(103)},
@ -182,6 +212,13 @@ var scanfTests = []ScanfTest{
ScanfTest{"%d", "111\n", &renamedUint32Val, renamedUint32(111)},
ScanfTest{"%d", "112\n", &renamedUint64Val, renamedUint64(112)},
ScanfTest{"%d", "113\n", &renamedUintptrVal, renamedUintptr(113)},
ScanfTest{"%s", "114\n", &renamedStringVal, renamedString("114")},
ScanfTest{"%g", "115.1\n", &renamedFloatVal, renamedFloat(115.1)},
ScanfTest{"%g", "116e1\n", &renamedFloat32Val, renamedFloat32(116e1)},
ScanfTest{"%g", "-11.7e+1", &renamedFloat64Val, renamedFloat64(-11.7e+1)},
ScanfTest{"%g", "11+5.1i\n", &renamedComplexVal, renamedComplex(11 + 5.1i)},
ScanfTest{"%g", "11+6e1i\n", &renamedComplex64Val, renamedComplex64(11 + 6e1i)},
ScanfTest{"%g", "-11.+7e+1i", &renamedComplex128Val, renamedComplex128(-11. + 7e+1i)},
ScanfTest{"%x", "FFFFFFFF\n", &uint32Val, uint32(0xFFFFFFFF)},
}