| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- // Package printf implements a parser for fmt.Printf-style format
- // strings.
- //
- // It parses verbs according to the following syntax:
- // Numeric -> '0'-'9'
- // Letter -> 'a'-'z' | 'A'-'Z'
- // Index -> '[' Numeric+ ']'
- // Star -> '*'
- // Star -> Index '*'
- //
- // Precision -> Numeric+ | Star
- // Width -> Numeric+ | Star
- //
- // WidthAndPrecision -> Width '.' Precision
- // WidthAndPrecision -> Width '.'
- // WidthAndPrecision -> Width
- // WidthAndPrecision -> '.' Precision
- // WidthAndPrecision -> '.'
- //
- // Flag -> '+' | '-' | '#' | ' ' | '0'
- // Verb -> Letter | '%'
- //
- // Input -> '%' [ Flag+ ] [ WidthAndPrecision ] [ Index ] Verb
- package printf
- import (
- "errors"
- "regexp"
- "strconv"
- "strings"
- )
- // ErrInvalid is returned for invalid format strings or verbs.
- var ErrInvalid = errors.New("invalid format string")
- type Verb struct {
- Letter rune
- Flags string
- Width Argument
- Precision Argument
- // Which value in the argument list the verb uses.
- // -1 denotes the next argument,
- // values > 0 denote explicit arguments.
- // The value 0 denotes that no argument is consumed. This is the case for %%.
- Value int
- Raw string
- }
- // Argument is an implicit or explicit width or precision.
- type Argument interface {
- isArgument()
- }
- // The Default value, when no width or precision is provided.
- type Default struct{}
- // Zero is the implicit zero value.
- // This value may only appear for precisions in format strings like %6.f
- type Zero struct{}
- // Star is a * value, which may either refer to the next argument (Index == -1) or an explicit argument.
- type Star struct{ Index int }
- // A Literal value, such as 6 in %6d.
- type Literal int
- func (Default) isArgument() {}
- func (Zero) isArgument() {}
- func (Star) isArgument() {}
- func (Literal) isArgument() {}
- // Parse parses f and returns a list of actions.
- // An action may either be a literal string, or a Verb.
- func Parse(f string) ([]interface{}, error) {
- var out []interface{}
- for len(f) > 0 {
- if f[0] == '%' {
- v, n, err := ParseVerb(f)
- if err != nil {
- return nil, err
- }
- f = f[n:]
- out = append(out, v)
- } else {
- n := strings.IndexByte(f, '%')
- if n > -1 {
- out = append(out, f[:n])
- f = f[n:]
- } else {
- out = append(out, f)
- f = ""
- }
- }
- }
- return out, nil
- }
- func atoi(s string) int {
- n, _ := strconv.Atoi(s)
- return n
- }
- // ParseVerb parses the verb at the beginning of f.
- // It returns the verb, how much of the input was consumed, and an error, if any.
- func ParseVerb(f string) (Verb, int, error) {
- if len(f) < 2 {
- return Verb{}, 0, ErrInvalid
- }
- const (
- flags = 1
- width = 2
- widthStar = 3
- widthIndex = 5
- dot = 6
- prec = 7
- precStar = 8
- precIndex = 10
- verbIndex = 11
- verb = 12
- )
- m := re.FindStringSubmatch(f)
- if m == nil {
- return Verb{}, 0, ErrInvalid
- }
- v := Verb{
- Letter: []rune(m[verb])[0],
- Flags: m[flags],
- Raw: m[0],
- }
- if m[width] != "" {
- // Literal width
- v.Width = Literal(atoi(m[width]))
- } else if m[widthStar] != "" {
- // Star width
- if m[widthIndex] != "" {
- v.Width = Star{atoi(m[widthIndex])}
- } else {
- v.Width = Star{-1}
- }
- } else {
- // Default width
- v.Width = Default{}
- }
- if m[dot] == "" {
- // default precision
- v.Precision = Default{}
- } else {
- if m[prec] != "" {
- // Literal precision
- v.Precision = Literal(atoi(m[prec]))
- } else if m[precStar] != "" {
- // Star precision
- if m[precIndex] != "" {
- v.Precision = Star{atoi(m[precIndex])}
- } else {
- v.Precision = Star{-1}
- }
- } else {
- // Zero precision
- v.Precision = Zero{}
- }
- }
- if m[verb] == "%" {
- v.Value = 0
- } else if m[verbIndex] != "" {
- v.Value = atoi(m[verbIndex])
- } else {
- v.Value = -1
- }
- return v, len(m[0]), nil
- }
- const (
- flags = `([+#0 -]*)`
- verb = `([a-zA-Z%])`
- index = `(?:\[([0-9]+)\])`
- star = `((` + index + `)?\*)`
- width1 = `([0-9]+)`
- width2 = star
- width = `(?:` + width1 + `|` + width2 + `)`
- precision = width
- widthAndPrecision = `(?:(?:` + width + `)?(?:(\.)(?:` + precision + `)?)?)`
- )
- var re = regexp.MustCompile(`^%` + flags + widthAndPrecision + `?` + index + `?` + verb)
|