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  }