printf.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Package printf implements a parser for fmt.Printf-style format
  2. // strings.
  3. //
  4. // It parses verbs according to the following syntax:
  5. // Numeric -> '0'-'9'
  6. // Letter -> 'a'-'z' | 'A'-'Z'
  7. // Index -> '[' Numeric+ ']'
  8. // Star -> '*'
  9. // Star -> Index '*'
  10. //
  11. // Precision -> Numeric+ | Star
  12. // Width -> Numeric+ | Star
  13. //
  14. // WidthAndPrecision -> Width '.' Precision
  15. // WidthAndPrecision -> Width '.'
  16. // WidthAndPrecision -> Width
  17. // WidthAndPrecision -> '.' Precision
  18. // WidthAndPrecision -> '.'
  19. //
  20. // Flag -> '+' | '-' | '#' | ' ' | '0'
  21. // Verb -> Letter | '%'
  22. //
  23. // Input -> '%' [ Flag+ ] [ WidthAndPrecision ] [ Index ] Verb
  24. package printf
  25. import (
  26. "errors"
  27. "regexp"
  28. "strconv"
  29. "strings"
  30. )
  31. // ErrInvalid is returned for invalid format strings or verbs.
  32. var ErrInvalid = errors.New("invalid format string")
  33. type Verb struct {
  34. Letter rune
  35. Flags string
  36. Width Argument
  37. Precision Argument
  38. // Which value in the argument list the verb uses.
  39. // -1 denotes the next argument,
  40. // values > 0 denote explicit arguments.
  41. // The value 0 denotes that no argument is consumed. This is the case for %%.
  42. Value int
  43. Raw string
  44. }
  45. // Argument is an implicit or explicit width or precision.
  46. type Argument interface {
  47. isArgument()
  48. }
  49. // The Default value, when no width or precision is provided.
  50. type Default struct{}
  51. // Zero is the implicit zero value.
  52. // This value may only appear for precisions in format strings like %6.f
  53. type Zero struct{}
  54. // Star is a * value, which may either refer to the next argument (Index == -1) or an explicit argument.
  55. type Star struct{ Index int }
  56. // A Literal value, such as 6 in %6d.
  57. type Literal int
  58. func (Default) isArgument() {}
  59. func (Zero) isArgument() {}
  60. func (Star) isArgument() {}
  61. func (Literal) isArgument() {}
  62. // Parse parses f and returns a list of actions.
  63. // An action may either be a literal string, or a Verb.
  64. func Parse(f string) ([]interface{}, error) {
  65. var out []interface{}
  66. for len(f) > 0 {
  67. if f[0] == '%' {
  68. v, n, err := ParseVerb(f)
  69. if err != nil {
  70. return nil, err
  71. }
  72. f = f[n:]
  73. out = append(out, v)
  74. } else {
  75. n := strings.IndexByte(f, '%')
  76. if n > -1 {
  77. out = append(out, f[:n])
  78. f = f[n:]
  79. } else {
  80. out = append(out, f)
  81. f = ""
  82. }
  83. }
  84. }
  85. return out, nil
  86. }
  87. func atoi(s string) int {
  88. n, _ := strconv.Atoi(s)
  89. return n
  90. }
  91. // ParseVerb parses the verb at the beginning of f.
  92. // It returns the verb, how much of the input was consumed, and an error, if any.
  93. func ParseVerb(f string) (Verb, int, error) {
  94. if len(f) < 2 {
  95. return Verb{}, 0, ErrInvalid
  96. }
  97. const (
  98. flags = 1
  99. width = 2
  100. widthStar = 3
  101. widthIndex = 5
  102. dot = 6
  103. prec = 7
  104. precStar = 8
  105. precIndex = 10
  106. verbIndex = 11
  107. verb = 12
  108. )
  109. m := re.FindStringSubmatch(f)
  110. if m == nil {
  111. return Verb{}, 0, ErrInvalid
  112. }
  113. v := Verb{
  114. Letter: []rune(m[verb])[0],
  115. Flags: m[flags],
  116. Raw: m[0],
  117. }
  118. if m[width] != "" {
  119. // Literal width
  120. v.Width = Literal(atoi(m[width]))
  121. } else if m[widthStar] != "" {
  122. // Star width
  123. if m[widthIndex] != "" {
  124. v.Width = Star{atoi(m[widthIndex])}
  125. } else {
  126. v.Width = Star{-1}
  127. }
  128. } else {
  129. // Default width
  130. v.Width = Default{}
  131. }
  132. if m[dot] == "" {
  133. // default precision
  134. v.Precision = Default{}
  135. } else {
  136. if m[prec] != "" {
  137. // Literal precision
  138. v.Precision = Literal(atoi(m[prec]))
  139. } else if m[precStar] != "" {
  140. // Star precision
  141. if m[precIndex] != "" {
  142. v.Precision = Star{atoi(m[precIndex])}
  143. } else {
  144. v.Precision = Star{-1}
  145. }
  146. } else {
  147. // Zero precision
  148. v.Precision = Zero{}
  149. }
  150. }
  151. if m[verb] == "%" {
  152. v.Value = 0
  153. } else if m[verbIndex] != "" {
  154. v.Value = atoi(m[verbIndex])
  155. } else {
  156. v.Value = -1
  157. }
  158. return v, len(m[0]), nil
  159. }
  160. const (
  161. flags = `([+#0 -]*)`
  162. verb = `([a-zA-Z%])`
  163. index = `(?:\[([0-9]+)\])`
  164. star = `((` + index + `)?\*)`
  165. width1 = `([0-9]+)`
  166. width2 = star
  167. width = `(?:` + width1 + `|` + width2 + `)`
  168. precision = width
  169. widthAndPrecision = `(?:(?:` + width + `)?(?:(\.)(?:` + precision + `)?)?)`
  170. )
  171. var re = regexp.MustCompile(`^%` + flags + widthAndPrecision + `?` + index + `?` + verb)