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