github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/text/currency/gen.go (about) 1 // Copyright 2015 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 // Generator for currency-related data. 8 9 package main 10 11 import ( 12 "flag" 13 "fmt" 14 "log" 15 "os" 16 "sort" 17 "strconv" 18 "strings" 19 20 "golang.org/x/text/internal" 21 "golang.org/x/text/internal/gen" 22 "golang.org/x/text/internal/tag" 23 "golang.org/x/text/language" 24 "golang.org/x/text/unicode/cldr" 25 ) 26 27 var ( 28 test = flag.Bool("test", false, 29 "test existing tables; can be used to compare web data with package data.") 30 outputFile = flag.String("output", "tables.go", "output file") 31 32 draft = flag.String("draft", 33 "contributed", 34 `Minimal draft requirements (approved, contributed, provisional, unconfirmed).`) 35 ) 36 37 func main() { 38 gen.Init() 39 40 gen.Repackage("gen_common.go", "common.go", "currency") 41 42 // Read the CLDR zip file. 43 r := gen.OpenCLDRCoreZip() 44 defer r.Close() 45 46 d := &cldr.Decoder{} 47 d.SetDirFilter("supplemental", "main") 48 d.SetSectionFilter("numbers") 49 data, err := d.DecodeZip(r) 50 if err != nil { 51 log.Fatalf("DecodeZip: %v", err) 52 } 53 54 w := gen.NewCodeWriter() 55 defer w.WriteGoFile(*outputFile, "currency") 56 57 fmt.Fprintln(w, `import "golang.org/x/text/internal/tag"`) 58 59 gen.WriteCLDRVersion(w) 60 b := &builder{} 61 b.genCurrencies(w, data.Supplemental()) 62 b.genSymbols(w, data) 63 } 64 65 var constants = []string{ 66 // Undefined and testing. 67 "XXX", "XTS", 68 // G11 currencies https://en.wikipedia.org/wiki/G10_currencies. 69 "USD", "EUR", "JPY", "GBP", "CHF", "AUD", "NZD", "CAD", "SEK", "NOK", "DKK", 70 // Precious metals. 71 "XAG", "XAU", "XPT", "XPD", 72 73 // Additional common currencies as defined by CLDR. 74 "BRL", "CNY", "INR", "RUB", "HKD", "IDR", "KRW", "MXN", "PLN", "SAR", 75 "THB", "TRY", "TWD", "ZAR", 76 } 77 78 type builder struct { 79 currencies tag.Index 80 numCurrencies int 81 } 82 83 func (b *builder) genCurrencies(w *gen.CodeWriter, data *cldr.SupplementalData) { 84 // 3-letter ISO currency codes 85 // Start with dummy to let index start at 1. 86 currencies := []string{"\x00\x00\x00\x00"} 87 88 // currency codes 89 for _, reg := range data.CurrencyData.Region { 90 for _, cur := range reg.Currency { 91 currencies = append(currencies, cur.Iso4217) 92 } 93 } 94 // Not included in the list for some reasons: 95 currencies = append(currencies, "MVP") 96 97 sort.Strings(currencies) 98 // Unique the elements. 99 k := 0 100 for i := 1; i < len(currencies); i++ { 101 if currencies[k] != currencies[i] { 102 currencies[k+1] = currencies[i] 103 k++ 104 } 105 } 106 currencies = currencies[:k+1] 107 108 // Close with dummy for simpler and faster searching. 109 currencies = append(currencies, "\xff\xff\xff\xff") 110 111 // Write currency values. 112 fmt.Fprintln(w, "const (") 113 for _, c := range constants { 114 index := sort.SearchStrings(currencies, c) 115 fmt.Fprintf(w, "\t%s = %d\n", strings.ToLower(c), index) 116 } 117 fmt.Fprint(w, ")") 118 119 // Compute currency-related data that we merge into the table. 120 for _, info := range data.CurrencyData.Fractions[0].Info { 121 if info.Iso4217 == "DEFAULT" { 122 continue 123 } 124 standard := getRoundingIndex(info.Digits, info.Rounding, 0) 125 cash := getRoundingIndex(info.CashDigits, info.CashRounding, standard) 126 127 index := sort.SearchStrings(currencies, info.Iso4217) 128 currencies[index] += mkCurrencyInfo(standard, cash) 129 } 130 131 // Set default values for entries that weren't touched. 132 for i, c := range currencies { 133 if len(c) == 3 { 134 currencies[i] += mkCurrencyInfo(0, 0) 135 } 136 } 137 138 b.currencies = tag.Index(strings.Join(currencies, "")) 139 w.WriteComment(` 140 currency holds an alphabetically sorted list of canonical 3-letter currency 141 identifiers. Each identifier is followed by a byte of type currencyInfo, 142 defined in gen_common.go.`) 143 w.WriteConst("currency", b.currencies) 144 145 // Hack alert: gofmt indents a trailing comment after an indented string. 146 // Ensure that the next thing written is not a comment. 147 b.numCurrencies = (len(b.currencies) / 4) - 2 148 w.WriteConst("numCurrencies", b.numCurrencies) 149 150 // Create a table that maps regions to currencies. 151 regionToCurrency := []toCurrency{} 152 153 for _, reg := range data.CurrencyData.Region { 154 if len(reg.Iso3166) != 2 { 155 log.Fatalf("Unexpected group %q in region data", reg.Iso3166) 156 } 157 if len(reg.Currency) == 0 { 158 continue 159 } 160 cur := reg.Currency[0] 161 if cur.To != "" || cur.Tender == "false" { 162 continue 163 } 164 regionToCurrency = append(regionToCurrency, toCurrency{ 165 region: regionToCode(language.MustParseRegion(reg.Iso3166)), 166 code: uint16(b.currencies.Index([]byte(cur.Iso4217))), 167 }) 168 } 169 sort.Sort(byRegion(regionToCurrency)) 170 171 w.WriteType(toCurrency{}) 172 w.WriteVar("regionToCurrency", regionToCurrency) 173 } 174 175 type toCurrency struct { 176 region uint16 177 code uint16 178 } 179 180 type byRegion []toCurrency 181 182 func (a byRegion) Len() int { return len(a) } 183 func (a byRegion) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 184 func (a byRegion) Less(i, j int) bool { return a[i].region < a[j].region } 185 186 func mkCurrencyInfo(standard, cash int) string { 187 return string([]byte{byte(cash<<cashShift | standard)}) 188 } 189 190 func getRoundingIndex(digits, rounding string, defIndex int) int { 191 round := roundings[defIndex] // default 192 193 if digits != "" { 194 round.scale = parseUint8(digits) 195 } 196 if rounding != "" && rounding != "0" { // 0 means 1 here in CLDR 197 round.increment = parseUint8(rounding) 198 } 199 200 // Will panic if the entry doesn't exist: 201 for i, r := range roundings { 202 if r == round { 203 return i 204 } 205 } 206 log.Fatalf("Rounding entry %#v does not exist.", round) 207 panic("unreachable") 208 } 209 210 // genSymbols generates the symbols used for currencies. Most symbols are 211 // defined in root and there is only very small variation per language. 212 // The following rules apply: 213 // - A symbol can be requested as normal or narrow. 214 // - If a symbol is not defined for a currency, it defaults to its ISO code. 215 func (b *builder) genSymbols(w *gen.CodeWriter, data *cldr.CLDR) { 216 d, err := cldr.ParseDraft(*draft) 217 if err != nil { 218 log.Fatalf("filter: %v", err) 219 } 220 221 const ( 222 normal = iota 223 narrow 224 numTypes 225 ) 226 // language -> currency -> type -> symbol 227 var symbols [language.NumCompactTags][][numTypes]*string 228 229 // Collect symbol information per language. 230 for _, lang := range data.Locales() { 231 ldml := data.RawLDML(lang) 232 if ldml.Numbers == nil || ldml.Numbers.Currencies == nil { 233 continue 234 } 235 236 langIndex, ok := language.CompactIndex(language.MustParse(lang)) 237 if !ok { 238 log.Fatalf("No compact index for language %s", lang) 239 } 240 241 symbols[langIndex] = make([][numTypes]*string, b.numCurrencies+1) 242 243 for _, c := range ldml.Numbers.Currencies.Currency { 244 syms := cldr.MakeSlice(&c.Symbol) 245 syms.SelectDraft(d) 246 247 for _, sym := range c.Symbol { 248 v := sym.Data() 249 if v == c.Type { 250 // We define "" to mean the ISO symbol. 251 v = "" 252 } 253 cur := b.currencies.Index([]byte(c.Type)) 254 // XXX gets reassigned to 0 in the package's code. 255 if c.Type == "XXX" { 256 cur = 0 257 } 258 if cur == -1 { 259 fmt.Println("Unsupported:", c.Type) 260 continue 261 } 262 263 switch sym.Alt { 264 case "": 265 symbols[langIndex][cur][normal] = &v 266 case "narrow": 267 symbols[langIndex][cur][narrow] = &v 268 } 269 } 270 } 271 } 272 273 // Remove values identical to the parent. 274 for langIndex, data := range symbols { 275 for curIndex, curs := range data { 276 for typ, sym := range curs { 277 if sym == nil { 278 continue 279 } 280 for p := uint16(langIndex); p != 0; { 281 p = internal.Parent[p] 282 x := symbols[p] 283 if x == nil { 284 continue 285 } 286 if v := x[curIndex][typ]; v != nil || p == 0 { 287 // Value is equal to the default value root value is undefined. 288 parentSym := "" 289 if v != nil { 290 parentSym = *v 291 } 292 if parentSym == *sym { 293 // Value is the same as parent. 294 data[curIndex][typ] = nil 295 } 296 break 297 } 298 } 299 } 300 } 301 } 302 303 // Create symbol index. 304 symbolData := []byte{0} 305 symbolLookup := map[string]uint16{"": 0} // 0 means default, so block that value. 306 for _, data := range symbols { 307 for _, curs := range data { 308 for _, sym := range curs { 309 if sym == nil { 310 continue 311 } 312 if _, ok := symbolLookup[*sym]; !ok { 313 symbolLookup[*sym] = uint16(len(symbolData)) 314 symbolData = append(symbolData, byte(len(*sym))) 315 symbolData = append(symbolData, *sym...) 316 } 317 } 318 } 319 } 320 w.WriteComment(` 321 symbols holds symbol data of the form <n> <str>, where n is the length of 322 the symbol string str.`) 323 w.WriteConst("symbols", string(symbolData)) 324 325 // Create index from language to currency lookup to symbol. 326 type curToIndex struct{ cur, idx uint16 } 327 w.WriteType(curToIndex{}) 328 329 prefix := []string{"normal", "narrow"} 330 // Create data for regular and narrow symbol data. 331 for typ := normal; typ <= narrow; typ++ { 332 333 indexes := []curToIndex{} // maps currency to symbol index 334 languages := []uint16{} 335 336 for _, data := range symbols { 337 languages = append(languages, uint16(len(indexes))) 338 for curIndex, curs := range data { 339 340 if sym := curs[typ]; sym != nil { 341 indexes = append(indexes, curToIndex{uint16(curIndex), symbolLookup[*sym]}) 342 } 343 } 344 } 345 languages = append(languages, uint16(len(indexes))) 346 347 w.WriteVar(prefix[typ]+"LangIndex", languages) 348 w.WriteVar(prefix[typ]+"SymIndex", indexes) 349 } 350 } 351 func parseUint8(str string) uint8 { 352 x, err := strconv.ParseUint(str, 10, 8) 353 if err != nil { 354 // Show line number of where this function was called. 355 log.New(os.Stderr, "", log.Lshortfile).Output(2, err.Error()) 356 os.Exit(1) 357 } 358 return uint8(x) 359 }