// 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 "" }