You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

216 lines
4.6 KiB

  1. /*
  2. Copyright 2015 Google LLC
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package main
  14. import (
  15. "bytes"
  16. "errors"
  17. "fmt"
  18. "io"
  19. "strconv"
  20. "strings"
  21. "unicode"
  22. "cloud.google.com/go/bigtable"
  23. )
  24. // Parse a GC policy. Valid policies include
  25. // never
  26. // maxage = 5d
  27. // maxversions = 3
  28. // maxage = 5d || maxversions = 3
  29. // maxage=30d || (maxage=3d && maxversions=100)
  30. func parseGCPolicy(s string) (bigtable.GCPolicy, error) {
  31. if strings.TrimSpace(s) == "never" {
  32. return bigtable.NoGcPolicy(), nil
  33. }
  34. r := strings.NewReader(s)
  35. p, err := parsePolicyExpr(r)
  36. if err != nil {
  37. return nil, fmt.Errorf("invalid GC policy: %v", err)
  38. }
  39. tok, err := getToken(r)
  40. if err != nil {
  41. return nil, err
  42. }
  43. if tok != "" {
  44. return nil, fmt.Errorf("invalid GC policy: want end of input, got %q", tok)
  45. }
  46. return p, nil
  47. }
  48. // expr ::= term (op term)*
  49. // op ::= "and" | "or" | "&&" | "||"
  50. func parsePolicyExpr(r io.RuneScanner) (bigtable.GCPolicy, error) {
  51. policy, err := parsePolicyTerm(r)
  52. if err != nil {
  53. return nil, err
  54. }
  55. for {
  56. tok, err := getToken(r)
  57. if err != nil {
  58. return nil, err
  59. }
  60. var f func(...bigtable.GCPolicy) bigtable.GCPolicy
  61. switch tok {
  62. case "and", "&&":
  63. f = bigtable.IntersectionPolicy
  64. case "or", "||":
  65. f = bigtable.UnionPolicy
  66. default:
  67. ungetToken(tok)
  68. return policy, nil
  69. }
  70. p2, err := parsePolicyTerm(r)
  71. if err != nil {
  72. return nil, err
  73. }
  74. policy = f(policy, p2)
  75. }
  76. }
  77. // term ::= "maxage" "=" duration | "maxversions" "=" int | "(" policy ")"
  78. func parsePolicyTerm(r io.RuneScanner) (bigtable.GCPolicy, error) {
  79. tok, err := getToken(r)
  80. if err != nil {
  81. return nil, err
  82. }
  83. switch tok {
  84. case "":
  85. return nil, errors.New("empty GC policy term")
  86. case "maxage", "maxversions":
  87. if err := expectToken(r, "="); err != nil {
  88. return nil, err
  89. }
  90. tok2, err := getToken(r)
  91. if err != nil {
  92. return nil, err
  93. }
  94. if tok2 == "" {
  95. return nil, errors.New("expected a token after '='")
  96. }
  97. if tok == "maxage" {
  98. dur, err := parseDuration(tok2)
  99. if err != nil {
  100. return nil, err
  101. }
  102. return bigtable.MaxAgePolicy(dur), nil
  103. }
  104. n, err := strconv.ParseUint(tok2, 10, 16)
  105. if err != nil {
  106. return nil, err
  107. }
  108. return bigtable.MaxVersionsPolicy(int(n)), nil
  109. case "(":
  110. p, err := parsePolicyExpr(r)
  111. if err != nil {
  112. return nil, err
  113. }
  114. if err := expectToken(r, ")"); err != nil {
  115. return nil, err
  116. }
  117. return p, nil
  118. default:
  119. return nil, fmt.Errorf("unexpected token: %q", tok)
  120. }
  121. }
  122. func expectToken(r io.RuneScanner, want string) error {
  123. got, err := getToken(r)
  124. if err != nil {
  125. return err
  126. }
  127. if got != want {
  128. return fmt.Errorf("expected %q, saw %q", want, got)
  129. }
  130. return nil
  131. }
  132. const noToken = "_" // empty token is valid, so use "_" instead
  133. // If not noToken, getToken will return this instead of reading a new token
  134. // from the input.
  135. var ungotToken = noToken
  136. // getToken extracts the first token from the input. Valid tokens include
  137. // any sequence of letters and digits, and these symbols: &&, ||, =, ( and ).
  138. // getToken returns ("", nil) at end of input.
  139. func getToken(r io.RuneScanner) (string, error) {
  140. if ungotToken != noToken {
  141. t := ungotToken
  142. ungotToken = noToken
  143. return t, nil
  144. }
  145. var err error
  146. // Skip leading whitespace.
  147. c := ' '
  148. for unicode.IsSpace(c) {
  149. c, _, err = r.ReadRune()
  150. if err == io.EOF {
  151. return "", nil
  152. }
  153. if err != nil {
  154. return "", err
  155. }
  156. }
  157. switch {
  158. case c == '=' || c == '(' || c == ')':
  159. return string(c), nil
  160. case c == '&' || c == '|':
  161. c2, _, err := r.ReadRune()
  162. if err != nil && err != io.EOF {
  163. return "", err
  164. }
  165. if c != c2 {
  166. return "", fmt.Errorf("expected %c%c", c, c)
  167. }
  168. return string([]rune{c, c}), nil
  169. case unicode.IsLetter(c) || unicode.IsDigit(c):
  170. // Collect an alphanumeric token.
  171. var b bytes.Buffer
  172. for unicode.IsLetter(c) || unicode.IsDigit(c) {
  173. b.WriteRune(c)
  174. c, _, err = r.ReadRune()
  175. if err == io.EOF {
  176. break
  177. }
  178. if err != nil {
  179. return "", err
  180. }
  181. }
  182. r.UnreadRune()
  183. return b.String(), nil
  184. default:
  185. return "", fmt.Errorf("bad rune %q", c)
  186. }
  187. }
  188. // "unget" a token so the next call to getToken will return it.
  189. func ungetToken(tok string) {
  190. if ungotToken != noToken {
  191. panic("ungetToken called twice")
  192. }
  193. ungotToken = tok
  194. }