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  }