--- /dev/null
+// 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)