diff options
author | Rob Pike <r@golang.org> | 2010-06-01 15:21:21 -0700 |
---|---|---|
committer | Rob Pike <r@golang.org> | 2010-06-01 15:21:21 -0700 |
commit | fa0c7819085d9c58540429a5fbe78e4d015d660f (patch) | |
tree | a328dacad893c6e4ae38b0e91c9468f9a01d3c1d /src | |
parent | 5c8dc2166a65a9f362bb9ba4fde715224e87affb (diff) | |
download | golang-fa0c7819085d9c58540429a5fbe78e4d015d660f.tar.gz |
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
http://codereview.appspot.com/1458041
Diffstat (limited to 'src')
-rw-r--r-- | src/pkg/fmt/scan.go | 478 | ||||
-rw-r--r-- | src/pkg/fmt/scan_test.go | 123 |
2 files changed, 409 insertions, 192 deletions
diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go index 9851d4d29..17a08226e 100644 --- a/src/pkg/fmt/scan.go +++ b/src/pkg/fmt/scan.go @@ -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 { - sreal, simag := s.complexParts(tok) - if s.err != nil { +func (s *ss) scanComplex(verb int, atof func(*ss, string) float64) complex128 { + if !s.okVerb(verb, floatVerbs, "complex") { return 0 } - var real, imag float64 - real = atof(s, sreal) - if s.err != nil { - return 0 + tok := s.token() + sreal, simag := s.complexParts(tok) + 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 "" } - imag = atof(s, simag) - if s.err != nil { - return 0 + s.skipSpace() + switch verb { + case 'q': + return s.quotedString() + case 'x': + return s.hexString() } - return cmplx(real, imag) + 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 } diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go index 55808e964..353aa7e74 100644 --- a/src/pkg/fmt/scan_test.go +++ b/src/pkg/fmt/scan_test.go @@ -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)}, } |