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.
 
 
 

209 lines
4.6 KiB

  1. // Copyright 2017 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package cldrtree
  5. import (
  6. "bytes"
  7. "fmt"
  8. "io"
  9. "reflect"
  10. "strconv"
  11. "strings"
  12. "golang.org/x/text/internal/gen"
  13. )
  14. func generate(b *Builder, t *Tree, w *gen.CodeWriter) error {
  15. fmt.Fprintln(w, `import "golang.org/x/text/internal/cldrtree"`)
  16. fmt.Fprintln(w)
  17. fmt.Fprintf(w, "var tree = &cldrtree.Tree{locales, indices, buckets}\n\n")
  18. w.WriteComment("Path values:\n" + b.stats())
  19. fmt.Fprintln(w)
  20. // Generate enum types.
  21. for _, e := range b.enums {
  22. // Build enum types.
  23. w.WriteComment("%s specifies a property of a CLDR field.", e.name)
  24. fmt.Fprintf(w, "type %s uint16\n", e.name)
  25. }
  26. d, err := getEnumData(b)
  27. if err != nil {
  28. return err
  29. }
  30. fmt.Fprintln(w, "const (")
  31. for i, k := range d.keys {
  32. fmt.Fprintf(w, "%s %s = %d // %s\n", toCamel(k), d.enums[i], d.m[k], k)
  33. }
  34. fmt.Fprintln(w, ")")
  35. w.WriteVar("locales", t.Locales)
  36. w.WriteVar("indices", t.Indices)
  37. // Generate string buckets.
  38. fmt.Fprintln(w, "var buckets = []string{")
  39. for i := range t.Buckets {
  40. fmt.Fprintf(w, "bucket%d,\n", i)
  41. }
  42. fmt.Fprint(w, "}\n\n")
  43. w.Size += int(reflect.TypeOf("").Size()) * len(t.Buckets)
  44. // Generate string buckets.
  45. for i, bucket := range t.Buckets {
  46. w.WriteVar(fmt.Sprint("bucket", i), bucket)
  47. }
  48. return nil
  49. }
  50. func generateTestData(b *Builder, w *gen.CodeWriter) error {
  51. d, err := getEnumData(b)
  52. if err != nil {
  53. return err
  54. }
  55. fmt.Fprintln(w)
  56. fmt.Fprintln(w, "var enumMap = map[string]uint16{")
  57. fmt.Fprintln(w, `"": 0,`)
  58. for _, k := range d.keys {
  59. fmt.Fprintf(w, "%q: %d,\n", k, d.m[k])
  60. }
  61. fmt.Fprintln(w, "}")
  62. return nil
  63. }
  64. func toCamel(s string) string {
  65. p := strings.Split(s, "-")
  66. for i, s := range p[1:] {
  67. p[i+1] = strings.Title(s)
  68. }
  69. return strings.Replace(strings.Join(p, ""), "/", "", -1)
  70. }
  71. func (b *Builder) stats() string {
  72. w := &bytes.Buffer{}
  73. b.rootMeta.validate()
  74. for _, es := range b.enums {
  75. fmt.Fprintf(w, "<%s>\n", es.name)
  76. printEnumValues(w, es, 1, nil)
  77. }
  78. fmt.Fprintln(w)
  79. printEnums(w, b.rootMeta.typeInfo, 0)
  80. fmt.Fprintln(w)
  81. fmt.Fprintln(w, "Nr elem: ", len(b.strToBucket))
  82. fmt.Fprintln(w, "uniqued size: ", b.size)
  83. fmt.Fprintln(w, "total string size: ", b.sizeAll)
  84. fmt.Fprintln(w, "bucket waste: ", b.bucketWaste)
  85. return w.String()
  86. }
  87. func printEnums(w io.Writer, s *typeInfo, indent int) {
  88. idStr := strings.Repeat(" ", indent) + "- "
  89. e := s.enum
  90. if e == nil {
  91. if len(s.entries) > 0 {
  92. panic(fmt.Errorf("has entries but no enum values: %#v", s.entries))
  93. }
  94. return
  95. }
  96. if e.name != "" {
  97. fmt.Fprintf(w, "%s<%s>\n", idStr, e.name)
  98. } else {
  99. printEnumValues(w, e, indent, s)
  100. }
  101. if s.sharedKeys() {
  102. for _, v := range s.entries {
  103. printEnums(w, v, indent+1)
  104. break
  105. }
  106. }
  107. }
  108. func printEnumValues(w io.Writer, e *enum, indent int, info *typeInfo) {
  109. idStr := strings.Repeat(" ", indent) + "- "
  110. for i := 0; i < len(e.keys); i++ {
  111. fmt.Fprint(w, idStr)
  112. k := e.keys[i]
  113. if u, err := strconv.ParseUint(k, 10, 16); err == nil {
  114. fmt.Fprintf(w, "%s", k)
  115. // Skip contiguous integers
  116. var v, last uint64
  117. for i++; i < len(e.keys); i++ {
  118. k = e.keys[i]
  119. if v, err = strconv.ParseUint(k, 10, 16); err != nil {
  120. break
  121. }
  122. last = v
  123. }
  124. if u < last {
  125. fmt.Fprintf(w, `..%d`, last)
  126. }
  127. fmt.Fprintln(w)
  128. if err != nil {
  129. fmt.Fprintf(w, "%s%s\n", idStr, k)
  130. }
  131. } else if k == "" {
  132. fmt.Fprintln(w, `""`)
  133. } else {
  134. fmt.Fprintf(w, "%s\n", k)
  135. }
  136. if info != nil && !info.sharedKeys() {
  137. if e := info.entries[enumIndex(i)]; e != nil {
  138. printEnums(w, e, indent+1)
  139. }
  140. }
  141. }
  142. }
  143. func getEnumData(b *Builder) (*enumData, error) {
  144. d := &enumData{m: map[string]int{}}
  145. if errStr := d.insert(b.rootMeta.typeInfo); errStr != "" {
  146. // TODO: consider returning the error.
  147. return nil, fmt.Errorf("cldrtree: %s", errStr)
  148. }
  149. return d, nil
  150. }
  151. type enumData struct {
  152. m map[string]int
  153. keys []string
  154. enums []string
  155. }
  156. func (d *enumData) insert(t *typeInfo) (errStr string) {
  157. e := t.enum
  158. if e == nil {
  159. return ""
  160. }
  161. for i, k := range e.keys {
  162. if _, err := strconv.ParseUint(k, 10, 16); err == nil {
  163. // We don't include any enum that has integer values.
  164. break
  165. }
  166. if v, ok := d.m[k]; ok {
  167. if v != i {
  168. return fmt.Sprintf("%q has value %d and %d", k, i, v)
  169. }
  170. } else {
  171. d.m[k] = i
  172. if k != "" {
  173. d.keys = append(d.keys, k)
  174. d.enums = append(d.enums, e.name)
  175. }
  176. }
  177. }
  178. for i := range t.enum.keys {
  179. if e := t.entries[enumIndex(i)]; e != nil {
  180. if errStr := d.insert(e); errStr != "" {
  181. return fmt.Sprintf("%q>%v", t.enum.keys[i], errStr)
  182. }
  183. }
  184. }
  185. return ""
  186. }