Switch to using the dep tool and update all the dependencies
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
package number
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
// A system identifies a CLDR numbering system.
|
||||
type system byte
|
||||
|
||||
type systemData struct {
|
||||
id system
|
||||
digitSize byte // number of UTF-8 bytes per digit
|
||||
zero [utf8.UTFMax]byte // UTF-8 sequence of zero digit.
|
||||
}
|
||||
|
||||
// A SymbolType identifies a symbol of a specific kind.
|
||||
type SymbolType int
|
||||
|
||||
const (
|
||||
SymDecimal SymbolType = iota
|
||||
SymGroup
|
||||
SymList
|
||||
SymPercentSign
|
||||
SymPlusSign
|
||||
SymMinusSign
|
||||
SymExponential
|
||||
SymSuperscriptingExponent
|
||||
SymPerMille
|
||||
SymInfinity
|
||||
SymNan
|
||||
SymTimeSeparator
|
||||
|
||||
NumSymbolTypes
|
||||
)
|
||||
|
||||
type altSymData struct {
|
||||
compactTag uint16
|
||||
system system
|
||||
symIndex byte
|
||||
}
|
||||
+458
@@ -0,0 +1,458 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/internal"
|
||||
"golang.org/x/text/internal/gen"
|
||||
"golang.org/x/text/internal/number"
|
||||
"golang.org/x/text/internal/stringset"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/unicode/cldr"
|
||||
)
|
||||
|
||||
var (
|
||||
test = flag.Bool("test", false,
|
||||
"test existing tables; can be used to compare web data with package data.")
|
||||
outputFile = flag.String("output", "tables.go", "output file")
|
||||
outputTestFile = flag.String("testoutput", "data_test.go", "output file")
|
||||
|
||||
draft = flag.String("draft",
|
||||
"contributed",
|
||||
`Minimal draft requirements (approved, contributed, provisional, unconfirmed).`)
|
||||
)
|
||||
|
||||
func main() {
|
||||
gen.Init()
|
||||
|
||||
const pkg = "number"
|
||||
|
||||
gen.Repackage("gen_common.go", "common.go", pkg)
|
||||
// Read the CLDR zip file.
|
||||
r := gen.OpenCLDRCoreZip()
|
||||
defer r.Close()
|
||||
|
||||
d := &cldr.Decoder{}
|
||||
d.SetDirFilter("supplemental", "main")
|
||||
d.SetSectionFilter("numbers", "numberingSystem")
|
||||
data, err := d.DecodeZip(r)
|
||||
if err != nil {
|
||||
log.Fatalf("DecodeZip: %v", err)
|
||||
}
|
||||
|
||||
w := gen.NewCodeWriter()
|
||||
defer w.WriteGoFile(*outputFile, pkg)
|
||||
|
||||
fmt.Fprintln(w, `import "golang.org/x/text/internal/stringset"`)
|
||||
|
||||
gen.WriteCLDRVersion(w)
|
||||
|
||||
genNumSystem(w, data)
|
||||
genSymbols(w, data)
|
||||
genFormats(w, data)
|
||||
}
|
||||
|
||||
var systemMap = map[string]system{"latn": 0}
|
||||
|
||||
func getNumberSystem(str string) system {
|
||||
ns, ok := systemMap[str]
|
||||
if !ok {
|
||||
log.Fatalf("No index for numbering system %q", str)
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
func genNumSystem(w *gen.CodeWriter, data *cldr.CLDR) {
|
||||
numSysData := []systemData{
|
||||
{digitSize: 1, zero: [4]byte{'0'}},
|
||||
}
|
||||
|
||||
for _, ns := range data.Supplemental().NumberingSystems.NumberingSystem {
|
||||
if len(ns.Digits) == 0 {
|
||||
continue
|
||||
}
|
||||
switch ns.Id {
|
||||
case "latn":
|
||||
// hard-wired
|
||||
continue
|
||||
case "hanidec":
|
||||
// non-consecutive digits: treat as "algorithmic"
|
||||
continue
|
||||
}
|
||||
|
||||
zero, sz := utf8.DecodeRuneInString(ns.Digits)
|
||||
if ns.Digits[sz-1]+9 > 0xBF { // 1011 1111: highest continuation byte
|
||||
log.Fatalf("Last byte of zero value overflows for %s", ns.Id)
|
||||
}
|
||||
|
||||
i := rune(0)
|
||||
for _, r := range ns.Digits {
|
||||
// Verify that we can do simple math on the UTF-8 byte sequence
|
||||
// of zero to get the digit.
|
||||
if zero+i != r {
|
||||
// Runes not consecutive.
|
||||
log.Fatalf("Digit %d of %s (%U) is not offset correctly from zero value", i, ns.Id, r)
|
||||
}
|
||||
i++
|
||||
}
|
||||
var x [utf8.UTFMax]byte
|
||||
utf8.EncodeRune(x[:], zero)
|
||||
id := system(len(numSysData))
|
||||
systemMap[ns.Id] = id
|
||||
numSysData = append(numSysData, systemData{
|
||||
id: id,
|
||||
digitSize: byte(sz),
|
||||
zero: x,
|
||||
})
|
||||
}
|
||||
w.WriteVar("numSysData", numSysData)
|
||||
|
||||
algoID := system(len(numSysData))
|
||||
fmt.Fprintln(w, "const (")
|
||||
for _, ns := range data.Supplemental().NumberingSystems.NumberingSystem {
|
||||
id, ok := systemMap[ns.Id]
|
||||
if !ok {
|
||||
id = algoID
|
||||
systemMap[ns.Id] = id
|
||||
algoID++
|
||||
}
|
||||
fmt.Fprintf(w, "num%s = %#x\n", strings.Title(ns.Id), id)
|
||||
}
|
||||
fmt.Fprintln(w, "numNumberSystems")
|
||||
fmt.Fprintln(w, ")")
|
||||
|
||||
fmt.Fprintln(w, "var systemMap = map[string]system{")
|
||||
for _, ns := range data.Supplemental().NumberingSystems.NumberingSystem {
|
||||
fmt.Fprintf(w, "%q: num%s,\n", ns.Id, strings.Title(ns.Id))
|
||||
w.Size += len(ns.Id) + 16 + 1 // very coarse approximation
|
||||
}
|
||||
fmt.Fprintln(w, "}")
|
||||
}
|
||||
|
||||
func genSymbols(w *gen.CodeWriter, data *cldr.CLDR) {
|
||||
d, err := cldr.ParseDraft(*draft)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid draft level: %v", err)
|
||||
}
|
||||
|
||||
nNumberSystems := system(len(systemMap))
|
||||
|
||||
type symbols [NumSymbolTypes]string
|
||||
|
||||
type key struct {
|
||||
tag int // from language.CompactIndex
|
||||
system system
|
||||
}
|
||||
symbolMap := map[key]*symbols{}
|
||||
|
||||
defaults := map[int]system{}
|
||||
|
||||
for _, lang := range data.Locales() {
|
||||
ldml := data.RawLDML(lang)
|
||||
if ldml.Numbers == nil {
|
||||
continue
|
||||
}
|
||||
langIndex, ok := language.CompactIndex(language.MustParse(lang))
|
||||
if !ok {
|
||||
log.Fatalf("No compact index for language %s", lang)
|
||||
}
|
||||
if d := ldml.Numbers.DefaultNumberingSystem; len(d) > 0 {
|
||||
defaults[langIndex] = getNumberSystem(d[0].Data())
|
||||
}
|
||||
|
||||
syms := cldr.MakeSlice(&ldml.Numbers.Symbols)
|
||||
syms.SelectDraft(d)
|
||||
|
||||
getFirst := func(name string, x interface{}) string {
|
||||
v := reflect.ValueOf(x)
|
||||
slice := cldr.MakeSlice(x)
|
||||
slice.SelectAnyOf("alt", "", "alt")
|
||||
if reflect.Indirect(v).Len() == 0 {
|
||||
return ""
|
||||
} else if reflect.Indirect(v).Len() > 1 {
|
||||
log.Fatalf("%s: multiple values of %q within single symbol not supported.", lang, name)
|
||||
}
|
||||
return reflect.Indirect(v).Index(0).MethodByName("Data").Call(nil)[0].String()
|
||||
}
|
||||
|
||||
for _, sym := range ldml.Numbers.Symbols {
|
||||
if sym.NumberSystem == "" {
|
||||
// This is just linking the default of root to "latn".
|
||||
continue
|
||||
}
|
||||
symbolMap[key{langIndex, getNumberSystem(sym.NumberSystem)}] = &symbols{
|
||||
SymDecimal: getFirst("decimal", &sym.Decimal),
|
||||
SymGroup: getFirst("group", &sym.Group),
|
||||
SymList: getFirst("list", &sym.List),
|
||||
SymPercentSign: getFirst("percentSign", &sym.PercentSign),
|
||||
SymPlusSign: getFirst("plusSign", &sym.PlusSign),
|
||||
SymMinusSign: getFirst("minusSign", &sym.MinusSign),
|
||||
SymExponential: getFirst("exponential", &sym.Exponential),
|
||||
SymSuperscriptingExponent: getFirst("superscriptingExponent", &sym.SuperscriptingExponent),
|
||||
SymPerMille: getFirst("perMille", &sym.PerMille),
|
||||
SymInfinity: getFirst("infinity", &sym.Infinity),
|
||||
SymNan: getFirst("nan", &sym.Nan),
|
||||
SymTimeSeparator: getFirst("timeSeparator", &sym.TimeSeparator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expand all values.
|
||||
for k, syms := range symbolMap {
|
||||
for t := SymDecimal; t < NumSymbolTypes; t++ {
|
||||
p := k.tag
|
||||
for syms[t] == "" {
|
||||
p = int(internal.Parent[p])
|
||||
if pSyms, ok := symbolMap[key{p, k.system}]; ok && (*pSyms)[t] != "" {
|
||||
syms[t] = (*pSyms)[t]
|
||||
break
|
||||
}
|
||||
if p == 0 /* und */ {
|
||||
// Default to root, latn.
|
||||
syms[t] = (*symbolMap[key{}])[t]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unique the symbol sets and write the string data.
|
||||
m := map[symbols]int{}
|
||||
sb := stringset.NewBuilder()
|
||||
|
||||
symIndex := [][NumSymbolTypes]byte{}
|
||||
|
||||
for ns := system(0); ns < nNumberSystems; ns++ {
|
||||
for _, l := range data.Locales() {
|
||||
langIndex, _ := language.CompactIndex(language.MustParse(l))
|
||||
s := symbolMap[key{langIndex, ns}]
|
||||
if s == nil {
|
||||
continue
|
||||
}
|
||||
if _, ok := m[*s]; !ok {
|
||||
m[*s] = len(symIndex)
|
||||
sb.Add(s[:]...)
|
||||
var x [NumSymbolTypes]byte
|
||||
for i := SymDecimal; i < NumSymbolTypes; i++ {
|
||||
x[i] = byte(sb.Index((*s)[i]))
|
||||
}
|
||||
symIndex = append(symIndex, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.WriteVar("symIndex", symIndex)
|
||||
w.WriteVar("symData", sb.Set())
|
||||
|
||||
// resolveSymbolIndex gets the index from the closest matching locale,
|
||||
// including the locale itself.
|
||||
resolveSymbolIndex := func(langIndex int, ns system) byte {
|
||||
for {
|
||||
if sym := symbolMap[key{langIndex, ns}]; sym != nil {
|
||||
return byte(m[*sym])
|
||||
}
|
||||
if langIndex == 0 {
|
||||
return 0 // und, latn
|
||||
}
|
||||
langIndex = int(internal.Parent[langIndex])
|
||||
}
|
||||
}
|
||||
|
||||
// Create an index with the symbols for each locale for the latn numbering
|
||||
// system. If this is not the default, or the only one, for a locale, we
|
||||
// will overwrite the value later.
|
||||
var langToDefaults [language.NumCompactTags]byte
|
||||
for _, l := range data.Locales() {
|
||||
langIndex, _ := language.CompactIndex(language.MustParse(l))
|
||||
langToDefaults[langIndex] = resolveSymbolIndex(langIndex, 0)
|
||||
}
|
||||
|
||||
// Delete redundant entries.
|
||||
for _, l := range data.Locales() {
|
||||
langIndex, _ := language.CompactIndex(language.MustParse(l))
|
||||
def := defaults[langIndex]
|
||||
syms := symbolMap[key{langIndex, def}]
|
||||
if syms == nil {
|
||||
continue
|
||||
}
|
||||
for ns := system(0); ns < nNumberSystems; ns++ {
|
||||
if ns == def {
|
||||
continue
|
||||
}
|
||||
if altSyms, ok := symbolMap[key{langIndex, ns}]; ok && *altSyms == *syms {
|
||||
delete(symbolMap, key{langIndex, ns})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a sorted list of alternatives per language. This will only need to
|
||||
// be referenced if a user specified an alternative numbering system.
|
||||
var langToAlt []altSymData
|
||||
for _, l := range data.Locales() {
|
||||
langIndex, _ := language.CompactIndex(language.MustParse(l))
|
||||
start := len(langToAlt)
|
||||
if start > 0x7F {
|
||||
log.Fatal("Number of alternative assignments > 0x7F")
|
||||
}
|
||||
// Create the entry for the default value.
|
||||
def := defaults[langIndex]
|
||||
langToAlt = append(langToAlt, altSymData{
|
||||
compactTag: uint16(langIndex),
|
||||
system: def,
|
||||
symIndex: resolveSymbolIndex(langIndex, def),
|
||||
})
|
||||
|
||||
for ns := system(0); ns < nNumberSystems; ns++ {
|
||||
if def == ns {
|
||||
continue
|
||||
}
|
||||
if sym := symbolMap[key{langIndex, ns}]; sym != nil {
|
||||
langToAlt = append(langToAlt, altSymData{
|
||||
compactTag: uint16(langIndex),
|
||||
system: ns,
|
||||
symIndex: resolveSymbolIndex(langIndex, ns),
|
||||
})
|
||||
}
|
||||
}
|
||||
if def == 0 && len(langToAlt) == start+1 {
|
||||
// No additional data: erase the entry.
|
||||
langToAlt = langToAlt[:start]
|
||||
} else {
|
||||
// Overwrite the entry in langToDefaults.
|
||||
langToDefaults[langIndex] = 0x80 | byte(start)
|
||||
}
|
||||
}
|
||||
w.WriteComment(`
|
||||
langToDefaults maps a compact language index to the default numbering system
|
||||
and default symbol set`)
|
||||
w.WriteVar("langToDefaults", langToDefaults)
|
||||
|
||||
w.WriteComment(`
|
||||
langToAlt is a list of numbering system and symbol set pairs, sorted and
|
||||
marked by compact language index.`)
|
||||
w.WriteVar("langToAlt", langToAlt)
|
||||
}
|
||||
|
||||
// genFormats generates the lookup table for decimal, scientific and percent
|
||||
// patterns.
|
||||
//
|
||||
// CLDR allows for patterns to be different per language for different numbering
|
||||
// systems. In practice the patterns are set to be consistent for a language
|
||||
// independent of the numbering system. genFormats verifies that no language
|
||||
// deviates from this.
|
||||
func genFormats(w *gen.CodeWriter, data *cldr.CLDR) {
|
||||
d, err := cldr.ParseDraft(*draft)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid draft level: %v", err)
|
||||
}
|
||||
|
||||
// Fill the first slot with a dummy so we can identify unspecified tags.
|
||||
formats := []number.Pattern{{}}
|
||||
patterns := map[string]int{}
|
||||
|
||||
// TODO: It would be possible to eliminate two of these slices by having
|
||||
// another indirection and store a reference to the combination of patterns.
|
||||
decimal := make([]byte, language.NumCompactTags)
|
||||
scientific := make([]byte, language.NumCompactTags)
|
||||
percent := make([]byte, language.NumCompactTags)
|
||||
|
||||
for _, lang := range data.Locales() {
|
||||
ldml := data.RawLDML(lang)
|
||||
if ldml.Numbers == nil {
|
||||
continue
|
||||
}
|
||||
langIndex, ok := language.CompactIndex(language.MustParse(lang))
|
||||
if !ok {
|
||||
log.Fatalf("No compact index for language %s", lang)
|
||||
}
|
||||
type patternSlice []*struct {
|
||||
cldr.Common
|
||||
Numbers string `xml:"numbers,attr"`
|
||||
Count string `xml:"count,attr"`
|
||||
}
|
||||
|
||||
add := func(name string, tags []byte, ps patternSlice) {
|
||||
sl := cldr.MakeSlice(&ps)
|
||||
sl.SelectDraft(d)
|
||||
if len(ps) == 0 {
|
||||
return
|
||||
}
|
||||
if len(ps) > 2 || len(ps) == 2 && ps[0] != ps[1] {
|
||||
log.Fatalf("Inconsistent %d patterns for language %s", name, lang)
|
||||
}
|
||||
s := ps[0].Data()
|
||||
|
||||
index, ok := patterns[s]
|
||||
if !ok {
|
||||
nf, err := number.ParsePattern(s)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
index = len(formats)
|
||||
patterns[s] = index
|
||||
formats = append(formats, *nf)
|
||||
}
|
||||
tags[langIndex] = byte(index)
|
||||
}
|
||||
|
||||
for _, df := range ldml.Numbers.DecimalFormats {
|
||||
for _, l := range df.DecimalFormatLength {
|
||||
if l.Type != "" {
|
||||
continue
|
||||
}
|
||||
for _, f := range l.DecimalFormat {
|
||||
add("decimal", decimal, f.Pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, df := range ldml.Numbers.ScientificFormats {
|
||||
for _, l := range df.ScientificFormatLength {
|
||||
if l.Type != "" {
|
||||
continue
|
||||
}
|
||||
for _, f := range l.ScientificFormat {
|
||||
add("scientific", scientific, f.Pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, df := range ldml.Numbers.PercentFormats {
|
||||
for _, l := range df.PercentFormatLength {
|
||||
if l.Type != "" {
|
||||
continue
|
||||
}
|
||||
for _, f := range l.PercentFormat {
|
||||
add("percent", percent, f.Pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Complete the parent tag array to reflect inheritance. An index of 0
|
||||
// indicates an unspecified value.
|
||||
for _, data := range [][]byte{decimal, scientific, percent} {
|
||||
for i := range data {
|
||||
p := uint16(i)
|
||||
for ; data[p] == 0; p = internal.Parent[p] {
|
||||
}
|
||||
data[i] = data[p]
|
||||
}
|
||||
}
|
||||
w.WriteVar("tagToDecimal", decimal)
|
||||
w.WriteVar("tagToScientific", scientific)
|
||||
w.WriteVar("tagToPercent", percent)
|
||||
|
||||
value := strings.Replace(fmt.Sprintf("%#v", formats), "number.", "", -1)
|
||||
// Break up the lines. This won't give ideal perfect formatting, but it is
|
||||
// better than one huge line.
|
||||
value = strings.Replace(value, ", ", ",\n", -1)
|
||||
fmt.Fprintf(w, "var formats = %s\n", value)
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
// A system identifies a CLDR numbering system.
|
||||
type system byte
|
||||
|
||||
type systemData struct {
|
||||
id system
|
||||
digitSize byte // number of UTF-8 bytes per digit
|
||||
zero [utf8.UTFMax]byte // UTF-8 sequence of zero digit.
|
||||
}
|
||||
|
||||
// A SymbolType identifies a symbol of a specific kind.
|
||||
type SymbolType int
|
||||
|
||||
const (
|
||||
SymDecimal SymbolType = iota
|
||||
SymGroup
|
||||
SymList
|
||||
SymPercentSign
|
||||
SymPlusSign
|
||||
SymMinusSign
|
||||
SymExponential
|
||||
SymSuperscriptingExponent
|
||||
SymPerMille
|
||||
SymInfinity
|
||||
SymNan
|
||||
SymTimeSeparator
|
||||
|
||||
NumSymbolTypes
|
||||
)
|
||||
|
||||
type altSymData struct {
|
||||
compactTag uint16
|
||||
system system
|
||||
symIndex byte
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run gen.go gen_common.go
|
||||
|
||||
// Package number contains tools and data for formatting numbers.
|
||||
package number
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/internal"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Info holds number formatting configuration data.
|
||||
type Info struct {
|
||||
system systemData // numbering system information
|
||||
symIndex byte // index to symbols
|
||||
}
|
||||
|
||||
// InfoFromLangID returns a Info for the given compact language identifier and
|
||||
// numbering system identifier. If system is the empty string, the default
|
||||
// numbering system will be taken for that language.
|
||||
func InfoFromLangID(compactIndex int, numberSystem string) Info {
|
||||
p := langToDefaults[compactIndex]
|
||||
// Lookup the entry for the language.
|
||||
pSymIndex := byte(0) // Default: Latin, default symbols
|
||||
system, ok := systemMap[numberSystem]
|
||||
if !ok {
|
||||
// Take the value for the default numbering system. This is by far the
|
||||
// most common case as an alternative numbering system is hardly used.
|
||||
if p&0x80 == 0 {
|
||||
pSymIndex = p
|
||||
} else {
|
||||
// Take the first entry from the alternatives list.
|
||||
data := langToAlt[p&^0x80]
|
||||
pSymIndex = data.symIndex
|
||||
system = data.system
|
||||
}
|
||||
} else {
|
||||
langIndex := compactIndex
|
||||
ns := system
|
||||
outerLoop:
|
||||
for {
|
||||
if p&0x80 == 0 {
|
||||
if ns == 0 {
|
||||
// The index directly points to the symbol data.
|
||||
pSymIndex = p
|
||||
break
|
||||
}
|
||||
// Move to the parent and retry.
|
||||
langIndex = int(internal.Parent[langIndex])
|
||||
}
|
||||
// The index points to a list of symbol data indexes.
|
||||
for _, e := range langToAlt[p&^0x80:] {
|
||||
if int(e.compactTag) != langIndex {
|
||||
if langIndex == 0 {
|
||||
// The CLDR root defines full symbol information for all
|
||||
// numbering systems (even though mostly by means of
|
||||
// aliases). This means that we will never fall back to
|
||||
// the default of the language. Also, the loop is
|
||||
// guaranteed to terminate as a consequence.
|
||||
ns = numLatn
|
||||
// Fall back to Latin and start from the original
|
||||
// language. See
|
||||
// http://unicode.org/reports/tr35/#Locale_Inheritance.
|
||||
langIndex = compactIndex
|
||||
} else {
|
||||
// Fall back to parent.
|
||||
langIndex = int(internal.Parent[langIndex])
|
||||
}
|
||||
break
|
||||
}
|
||||
if e.system == ns {
|
||||
pSymIndex = e.symIndex
|
||||
break outerLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if int(system) >= len(numSysData) { // algorithmic
|
||||
// Will generate ASCII digits in case the user inadvertently calls
|
||||
// WriteDigit or Digit on it.
|
||||
d := numSysData[0]
|
||||
d.id = system
|
||||
return Info{
|
||||
system: d,
|
||||
symIndex: pSymIndex,
|
||||
}
|
||||
}
|
||||
return Info{
|
||||
system: numSysData[system],
|
||||
symIndex: pSymIndex,
|
||||
}
|
||||
}
|
||||
|
||||
// InfoFromTag returns a Info for the given language tag.
|
||||
func InfoFromTag(t language.Tag) Info {
|
||||
for {
|
||||
if index, ok := language.CompactIndex(t); ok {
|
||||
return InfoFromLangID(index, t.TypeForKey("nu"))
|
||||
}
|
||||
t = t.Parent()
|
||||
}
|
||||
}
|
||||
|
||||
// IsDecimal reports if the numbering system can convert decimal to native
|
||||
// symbols one-to-one.
|
||||
func (n Info) IsDecimal() bool {
|
||||
return int(n.system.id) < len(numSysData)
|
||||
}
|
||||
|
||||
// WriteDigit writes the UTF-8 sequence for n corresponding to the given ASCII
|
||||
// digit to dst and reports the number of bytes written. dst must be large
|
||||
// enough to hold the rune (can be up to utf8.UTFMax bytes).
|
||||
func (n Info) WriteDigit(dst []byte, asciiDigit rune) int {
|
||||
copy(dst, n.system.zero[:n.system.digitSize])
|
||||
dst[n.system.digitSize-1] += byte(asciiDigit - '0')
|
||||
return int(n.system.digitSize)
|
||||
}
|
||||
|
||||
// Digit returns the digit for the numbering system for the corresponding ASCII
|
||||
// value. For example, ni.Digit('3') could return '三'. Note that the argument
|
||||
// is the rune constant '3', which equals 51, not the integer constant 3.
|
||||
func (n Info) Digit(asciiDigit rune) rune {
|
||||
var x [utf8.UTFMax]byte
|
||||
n.WriteDigit(x[:], asciiDigit)
|
||||
r, _ := utf8.DecodeRune(x[:])
|
||||
return r
|
||||
}
|
||||
|
||||
// Symbol returns the string for the given symbol type.
|
||||
func (n Info) Symbol(t SymbolType) string {
|
||||
return symData.Elem(int(symIndex[n.symIndex][t]))
|
||||
}
|
||||
|
||||
func formatForLang(t language.Tag, index []byte) *Pattern {
|
||||
for ; ; t = t.Parent() {
|
||||
if x, ok := language.CompactIndex(t); ok {
|
||||
return &formats[index[x]]
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package number
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/internal/testtext"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
testCases := []struct {
|
||||
lang string
|
||||
sym SymbolType
|
||||
wantSym string
|
||||
wantNine rune
|
||||
}{
|
||||
{"und", SymDecimal, ".", '9'},
|
||||
{"de", SymGroup, ".", '9'},
|
||||
{"de-BE", SymGroup, ".", '9'}, // inherits from de (no number data in CLDR)
|
||||
{"de-BE-oxendict", SymGroup, ".", '9'}, // inherits from de (no compact index)
|
||||
|
||||
// U+096F DEVANAGARI DIGIT NINE ('९')
|
||||
{"de-BE-u-nu-deva", SymGroup, ".", '\u096f'}, // miss -> latn -> de
|
||||
{"de-Cyrl-BE", SymGroup, ",", '9'}, // inherits from root
|
||||
{"de-CH", SymGroup, "'", '9'}, // overrides values in de
|
||||
{"de-CH-oxendict", SymGroup, "'", '9'}, // inherits from de-CH (no compact index)
|
||||
{"de-CH-u-nu-deva", SymGroup, "'", '\u096f'}, // miss -> latn -> de-CH
|
||||
|
||||
{"pa", SymExponential, "E", '9'},
|
||||
|
||||
// "×۱۰^" -> U+00d7 U+06f1 U+06f0^"
|
||||
// U+06F0 EXTENDED ARABIC-INDIC DIGIT ZERO
|
||||
// U+06F1 EXTENDED ARABIC-INDIC DIGIT ONE
|
||||
// U+06F9 EXTENDED ARABIC-INDIC DIGIT NINE
|
||||
{"pa-u-nu-arabext", SymExponential, "\u00d7\u06f1\u06f0^", '\u06f9'},
|
||||
|
||||
// "གྲངས་མེད" - > U+0f42 U+0fb2 U+0f44 U+0f66 U+0f0b U+0f58 U+0f7a U+0f51
|
||||
// Examples:
|
||||
// U+0F29 TIBETAN DIGIT NINE (༩)
|
||||
{"dz", SymInfinity, "\u0f42\u0fb2\u0f44\u0f66\u0f0b\u0f58\u0f7a\u0f51", '\u0f29'}, // defaults to tibt
|
||||
{"dz-u-nu-latn", SymInfinity, "∞", '9'}, // select alternative
|
||||
{"dz-u-nu-tibt", SymInfinity, "\u0f42\u0fb2\u0f44\u0f66\u0f0b\u0f58\u0f7a\u0f51", '\u0f29'},
|
||||
{"en-u-nu-tibt", SymInfinity, "∞", '\u0f29'},
|
||||
|
||||
// algorithmic number systems fall back to ASCII if Digits is used.
|
||||
{"en-u-nu-hanidec", SymPlusSign, "+", '9'},
|
||||
{"en-u-nu-roman", SymPlusSign, "+", '9'},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
info := InfoFromTag(language.MustParse(tc.lang))
|
||||
if got := info.Symbol(tc.sym); got != tc.wantSym {
|
||||
t.Errorf("%s:%v:sym: got %q; want %q", tc.lang, tc.sym, got, tc.wantSym)
|
||||
}
|
||||
if got := info.Digit('9'); got != tc.wantNine {
|
||||
t.Errorf("%s:%v:nine: got %q; want %q", tc.lang, tc.sym, got, tc.wantNine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormats(t *testing.T) {
|
||||
testCases := []struct {
|
||||
lang string
|
||||
pattern string
|
||||
index []byte
|
||||
}{
|
||||
{"en", "#,##0.###", tagToDecimal},
|
||||
{"de", "#,##0.###", tagToDecimal},
|
||||
{"de-CH", "#,##0.###", tagToDecimal},
|
||||
{"pa", "#,##,##0.###", tagToDecimal},
|
||||
{"pa-Arab", "#,##0.###", tagToDecimal}, // Does NOT inherit from pa!
|
||||
{"mr", "#,##,##0.###", tagToDecimal},
|
||||
{"mr-IN", "#,##,##0.###", tagToDecimal}, // Inherits from mr.
|
||||
{"nl", "#E0", tagToScientific},
|
||||
{"nl-MX", "#E0", tagToScientific}, // Inherits through Tag.Parent.
|
||||
{"zgh", "#,##0 %", tagToPercent},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
testtext.Run(t, tc.lang, func(t *testing.T) {
|
||||
got := formatForLang(language.MustParse(tc.lang), tc.index)
|
||||
want, _ := ParsePattern(tc.pattern)
|
||||
if *got != *want {
|
||||
t.Errorf("\ngot %#v;\nwant %#v", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package number
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// This file contains a parser for the CLDR number patterns as described in
|
||||
// http://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns.
|
||||
//
|
||||
// The following BNF is derived from this standard.
|
||||
//
|
||||
// pattern := subpattern (';' subpattern)?
|
||||
// subpattern := affix? number exponent? affix?
|
||||
// number := decimal | sigDigits
|
||||
// decimal := '#'* '0'* ('.' fraction)? | '#' | '0'
|
||||
// fraction := '0'* '#'*
|
||||
// sigDigits := '#'* '@' '@'* '#'*
|
||||
// exponent := 'E' '+'? '0'* '0'
|
||||
// padSpec := '*' \L
|
||||
//
|
||||
// Notes:
|
||||
// - An affix pattern may contain any runes, but runes with special meaning
|
||||
// should be escaped.
|
||||
// - Sequences of digits, '#', and '@' in decimal and sigDigits may have
|
||||
// interstitial commas.
|
||||
|
||||
// TODO: replace special characters in affixes (-, +, ¤) with control codes.
|
||||
|
||||
// Pattern holds information for formatting numbers. It is designed to hold
|
||||
// information from CLDR number patterns.
|
||||
//
|
||||
// This pattern is precompiled for all patterns for all languages. Even though
|
||||
// the number of patterns is not very large, we want to keep this small.
|
||||
//
|
||||
// This type is only intended for internal use.
|
||||
type Pattern struct {
|
||||
// TODO: this struct can be packed a lot better than it is now. Should be
|
||||
// possible to make it 32 bytes.
|
||||
|
||||
Affix string // includes prefix and suffix. First byte is prefix length.
|
||||
Offset uint16 // Offset into Affix for prefix and suffix
|
||||
NegOffset uint16 // Offset into Affix for negative prefix and suffix or 0.
|
||||
|
||||
Multiplier uint32
|
||||
RoundIncrement uint32 // Use Min*Digits to determine scale
|
||||
PadRune rune
|
||||
|
||||
FormatWidth uint16
|
||||
|
||||
GroupingSize [2]uint8
|
||||
Flags PatternFlag
|
||||
|
||||
// Number of digits.
|
||||
MinIntegerDigits uint8
|
||||
MaxIntegerDigits uint8
|
||||
MinFractionDigits uint8
|
||||
MaxFractionDigits uint8
|
||||
MinSignificantDigits uint8
|
||||
MaxSignificantDigits uint8
|
||||
MinExponentDigits uint8
|
||||
}
|
||||
|
||||
// A PatternFlag is a bit mask for the flag field of a Format.
|
||||
type PatternFlag uint8
|
||||
|
||||
const (
|
||||
AlwaysSign PatternFlag = 1 << iota
|
||||
AlwaysExpSign
|
||||
AlwaysDecimalSeparator
|
||||
ParenthesisForNegative // Common pattern. Saves space.
|
||||
|
||||
PadAfterNumber
|
||||
PadAfterAffix
|
||||
|
||||
PadBeforePrefix = 0 // Default
|
||||
PadAfterPrefix = PadAfterAffix
|
||||
PadBeforeSuffix = PadAfterNumber
|
||||
PadAfterSuffix = PadAfterNumber | PadAfterAffix
|
||||
PadMask = PadAfterNumber | PadAfterAffix
|
||||
)
|
||||
|
||||
type parser struct {
|
||||
*Pattern
|
||||
|
||||
leadingSharps int
|
||||
|
||||
pos int
|
||||
err error
|
||||
doNotTerminate bool
|
||||
groupingCount uint
|
||||
hasGroup bool
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (p *parser) setError(err error) {
|
||||
if p.err == nil {
|
||||
p.err = err
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) updateGrouping() {
|
||||
if p.hasGroup && p.groupingCount < 255 {
|
||||
p.GroupingSize[1] = p.GroupingSize[0]
|
||||
p.GroupingSize[0] = uint8(p.groupingCount)
|
||||
}
|
||||
p.groupingCount = 0
|
||||
p.hasGroup = true
|
||||
}
|
||||
|
||||
var (
|
||||
// TODO: more sensible and localizeable error messages.
|
||||
errMultiplePadSpecifiers = errors.New("format: pattern has multiple pad specifiers")
|
||||
errInvalidPadSpecifier = errors.New("format: invalid pad specifier")
|
||||
errInvalidQuote = errors.New("format: invalid quote")
|
||||
errAffixTooLarge = errors.New("format: prefix or suffix exceeds maximum UTF-8 length of 256 bytes")
|
||||
errDuplicatePercentSign = errors.New("format: duplicate percent sign")
|
||||
errDuplicatePermilleSign = errors.New("format: duplicate permille sign")
|
||||
errUnexpectedEnd = errors.New("format: unexpected end of pattern")
|
||||
)
|
||||
|
||||
// ParsePattern extracts formatting information from a CLDR number pattern.
|
||||
//
|
||||
// See http://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns.
|
||||
func ParsePattern(s string) (f *Pattern, err error) {
|
||||
p := parser{Pattern: &Pattern{}}
|
||||
|
||||
s = p.parseSubPattern(s)
|
||||
|
||||
if s != "" {
|
||||
// Parse negative sub pattern.
|
||||
if s[0] != ';' {
|
||||
p.setError(errors.New("format: error parsing first sub pattern"))
|
||||
return nil, p.err
|
||||
}
|
||||
neg := parser{Pattern: &Pattern{}} // just for extracting the affixes.
|
||||
s = neg.parseSubPattern(s[len(";"):])
|
||||
p.NegOffset = uint16(len(p.buf))
|
||||
p.buf = append(p.buf, neg.buf...)
|
||||
}
|
||||
if s != "" {
|
||||
p.setError(errors.New("format: spurious characters at end of pattern"))
|
||||
}
|
||||
if p.err != nil {
|
||||
return nil, p.err
|
||||
}
|
||||
if affix := string(p.buf); affix == "\x00\x00" || affix == "\x00\x00\x00\x00" {
|
||||
// No prefix or suffixes.
|
||||
p.NegOffset = 0
|
||||
} else {
|
||||
p.Affix = affix
|
||||
}
|
||||
return p.Pattern, nil
|
||||
}
|
||||
|
||||
func (p *parser) parseSubPattern(s string) string {
|
||||
s = p.parsePad(s, PadBeforePrefix)
|
||||
s = p.parseAffix(s)
|
||||
s = p.parsePad(s, PadAfterPrefix)
|
||||
|
||||
s = p.parse(p.number, s)
|
||||
|
||||
s = p.parsePad(s, PadBeforeSuffix)
|
||||
s = p.parseAffix(s)
|
||||
s = p.parsePad(s, PadAfterSuffix)
|
||||
return s
|
||||
}
|
||||
|
||||
func (p *parser) parsePad(s string, f PatternFlag) (tail string) {
|
||||
if len(s) >= 2 && s[0] == '*' {
|
||||
r, sz := utf8.DecodeRuneInString(s[1:])
|
||||
if p.PadRune != 0 {
|
||||
p.err = errMultiplePadSpecifiers
|
||||
} else {
|
||||
p.Flags |= f
|
||||
p.PadRune = r
|
||||
}
|
||||
return s[1+sz:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (p *parser) parseAffix(s string) string {
|
||||
x := len(p.buf)
|
||||
p.buf = append(p.buf, 0) // placeholder for affix length
|
||||
|
||||
s = p.parse(p.affix, s)
|
||||
|
||||
n := len(p.buf) - x - 1
|
||||
if n > 0xFF {
|
||||
p.setError(errAffixTooLarge)
|
||||
}
|
||||
p.buf[x] = uint8(n)
|
||||
return s
|
||||
}
|
||||
|
||||
// state implements a state transition. It returns the new state. A state
|
||||
// function may set an error on the parser or may simply return on an incorrect
|
||||
// token and let the next phase fail.
|
||||
type state func(r rune) state
|
||||
|
||||
// parse repeatedly applies a state function on the given string until a
|
||||
// termination condition is reached.
|
||||
func (p *parser) parse(fn state, s string) (tail string) {
|
||||
for i, r := range s {
|
||||
p.doNotTerminate = false
|
||||
if fn = fn(r); fn == nil || p.err != nil {
|
||||
return s[i:]
|
||||
}
|
||||
p.FormatWidth++
|
||||
}
|
||||
if p.doNotTerminate {
|
||||
p.setError(errUnexpectedEnd)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *parser) affix(r rune) state {
|
||||
switch r {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'#', '@', '.', '*', ',', ';':
|
||||
return nil
|
||||
case '\'':
|
||||
return p.escape
|
||||
case '%':
|
||||
if p.Multiplier != 0 {
|
||||
p.setError(errDuplicatePercentSign)
|
||||
}
|
||||
p.Multiplier = 100
|
||||
case '\u2030': // ‰ Per mille
|
||||
if p.Multiplier != 0 {
|
||||
p.setError(errDuplicatePermilleSign)
|
||||
}
|
||||
p.Multiplier = 1000
|
||||
// TODO: handle currency somehow: ¤, ¤¤, ¤¤¤, ¤¤¤¤
|
||||
}
|
||||
p.buf = append(p.buf, string(r)...)
|
||||
return p.affix
|
||||
}
|
||||
|
||||
func (p *parser) escape(r rune) state {
|
||||
switch r {
|
||||
case '\'':
|
||||
return p.affix
|
||||
default:
|
||||
p.buf = append(p.buf, string(r)...)
|
||||
}
|
||||
return p.escape
|
||||
}
|
||||
|
||||
// number parses a number. The BNF says the integer part should always have
|
||||
// a '0', but that does not appear to be the case according to the rest of the
|
||||
// documentation. We will allow having only '#' numbers.
|
||||
func (p *parser) number(r rune) state {
|
||||
switch r {
|
||||
case '#':
|
||||
p.groupingCount++
|
||||
p.leadingSharps++
|
||||
case '@':
|
||||
p.groupingCount++
|
||||
p.leadingSharps = 0
|
||||
return p.sigDigits(r)
|
||||
case ',':
|
||||
if p.leadingSharps == 0 { // no leading commas
|
||||
return nil
|
||||
}
|
||||
p.updateGrouping()
|
||||
case 'E':
|
||||
p.MaxIntegerDigits = uint8(p.leadingSharps)
|
||||
return p.exponent
|
||||
case '.': // allow ".##" etc.
|
||||
p.updateGrouping()
|
||||
return p.fraction
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return p.integer(r)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return p.number
|
||||
}
|
||||
|
||||
func (p *parser) integer(r rune) state {
|
||||
if !('0' <= r && r <= '9') {
|
||||
var next state
|
||||
switch r {
|
||||
case 'E':
|
||||
if p.leadingSharps > 0 {
|
||||
p.MaxIntegerDigits = uint8(p.leadingSharps) + p.MinIntegerDigits
|
||||
}
|
||||
next = p.exponent
|
||||
case '.':
|
||||
next = p.fraction
|
||||
}
|
||||
p.updateGrouping()
|
||||
return next
|
||||
}
|
||||
p.RoundIncrement = p.RoundIncrement*10 + uint32(r-'0')
|
||||
p.groupingCount++
|
||||
p.MinIntegerDigits++
|
||||
return p.integer
|
||||
}
|
||||
|
||||
func (p *parser) sigDigits(r rune) state {
|
||||
switch r {
|
||||
case '@':
|
||||
p.groupingCount++
|
||||
p.MaxSignificantDigits++
|
||||
p.MinSignificantDigits++
|
||||
case '#':
|
||||
return p.sigDigitsFinal(r)
|
||||
case 'E':
|
||||
p.updateGrouping()
|
||||
return p.normalizeSigDigitsWithExponent()
|
||||
default:
|
||||
p.updateGrouping()
|
||||
return nil
|
||||
}
|
||||
return p.sigDigits
|
||||
}
|
||||
|
||||
func (p *parser) sigDigitsFinal(r rune) state {
|
||||
switch r {
|
||||
case '#':
|
||||
p.groupingCount++
|
||||
p.MaxSignificantDigits++
|
||||
case 'E':
|
||||
p.updateGrouping()
|
||||
return p.normalizeSigDigitsWithExponent()
|
||||
default:
|
||||
p.updateGrouping()
|
||||
return nil
|
||||
}
|
||||
return p.sigDigitsFinal
|
||||
}
|
||||
|
||||
func (p *parser) normalizeSigDigitsWithExponent() state {
|
||||
p.MinIntegerDigits, p.MaxIntegerDigits = 1, 1
|
||||
p.MinFractionDigits = p.MinSignificantDigits - 1
|
||||
p.MaxFractionDigits = p.MaxSignificantDigits - 1
|
||||
p.MinSignificantDigits, p.MaxSignificantDigits = 0, 0
|
||||
return p.exponent
|
||||
}
|
||||
|
||||
func (p *parser) fraction(r rune) state {
|
||||
switch r {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
p.RoundIncrement = p.RoundIncrement*10 + uint32(r-'0')
|
||||
p.MinFractionDigits++
|
||||
p.MaxFractionDigits++
|
||||
case '#':
|
||||
p.MaxFractionDigits++
|
||||
case 'E':
|
||||
if p.leadingSharps > 0 {
|
||||
p.MaxIntegerDigits = uint8(p.leadingSharps) + p.MinIntegerDigits
|
||||
}
|
||||
return p.exponent
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return p.fraction
|
||||
}
|
||||
|
||||
func (p *parser) exponent(r rune) state {
|
||||
switch r {
|
||||
case '+':
|
||||
// Set mode and check it wasn't already set.
|
||||
if p.Flags&AlwaysExpSign != 0 || p.MinExponentDigits > 0 {
|
||||
break
|
||||
}
|
||||
p.Flags |= AlwaysExpSign
|
||||
p.doNotTerminate = true
|
||||
return p.exponent
|
||||
case '0':
|
||||
p.MinExponentDigits++
|
||||
return p.exponent
|
||||
}
|
||||
// termination condition
|
||||
if p.MinExponentDigits == 0 {
|
||||
p.setError(errors.New("format: need at least one digit"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package number
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var testCases = []struct {
|
||||
pat string
|
||||
want *Pattern
|
||||
}{{
|
||||
"#",
|
||||
&Pattern{
|
||||
FormatWidth: 1,
|
||||
// TODO: Should MinIntegerDigits be 1?
|
||||
},
|
||||
}, {
|
||||
"0",
|
||||
&Pattern{
|
||||
FormatWidth: 1,
|
||||
MinIntegerDigits: 1,
|
||||
},
|
||||
}, {
|
||||
"0000",
|
||||
&Pattern{
|
||||
FormatWidth: 4,
|
||||
MinIntegerDigits: 4,
|
||||
},
|
||||
}, {
|
||||
".#",
|
||||
&Pattern{
|
||||
FormatWidth: 2,
|
||||
MaxFractionDigits: 1,
|
||||
},
|
||||
}, {
|
||||
"#0.###",
|
||||
&Pattern{
|
||||
FormatWidth: 6,
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 3,
|
||||
},
|
||||
}, {
|
||||
"#0.######",
|
||||
&Pattern{
|
||||
FormatWidth: 9,
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 6,
|
||||
},
|
||||
}, {
|
||||
"#,##0.###",
|
||||
&Pattern{
|
||||
FormatWidth: 9,
|
||||
GroupingSize: [2]uint8{3, 0},
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 3,
|
||||
},
|
||||
}, {
|
||||
"#,##,##0.###",
|
||||
&Pattern{
|
||||
FormatWidth: 12,
|
||||
GroupingSize: [2]uint8{3, 2},
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 3,
|
||||
},
|
||||
}, {
|
||||
// Ignore additional separators.
|
||||
"#,####,##,##0.###",
|
||||
&Pattern{
|
||||
FormatWidth: 17,
|
||||
GroupingSize: [2]uint8{3, 2},
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 3,
|
||||
},
|
||||
}, {
|
||||
"#E0",
|
||||
&Pattern{
|
||||
FormatWidth: 3,
|
||||
MaxIntegerDigits: 1,
|
||||
MinExponentDigits: 1,
|
||||
},
|
||||
}, {
|
||||
"0E0",
|
||||
&Pattern{
|
||||
FormatWidth: 3,
|
||||
MinIntegerDigits: 1,
|
||||
MinExponentDigits: 1,
|
||||
},
|
||||
}, {
|
||||
"##00.0#E0",
|
||||
&Pattern{
|
||||
FormatWidth: 9,
|
||||
MinIntegerDigits: 2,
|
||||
MaxIntegerDigits: 4,
|
||||
MinFractionDigits: 1,
|
||||
MaxFractionDigits: 2,
|
||||
MinExponentDigits: 1,
|
||||
},
|
||||
}, {
|
||||
"#00.0E+0",
|
||||
&Pattern{
|
||||
FormatWidth: 8,
|
||||
Flags: AlwaysExpSign,
|
||||
MinIntegerDigits: 2,
|
||||
MaxIntegerDigits: 3,
|
||||
MinFractionDigits: 1,
|
||||
MaxFractionDigits: 1,
|
||||
MinExponentDigits: 1,
|
||||
},
|
||||
}, {
|
||||
"0.0E++0",
|
||||
nil,
|
||||
}, {
|
||||
"#0E+",
|
||||
nil,
|
||||
}, {
|
||||
// significant digits
|
||||
"@",
|
||||
&Pattern{
|
||||
FormatWidth: 1,
|
||||
MinSignificantDigits: 1,
|
||||
MaxSignificantDigits: 1,
|
||||
},
|
||||
}, {
|
||||
// significant digits
|
||||
"@@@@",
|
||||
&Pattern{
|
||||
FormatWidth: 4,
|
||||
MinSignificantDigits: 4,
|
||||
MaxSignificantDigits: 4,
|
||||
},
|
||||
}, {
|
||||
"@###",
|
||||
&Pattern{
|
||||
FormatWidth: 4,
|
||||
MinSignificantDigits: 1,
|
||||
MaxSignificantDigits: 4,
|
||||
},
|
||||
}, {
|
||||
// Exponents in significant digits mode gets normalized.
|
||||
"@@E0",
|
||||
&Pattern{
|
||||
FormatWidth: 4,
|
||||
MinIntegerDigits: 1,
|
||||
MaxIntegerDigits: 1,
|
||||
MinFractionDigits: 1,
|
||||
MaxFractionDigits: 1,
|
||||
MinExponentDigits: 1,
|
||||
},
|
||||
}, {
|
||||
"@###E00",
|
||||
&Pattern{
|
||||
FormatWidth: 7,
|
||||
MinIntegerDigits: 1,
|
||||
MaxIntegerDigits: 1,
|
||||
MinFractionDigits: 0,
|
||||
MaxFractionDigits: 3,
|
||||
MinExponentDigits: 2,
|
||||
},
|
||||
}, {
|
||||
// The significant digits mode does not allow fractions.
|
||||
"@###.#E0",
|
||||
nil,
|
||||
}, {
|
||||
//alternative negative pattern
|
||||
"#0.###;(#0.###)",
|
||||
&Pattern{
|
||||
Affix: "\x00\x00\x01(\x01)",
|
||||
NegOffset: 2,
|
||||
FormatWidth: 6,
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 3,
|
||||
},
|
||||
}, {
|
||||
// Rounding increments
|
||||
"1.05",
|
||||
&Pattern{
|
||||
RoundIncrement: 105,
|
||||
FormatWidth: 4,
|
||||
MinIntegerDigits: 1,
|
||||
MinFractionDigits: 2,
|
||||
MaxFractionDigits: 2,
|
||||
},
|
||||
}, {
|
||||
"0.0%",
|
||||
&Pattern{
|
||||
Affix: "\x00\x01%",
|
||||
Multiplier: 100,
|
||||
FormatWidth: 4,
|
||||
MinIntegerDigits: 1,
|
||||
MinFractionDigits: 1,
|
||||
MaxFractionDigits: 1,
|
||||
},
|
||||
}, {
|
||||
"0.0‰",
|
||||
&Pattern{
|
||||
Affix: "\x00\x03‰",
|
||||
Multiplier: 1000,
|
||||
FormatWidth: 4,
|
||||
MinIntegerDigits: 1,
|
||||
MinFractionDigits: 1,
|
||||
MaxFractionDigits: 1,
|
||||
},
|
||||
}, {
|
||||
"#,##0.00¤",
|
||||
&Pattern{
|
||||
Affix: "\x00\x02¤",
|
||||
FormatWidth: 9,
|
||||
GroupingSize: [2]uint8{3, 0},
|
||||
MinIntegerDigits: 1,
|
||||
MinFractionDigits: 2,
|
||||
MaxFractionDigits: 2,
|
||||
},
|
||||
}, {
|
||||
"#,##0.00 ¤;(#,##0.00 ¤)",
|
||||
&Pattern{Affix: "\x00\x04\u00a0¤\x01(\x05\u00a0¤)",
|
||||
NegOffset: 6,
|
||||
Multiplier: 0,
|
||||
FormatWidth: 10,
|
||||
GroupingSize: [2]uint8{3, 0},
|
||||
MinIntegerDigits: 1,
|
||||
MinFractionDigits: 2,
|
||||
MaxFractionDigits: 2,
|
||||
},
|
||||
}, {
|
||||
// padding
|
||||
"*x#",
|
||||
&Pattern{
|
||||
PadRune: 'x',
|
||||
FormatWidth: 1,
|
||||
},
|
||||
}, {
|
||||
// padding
|
||||
"#*x",
|
||||
&Pattern{
|
||||
PadRune: 'x',
|
||||
FormatWidth: 1,
|
||||
Flags: PadBeforeSuffix,
|
||||
},
|
||||
}, {
|
||||
"*xpre#suf",
|
||||
&Pattern{
|
||||
Affix: "\x03pre\x03suf",
|
||||
PadRune: 'x',
|
||||
FormatWidth: 7,
|
||||
},
|
||||
}, {
|
||||
"pre*x#suf",
|
||||
&Pattern{
|
||||
Affix: "\x03pre\x03suf",
|
||||
PadRune: 'x',
|
||||
FormatWidth: 7,
|
||||
Flags: PadAfterPrefix,
|
||||
},
|
||||
}, {
|
||||
"pre#*xsuf",
|
||||
&Pattern{
|
||||
Affix: "\x03pre\x03suf",
|
||||
PadRune: 'x',
|
||||
FormatWidth: 7,
|
||||
Flags: PadBeforeSuffix,
|
||||
},
|
||||
}, {
|
||||
"pre#suf*x",
|
||||
&Pattern{
|
||||
Affix: "\x03pre\x03suf",
|
||||
PadRune: 'x',
|
||||
FormatWidth: 7,
|
||||
Flags: PadAfterSuffix,
|
||||
},
|
||||
}, {
|
||||
// no duplicate padding
|
||||
"*xpre#suf*x", nil,
|
||||
}, {
|
||||
// no duplicate padding
|
||||
"*xpre#suf*x", nil,
|
||||
}}
|
||||
|
||||
func TestParsePattern(t *testing.T) {
|
||||
for i, tc := range testCases {
|
||||
f, err := ParsePattern(tc.pat)
|
||||
if !reflect.DeepEqual(f, tc.want) {
|
||||
t.Errorf("%d:%s:\ngot %#v;\nwant %#v", i, tc.pat, f, tc.want)
|
||||
}
|
||||
if got, want := err != nil, tc.want == nil; got != want {
|
||||
t.Errorf("%d:%s:error: got %v; want %v", i, tc.pat, err, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatternSize(t *testing.T) {
|
||||
if sz := unsafe.Sizeof(Pattern{}); sz > 48 {
|
||||
t.Errorf("got %d; want 48", sz)
|
||||
}
|
||||
|
||||
}
|
||||
+1119
File diff suppressed because it is too large
Load Diff
+125
@@ -0,0 +1,125 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package number
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/internal/gen"
|
||||
"golang.org/x/text/internal/testtext"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/unicode/cldr"
|
||||
)
|
||||
|
||||
var draft = flag.String("draft",
|
||||
"contributed",
|
||||
`Minimal draft requirements (approved, contributed, provisional, unconfirmed).`)
|
||||
|
||||
func TestNumberSystems(t *testing.T) {
|
||||
testtext.SkipIfNotLong(t)
|
||||
|
||||
r := gen.OpenCLDRCoreZip()
|
||||
defer r.Close()
|
||||
|
||||
d := &cldr.Decoder{}
|
||||
d.SetDirFilter("supplemental")
|
||||
d.SetSectionFilter("numberingSystem")
|
||||
data, err := d.DecodeZip(r)
|
||||
if err != nil {
|
||||
t.Fatalf("DecodeZip: %v", err)
|
||||
}
|
||||
|
||||
for _, ns := range data.Supplemental().NumberingSystems.NumberingSystem {
|
||||
n := systemMap[ns.Id]
|
||||
if int(n) >= len(numSysData) {
|
||||
continue
|
||||
}
|
||||
info := InfoFromLangID(0, ns.Id)
|
||||
val := '0'
|
||||
for _, rWant := range ns.Digits {
|
||||
if rGot := info.Digit(val); rGot != rWant {
|
||||
t.Errorf("%s:%d: got %U; want %U", ns.Id, val, rGot, rWant)
|
||||
}
|
||||
val++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSymbols(t *testing.T) {
|
||||
testtext.SkipIfNotLong(t)
|
||||
|
||||
draft, err := cldr.ParseDraft(*draft)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid draft level: %v", err)
|
||||
}
|
||||
|
||||
r := gen.OpenCLDRCoreZip()
|
||||
defer r.Close()
|
||||
|
||||
d := &cldr.Decoder{}
|
||||
d.SetDirFilter("main")
|
||||
d.SetSectionFilter("numbers")
|
||||
data, err := d.DecodeZip(r)
|
||||
if err != nil {
|
||||
t.Fatalf("DecodeZip: %v", err)
|
||||
}
|
||||
|
||||
for _, lang := range data.Locales() {
|
||||
ldml := data.RawLDML(lang)
|
||||
if ldml.Numbers == nil {
|
||||
continue
|
||||
}
|
||||
langIndex, ok := language.CompactIndex(language.MustParse(lang))
|
||||
if !ok {
|
||||
t.Fatalf("No compact index for language %s", lang)
|
||||
}
|
||||
|
||||
syms := cldr.MakeSlice(&ldml.Numbers.Symbols)
|
||||
syms.SelectDraft(draft)
|
||||
|
||||
for _, sym := range ldml.Numbers.Symbols {
|
||||
if sym.NumberSystem == "" {
|
||||
continue
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
st SymbolType
|
||||
x interface{}
|
||||
}{
|
||||
{"Decimal", SymDecimal, sym.Decimal},
|
||||
{"Group", SymGroup, sym.Group},
|
||||
{"List", SymList, sym.List},
|
||||
{"PercentSign", SymPercentSign, sym.PercentSign},
|
||||
{"PlusSign", SymPlusSign, sym.PlusSign},
|
||||
{"MinusSign", SymMinusSign, sym.MinusSign},
|
||||
{"Exponential", SymExponential, sym.Exponential},
|
||||
{"SuperscriptingExponent", SymSuperscriptingExponent, sym.SuperscriptingExponent},
|
||||
{"PerMille", SymPerMille, sym.PerMille},
|
||||
{"Infinity", SymInfinity, sym.Infinity},
|
||||
{"NaN", SymNan, sym.Nan},
|
||||
{"TimeSeparator", SymTimeSeparator, sym.TimeSeparator},
|
||||
}
|
||||
info := InfoFromLangID(langIndex, sym.NumberSystem)
|
||||
for _, tc := range testCases {
|
||||
// Extract the wanted value.
|
||||
v := reflect.ValueOf(tc.x)
|
||||
if v.Len() == 0 {
|
||||
return
|
||||
}
|
||||
if v.Len() > 1 {
|
||||
t.Fatalf("Multiple values of %q within single symbol not supported.", tc.name)
|
||||
}
|
||||
want := v.Index(0).MethodByName("Data").Call(nil)[0].String()
|
||||
got := info.Symbol(tc.st)
|
||||
if got != want {
|
||||
t.Errorf("%s:%s:%s: got %q; want %q", lang, sym.NumberSystem, tc.name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user