123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- // Copyright 2017 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package cldrtree
- import (
- "bytes"
- "fmt"
- "io"
- "reflect"
- "strconv"
- "strings"
- "golang.org/x/text/internal/gen"
- )
- func generate(b *Builder, t *Tree, w *gen.CodeWriter) error {
- fmt.Fprintln(w, `import "golang.org/x/text/internal/cldrtree"`)
- fmt.Fprintln(w)
- fmt.Fprintf(w, "var tree = &cldrtree.Tree{locales, indices, buckets}\n\n")
- w.WriteComment("Path values:\n" + b.stats())
- fmt.Fprintln(w)
- // Generate enum types.
- for _, e := range b.enums {
- // Build enum types.
- w.WriteComment("%s specifies a property of a CLDR field.", e.name)
- fmt.Fprintf(w, "type %s uint16\n", e.name)
- }
- d, err := getEnumData(b)
- if err != nil {
- return err
- }
- fmt.Fprintln(w, "const (")
- for i, k := range d.keys {
- fmt.Fprintf(w, "%s %s = %d // %s\n", toCamel(k), d.enums[i], d.m[k], k)
- }
- fmt.Fprintln(w, ")")
- w.WriteVar("locales", t.Locales)
- w.WriteVar("indices", t.Indices)
- // Generate string buckets.
- fmt.Fprintln(w, "var buckets = []string{")
- for i := range t.Buckets {
- fmt.Fprintf(w, "bucket%d,\n", i)
- }
- fmt.Fprint(w, "}\n\n")
- w.Size += int(reflect.TypeOf("").Size()) * len(t.Buckets)
- // Generate string buckets.
- for i, bucket := range t.Buckets {
- w.WriteVar(fmt.Sprint("bucket", i), bucket)
- }
- return nil
- }
- func generateTestData(b *Builder, w *gen.CodeWriter) error {
- d, err := getEnumData(b)
- if err != nil {
- return err
- }
- fmt.Fprintln(w)
- fmt.Fprintln(w, "var enumMap = map[string]uint16{")
- fmt.Fprintln(w, `"": 0,`)
- for _, k := range d.keys {
- fmt.Fprintf(w, "%q: %d,\n", k, d.m[k])
- }
- fmt.Fprintln(w, "}")
- return nil
- }
- func toCamel(s string) string {
- p := strings.Split(s, "-")
- for i, s := range p[1:] {
- p[i+1] = strings.Title(s)
- }
- return strings.Replace(strings.Join(p, ""), "/", "", -1)
- }
- func (b *Builder) stats() string {
- w := &bytes.Buffer{}
- b.rootMeta.validate()
- for _, es := range b.enums {
- fmt.Fprintf(w, "<%s>\n", es.name)
- printEnumValues(w, es, 1, nil)
- }
- fmt.Fprintln(w)
- printEnums(w, b.rootMeta.typeInfo, 0)
- fmt.Fprintln(w)
- fmt.Fprintln(w, "Nr elem: ", len(b.strToBucket))
- fmt.Fprintln(w, "uniqued size: ", b.size)
- fmt.Fprintln(w, "total string size: ", b.sizeAll)
- fmt.Fprintln(w, "bucket waste: ", b.bucketWaste)
- return w.String()
- }
- func printEnums(w io.Writer, s *typeInfo, indent int) {
- idStr := strings.Repeat(" ", indent) + "- "
- e := s.enum
- if e == nil {
- if len(s.entries) > 0 {
- panic(fmt.Errorf("has entries but no enum values: %#v", s.entries))
- }
- return
- }
- if e.name != "" {
- fmt.Fprintf(w, "%s<%s>\n", idStr, e.name)
- } else {
- printEnumValues(w, e, indent, s)
- }
- if s.sharedKeys() {
- for _, v := range s.entries {
- printEnums(w, v, indent+1)
- break
- }
- }
- }
- func printEnumValues(w io.Writer, e *enum, indent int, info *typeInfo) {
- idStr := strings.Repeat(" ", indent) + "- "
- for i := 0; i < len(e.keys); i++ {
- fmt.Fprint(w, idStr)
- k := e.keys[i]
- if u, err := strconv.ParseUint(k, 10, 16); err == nil {
- fmt.Fprintf(w, "%s", k)
- // Skip contiguous integers
- var v, last uint64
- for i++; i < len(e.keys); i++ {
- k = e.keys[i]
- if v, err = strconv.ParseUint(k, 10, 16); err != nil {
- break
- }
- last = v
- }
- if u < last {
- fmt.Fprintf(w, `..%d`, last)
- }
- fmt.Fprintln(w)
- if err != nil {
- fmt.Fprintf(w, "%s%s\n", idStr, k)
- }
- } else if k == "" {
- fmt.Fprintln(w, `""`)
- } else {
- fmt.Fprintf(w, "%s\n", k)
- }
- if info != nil && !info.sharedKeys() {
- if e := info.entries[enumIndex(i)]; e != nil {
- printEnums(w, e, indent+1)
- }
- }
- }
- }
- func getEnumData(b *Builder) (*enumData, error) {
- d := &enumData{m: map[string]int{}}
- if errStr := d.insert(b.rootMeta.typeInfo); errStr != "" {
- // TODO: consider returning the error.
- return nil, fmt.Errorf("cldrtree: %s", errStr)
- }
- return d, nil
- }
- type enumData struct {
- m map[string]int
- keys []string
- enums []string
- }
- func (d *enumData) insert(t *typeInfo) (errStr string) {
- e := t.enum
- if e == nil {
- return ""
- }
- for i, k := range e.keys {
- if _, err := strconv.ParseUint(k, 10, 16); err == nil {
- // We don't include any enum that has integer values.
- break
- }
- if v, ok := d.m[k]; ok {
- if v != i {
- return fmt.Sprintf("%q has value %d and %d", k, i, v)
- }
- } else {
- d.m[k] = i
- if k != "" {
- d.keys = append(d.keys, k)
- d.enums = append(d.enums, e.name)
- }
- }
- }
- for i := range t.enum.keys {
- if e := t.entries[enumIndex(i)]; e != nil {
- if errStr := d.insert(e); errStr != "" {
- return fmt.Sprintf("%q>%v", t.enum.keys[i], errStr)
- }
- }
- }
- return ""
- }
|