OSDN Git Service

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