mirror of
https://github.com/golang/go
synced 2024-11-20 06:14:53 -07:00
fmt.Scan: custom formatters
R=rsc CC=golang-dev https://golang.org/cl/1315042
This commit is contained in:
parent
a4129988a4
commit
6965b407dd
@ -21,6 +21,27 @@ type readRuner interface {
|
|||||||
ReadRune() (rune int, size int, err os.Error)
|
ReadRune() (rune int, size int, err os.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScanState represents the scanner state passed to custom scanners.
|
||||||
|
// Scanners may do rune-at-a-time scanning or ask the ScanState
|
||||||
|
// to discover the next space-delimited token.
|
||||||
|
type ScanState interface {
|
||||||
|
// GetRune reads the next rune (Unicode code point) from the input.
|
||||||
|
GetRune() (rune int, err os.Error)
|
||||||
|
// UngetRune causes the next call to Get to return the rune.
|
||||||
|
UngetRune(rune int)
|
||||||
|
// Token returns the next space-delimited token from the input.
|
||||||
|
Token() (token string, err os.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scanner is implemented by any value that has a Scan method, which scans
|
||||||
|
// the input for the representation of a value and stores the result in the
|
||||||
|
// receiver, which must be a pointer to be useful. The Scan method is called
|
||||||
|
// for any argument to Scan or Scanln that implements it.
|
||||||
|
type Scanner interface {
|
||||||
|
Scan(ScanState) os.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ss is the internal implementation of ScanState.
|
||||||
type ss struct {
|
type ss struct {
|
||||||
rr readRuner // where to read input
|
rr readRuner // where to read input
|
||||||
buf bytes.Buffer // token accumulator
|
buf bytes.Buffer // token accumulator
|
||||||
@ -29,9 +50,29 @@ type ss struct {
|
|||||||
err os.Error
|
err os.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ss) GetRune() (rune int, err os.Error) {
|
||||||
|
if s.peekRune >= 0 {
|
||||||
|
rune = s.peekRune
|
||||||
|
s.peekRune = -1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rune, _, err = s.rr.ReadRune()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ss) UngetRune(rune int) {
|
||||||
|
s.peekRune = rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ss) Token() (tok string, err os.Error) {
|
||||||
|
tok = s.token()
|
||||||
|
err = s.err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// readRune is a structure to enable reading UTF-8 encoded code points
|
// readRune is a structure to enable reading UTF-8 encoded code points
|
||||||
// from an io.Reader. It is used if the Reader given to the scanner does
|
// from an io.Reader. It is used if the Reader given to the scanner does
|
||||||
// not already implement readRuner.
|
// not already implement ReadRuner.
|
||||||
// TODO: readByteRune for things that can read bytes.
|
// TODO: readByteRune for things that can read bytes.
|
||||||
type readRune struct {
|
type readRune struct {
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
@ -97,17 +138,6 @@ func (s *ss) free() {
|
|||||||
_ = ssFree <- s
|
_ = ssFree <- s
|
||||||
}
|
}
|
||||||
|
|
||||||
// readRune reads the next rune, but checks the peeked item first.
|
|
||||||
func (s *ss) readRune() (rune int, err os.Error) {
|
|
||||||
if s.peekRune >= 0 {
|
|
||||||
rune = s.peekRune
|
|
||||||
s.peekRune = -1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rune, _, err = s.rr.ReadRune()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// token returns the next space-delimited string from the input.
|
// token returns the next space-delimited string from the input.
|
||||||
// For Scanln, it stops at newlines. For Scan, newlines are treated as
|
// For Scanln, it stops at newlines. For Scan, newlines are treated as
|
||||||
// spaces.
|
// spaces.
|
||||||
@ -115,7 +145,7 @@ func (s *ss) token() string {
|
|||||||
s.buf.Reset()
|
s.buf.Reset()
|
||||||
// skip white space and maybe newline
|
// skip white space and maybe newline
|
||||||
for {
|
for {
|
||||||
rune, err := s.readRune()
|
rune, err := s.GetRune()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.err = err
|
s.err = err
|
||||||
return ""
|
return ""
|
||||||
@ -134,7 +164,7 @@ func (s *ss) token() string {
|
|||||||
}
|
}
|
||||||
// read until white space or newline
|
// read until white space or newline
|
||||||
for {
|
for {
|
||||||
rune, err := s.readRune()
|
rune, err := s.GetRune()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == os.EOF {
|
if err == os.EOF {
|
||||||
break
|
break
|
||||||
@ -143,7 +173,7 @@ func (s *ss) token() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
if unicode.IsSpace(rune) {
|
if unicode.IsSpace(rune) {
|
||||||
s.peekRune = rune
|
s.UngetRune(rune)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
s.buf.WriteRune(rune)
|
s.buf.WriteRune(rune)
|
||||||
@ -324,6 +354,14 @@ func (s *ss) scanUint(tok string, bitSize uint) uint64 {
|
|||||||
// doScan does the real work. At the moment, it handles only pointers to basic types.
|
// doScan does the real work. At the moment, it handles only pointers to basic types.
|
||||||
func (s *ss) doScan(a []interface{}) int {
|
func (s *ss) doScan(a []interface{}) int {
|
||||||
for n, param := range a {
|
for n, param := range a {
|
||||||
|
// If the parameter has its own Scan method, use that.
|
||||||
|
if v, ok := param.(Scanner); ok {
|
||||||
|
s.err = v.Scan(s)
|
||||||
|
if s.err != nil {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
tok := s.token()
|
tok := s.token()
|
||||||
switch v := param.(type) {
|
switch v := param.(type) {
|
||||||
case *bool:
|
case *bool:
|
||||||
@ -392,7 +430,7 @@ func (s *ss) doScan(a []interface{}) int {
|
|||||||
// Check for newline if required.
|
// Check for newline if required.
|
||||||
if !s.nlIsSpace {
|
if !s.nlIsSpace {
|
||||||
for {
|
for {
|
||||||
rune, err := s.readRune()
|
rune, err := s.GetRune()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == os.EOF {
|
if err == os.EOF {
|
||||||
break
|
break
|
||||||
|
@ -38,6 +38,25 @@ var complexVal complex
|
|||||||
var complex64Val complex64
|
var complex64Val complex64
|
||||||
var complex128Val complex128
|
var complex128Val complex128
|
||||||
|
|
||||||
|
// Xs accepts any non-empty run of x's.
|
||||||
|
var xPat = testing.MustCompile("x+")
|
||||||
|
|
||||||
|
type Xs string
|
||||||
|
|
||||||
|
func (x *Xs) Scan(state ScanState) os.Error {
|
||||||
|
tok, err := state.Token()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !xPat.MatchString(tok) {
|
||||||
|
return os.ErrorString("syntax error for xs")
|
||||||
|
}
|
||||||
|
*x = Xs(tok)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var xVal Xs
|
||||||
|
|
||||||
var scanTests = []ScanTest{
|
var scanTests = []ScanTest{
|
||||||
ScanTest{"T\n", &boolVal, true},
|
ScanTest{"T\n", &boolVal, true},
|
||||||
ScanTest{"21\n", &intVal, 21},
|
ScanTest{"21\n", &intVal, 21},
|
||||||
@ -72,6 +91,9 @@ var scanTests = []ScanTest{
|
|||||||
ScanTest{"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i},
|
ScanTest{"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i},
|
||||||
ScanTest{"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)},
|
ScanTest{"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)},
|
||||||
ScanTest{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)},
|
ScanTest{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)},
|
||||||
|
|
||||||
|
// Custom scanner.
|
||||||
|
ScanTest{" xxx ", &xVal, Xs("xxx")},
|
||||||
}
|
}
|
||||||
|
|
||||||
var overflowTests = []ScanTest{
|
var overflowTests = []ScanTest{
|
||||||
|
Loading…
Reference in New Issue
Block a user