github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/text/internal/number/gen.go (about) 1 // Copyright 2016 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 5 // +build ignore 6 7 package main 8 9 import ( 10 "flag" 11 "fmt" 12 "log" 13 "reflect" 14 "strings" 15 "unicode/utf8" 16 17 "github.com/insionng/yougam/libraries/x/text/internal" 18 "github.com/insionng/yougam/libraries/x/text/internal/gen" 19 "github.com/insionng/yougam/libraries/x/text/internal/stringset" 20 "github.com/insionng/yougam/libraries/x/text/language" 21 "github.com/insionng/yougam/libraries/x/text/unicode/cldr" 22 ) 23 24 var ( 25 test = flag.Bool("test", false, 26 "test existing tables; can be used to compare web data with package data.") 27 outputFile = flag.String("output", "tables.go", "output file") 28 outputTestFile = flag.String("testoutput", "data_test.go", "output file") 29 30 draft = flag.String("draft", 31 "contributed", 32 `Minimal draft requirements (approved, contributed, provisional, unconfirmed).`) 33 ) 34 35 func main() { 36 gen.Init() 37 38 const pkg = "number" 39 40 gen.Repackage("gen_common.go", "common.go", pkg) 41 // Read the CLDR zip file. 42 r := gen.OpenCLDRCoreZip() 43 defer r.Close() 44 45 d := &cldr.Decoder{} 46 d.SetDirFilter("supplemental", "main") 47 d.SetSectionFilter("numbers", "numberingSystem", "plurals") 48 data, err := d.DecodeZip(r) 49 if err != nil { 50 log.Fatalf("DecodeZip: %v", err) 51 } 52 53 w := gen.NewCodeWriter() 54 defer w.WriteGoFile(*outputFile, pkg) 55 56 fmt.Fprintln(w, `import "github.com/insionng/yougam/libraries/x/text/internal/stringset"`) 57 58 gen.WriteCLDRVersion(w) 59 60 genNumSystem(w, data) 61 genSymbols(w, data) 62 genPlurals(w, data) 63 64 w = gen.NewCodeWriter() 65 defer w.WriteGoFile(*outputTestFile, pkg) 66 67 fmt.Fprintln(w, `import "github.com/insionng/yougam/libraries/x/text/internal/format/plural"`) 68 69 genPluralsTests(w, data) 70 } 71 72 var systemMap = map[string]system{"latn": 0} 73 74 func getNumberSystem(str string) system { 75 ns, ok := systemMap[str] 76 if !ok { 77 log.Fatalf("No index for numbering system %q", str) 78 } 79 return ns 80 } 81 82 func genNumSystem(w *gen.CodeWriter, data *cldr.CLDR) { 83 numSysData := []systemData{ 84 {digitSize: 1, zero: [4]byte{'0'}}, 85 } 86 87 for _, ns := range data.Supplemental().NumberingSystems.NumberingSystem { 88 if len(ns.Digits) == 0 { 89 continue 90 } 91 switch ns.Id { 92 case "latn": 93 // hard-wired 94 continue 95 case "hanidec": 96 // non-consecutive digits: treat as "algorithmic" 97 continue 98 } 99 100 zero, sz := utf8.DecodeRuneInString(ns.Digits) 101 if ns.Digits[sz-1]+9 > 0xBF { // 1011 1111: highest continuation byte 102 log.Fatalf("Last byte of zero value overflows for %s", ns.Id) 103 } 104 105 i := rune(0) 106 for _, r := range ns.Digits { 107 // Verify that we can do simple math on the UTF-8 byte sequence 108 // of zero to get the digit. 109 if zero+i != r { 110 // Runes not consecutive. 111 log.Fatalf("Digit %d of %s (%U) is not offset correctly from zero value", i, ns.Id, r) 112 } 113 i++ 114 } 115 var x [utf8.UTFMax]byte 116 utf8.EncodeRune(x[:], zero) 117 id := system(len(numSysData)) 118 systemMap[ns.Id] = id 119 numSysData = append(numSysData, systemData{ 120 id: id, 121 digitSize: byte(sz), 122 zero: x, 123 }) 124 } 125 w.WriteVar("numSysData", numSysData) 126 127 algoID := system(len(numSysData)) 128 fmt.Fprintln(w, "const (") 129 for _, ns := range data.Supplemental().NumberingSystems.NumberingSystem { 130 id, ok := systemMap[ns.Id] 131 if !ok { 132 id = algoID 133 systemMap[ns.Id] = id 134 algoID++ 135 } 136 fmt.Fprintf(w, "num%s = %#x\n", strings.Title(ns.Id), id) 137 } 138 fmt.Fprintln(w, "numNumberSystems") 139 fmt.Fprintln(w, ")") 140 141 fmt.Fprintln(w, "var systemMap = map[string]system{") 142 for _, ns := range data.Supplemental().NumberingSystems.NumberingSystem { 143 fmt.Fprintf(w, "%q: num%s,\n", ns.Id, strings.Title(ns.Id)) 144 w.Size += len(ns.Id) + 16 + 1 // very coarse approximation 145 } 146 fmt.Fprintln(w, "}") 147 } 148 149 func genSymbols(w *gen.CodeWriter, data *cldr.CLDR) { 150 d, err := cldr.ParseDraft(*draft) 151 if err != nil { 152 log.Fatalf("invalid draft level: %v", err) 153 } 154 155 nNumberSystems := system(len(systemMap)) 156 157 type symbols [NumSymbolTypes]string 158 159 type key struct { 160 tag int // from language.CompactIndex 161 system system 162 } 163 symbolMap := map[key]*symbols{} 164 165 defaults := map[int]system{} 166 167 for _, lang := range data.Locales() { 168 ldml := data.RawLDML(lang) 169 if ldml.Numbers == nil { 170 continue 171 } 172 langIndex, ok := language.CompactIndex(language.MustParse(lang)) 173 if !ok { 174 log.Fatalf("No compact index for language %s", lang) 175 } 176 if d := ldml.Numbers.DefaultNumberingSystem; len(d) > 0 { 177 defaults[langIndex] = getNumberSystem(d[0].Data()) 178 } 179 180 syms := cldr.MakeSlice(&ldml.Numbers.Symbols) 181 syms.SelectDraft(d) 182 183 for _, sym := range ldml.Numbers.Symbols { 184 if sym.NumberSystem == "" { 185 // This is just linking the default of root to "latn". 186 continue 187 } 188 symbolMap[key{langIndex, getNumberSystem(sym.NumberSystem)}] = &symbols{ 189 SymDecimal: getFirst("decimal", sym.Decimal), 190 SymGroup: getFirst("group", sym.Group), 191 SymList: getFirst("list", sym.List), 192 SymPercentSign: getFirst("percentSign", sym.PercentSign), 193 SymPlusSign: getFirst("plusSign", sym.PlusSign), 194 SymMinusSign: getFirst("minusSign", sym.MinusSign), 195 SymExponential: getFirst("exponential", sym.Exponential), 196 SymSuperscriptingExponent: getFirst("superscriptingExponent", sym.SuperscriptingExponent), 197 SymPerMille: getFirst("perMille", sym.PerMille), 198 SymInfinity: getFirst("infinity", sym.Infinity), 199 SymNan: getFirst("nan", sym.Nan), 200 SymTimeSeparator: getFirst("timeSeparator", sym.TimeSeparator), 201 } 202 } 203 } 204 205 // Expand all values. 206 for k, syms := range symbolMap { 207 for t := SymDecimal; t < NumSymbolTypes; t++ { 208 p := k.tag 209 for syms[t] == "" { 210 p = int(internal.Parent[p]) 211 if pSyms, ok := symbolMap[key{p, k.system}]; ok && (*pSyms)[t] != "" { 212 syms[t] = (*pSyms)[t] 213 break 214 } 215 if p == 0 /* und */ { 216 // Default to root, latn. 217 syms[t] = (*symbolMap[key{}])[t] 218 } 219 } 220 } 221 } 222 223 // Unique the symbol sets and write the string data. 224 m := map[symbols]int{} 225 sb := stringset.NewBuilder() 226 227 symIndex := [][NumSymbolTypes]byte{} 228 229 for ns := system(0); ns < nNumberSystems; ns++ { 230 for _, l := range data.Locales() { 231 langIndex, _ := language.CompactIndex(language.MustParse(l)) 232 s := symbolMap[key{langIndex, ns}] 233 if s == nil { 234 continue 235 } 236 if _, ok := m[*s]; !ok { 237 m[*s] = len(symIndex) 238 sb.Add(s[:]...) 239 var x [NumSymbolTypes]byte 240 for i := SymDecimal; i < NumSymbolTypes; i++ { 241 x[i] = byte(sb.Index((*s)[i])) 242 } 243 symIndex = append(symIndex, x) 244 } 245 } 246 } 247 w.WriteVar("symIndex", symIndex) 248 w.WriteVar("symData", sb.Set()) 249 250 // resolveSymbolIndex gets the index from the closest matching locale, 251 // including the locale itself. 252 resolveSymbolIndex := func(langIndex int, ns system) byte { 253 for { 254 if sym := symbolMap[key{langIndex, ns}]; sym != nil { 255 return byte(m[*sym]) 256 } 257 if langIndex == 0 { 258 return 0 // und, latn 259 } 260 langIndex = int(internal.Parent[langIndex]) 261 } 262 } 263 264 // Create an index with the symbols for each locale for the latn numbering 265 // system. If this is not the default, or the only one, for a locale, we 266 // will overwrite the value later. 267 var langToDefaults [language.NumCompactTags]byte 268 for _, l := range data.Locales() { 269 langIndex, _ := language.CompactIndex(language.MustParse(l)) 270 langToDefaults[langIndex] = resolveSymbolIndex(langIndex, 0) 271 } 272 273 // Delete redundant entries. 274 for _, l := range data.Locales() { 275 langIndex, _ := language.CompactIndex(language.MustParse(l)) 276 def := defaults[langIndex] 277 syms := symbolMap[key{langIndex, def}] 278 if syms == nil { 279 continue 280 } 281 for ns := system(0); ns < nNumberSystems; ns++ { 282 if ns == def { 283 continue 284 } 285 if altSyms, ok := symbolMap[key{langIndex, ns}]; ok && *altSyms == *syms { 286 delete(symbolMap, key{langIndex, ns}) 287 } 288 } 289 } 290 291 // Create a sorted list of alternatives per language. This will only need to 292 // be referenced if a user specified an alternative numbering system. 293 var langToAlt []altSymData 294 for _, l := range data.Locales() { 295 langIndex, _ := language.CompactIndex(language.MustParse(l)) 296 start := len(langToAlt) 297 if start > 0x7F { 298 log.Fatal("Number of alternative assignments > 0x7F") 299 } 300 // Create the entry for the default value. 301 def := defaults[langIndex] 302 langToAlt = append(langToAlt, altSymData{ 303 compactTag: uint16(langIndex), 304 system: def, 305 symIndex: resolveSymbolIndex(langIndex, def), 306 }) 307 308 for ns := system(0); ns < nNumberSystems; ns++ { 309 if def == ns { 310 continue 311 } 312 if sym := symbolMap[key{langIndex, ns}]; sym != nil { 313 langToAlt = append(langToAlt, altSymData{ 314 compactTag: uint16(langIndex), 315 system: ns, 316 symIndex: resolveSymbolIndex(langIndex, ns), 317 }) 318 } 319 } 320 if def == 0 && len(langToAlt) == start+1 { 321 // No additional data: erase the entry. 322 langToAlt = langToAlt[:start] 323 } else { 324 // Overwrite the entry in langToDefaults. 325 langToDefaults[langIndex] = 0x80 | byte(start) 326 } 327 } 328 w.WriteComment(` 329 langToDefaults maps a compact language index to the default numbering system 330 and default symbol set`) 331 w.WriteVar("langToDefaults", langToDefaults) 332 333 w.WriteComment(` 334 langToAlt is a list of numbering system and symbol set pairs, sorted and 335 marked by compact language index.`) 336 w.WriteVar("langToAlt", langToAlt) 337 } 338 339 func getFirst(name string, x interface{}) string { 340 v := reflect.ValueOf(x) 341 if v.Len() == 0 { 342 return "" 343 } else if v.Len() > 1 { 344 log.Fatalf("Multiple values of %q within single symbol not supported.", name) 345 } 346 return v.Index(0).MethodByName("Data").Call(nil)[0].String() 347 }