1
0
mirror of https://github.com/golang/go synced 2024-11-22 01:24:42 -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,40 +256,39 @@ 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 {
rune := s.getRune()
if rune == EOF {
break
}
s.err = err
return ""
}
if unicode.IsSpace(rune) {
s.UngetRune(rune)
break
@ -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
err = v.Scan(s)
if err != nil {
s.error(err)
}
tok := s.token()
if s.err != nil {
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
numProcessed++
}
fieldnum++
}
return fieldnum
return
}

View File

@ -27,6 +27,7 @@ type ScanfTest struct {
}
type (
renamedBool bool
renamedInt int
renamedInt8 int8
renamedInt16 int16
@ -38,6 +39,13 @@ type (
renamedUint32 uint32
renamedUint64 uint64
renamedUintptr uintptr
renamedString string
renamedFloat float
renamedFloat32 float32
renamedFloat64 float64
renamedComplex complex
renamedComplex64 complex64
renamedComplex128 complex128
)
var (
@ -59,6 +67,7 @@ var (
complexVal complex
complex64Val complex64
complex128Val complex128
renamedBoolVal renamedBool
renamedIntVal renamedInt
renamedInt8Val renamedInt8
renamedInt16Val renamedInt16
@ -70,6 +79,13 @@ var (
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)},
}