Checking in vendor folder for ease of using go get.
This commit is contained in:
parent
7a1251853b
commit
cdb4b5a1d0
3554 changed files with 1270116 additions and 0 deletions
51
vendor/golang.org/x/text/internal/number/common.go
generated
vendored
Normal file
51
vendor/golang.org/x/text/internal/number/common.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// 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
|
||||
)
|
||||
|
||||
const hasNonLatnMask = 0x8000
|
||||
|
||||
// symOffset is an offset into altSymData if the bit indicated by hasNonLatnMask
|
||||
// is not 0 (with this bit masked out), and an offset into symIndex otherwise.
|
||||
//
|
||||
// TODO: this type can be a byte again if we use an indirection into altsymData
|
||||
// and introduce an alt -> offset slice (the length of this will be number of
|
||||
// alternatives plus 1). This also allows getting rid of the compactTag field
|
||||
// in altSymData. In total this will save about 1K.
|
||||
type symOffset uint16
|
||||
|
||||
type altSymData struct {
|
||||
compactTag uint16
|
||||
symIndex symOffset
|
||||
system system
|
||||
}
|
498
vendor/golang.org/x/text/internal/number/decimal.go
generated
vendored
Normal file
498
vendor/golang.org/x/text/internal/number/decimal.go
generated
vendored
Normal file
|
@ -0,0 +1,498 @@
|
|||
// Copyright 2017 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 stringer -type RoundingMode
|
||||
|
||||
package number
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// RoundingMode determines how a number is rounded to the desired precision.
|
||||
type RoundingMode byte
|
||||
|
||||
const (
|
||||
ToNearestEven RoundingMode = iota // towards the nearest integer, or towards an even number if equidistant.
|
||||
ToNearestZero // towards the nearest integer, or towards zero if equidistant.
|
||||
ToNearestAway // towards the nearest integer, or away from zero if equidistant.
|
||||
ToPositiveInf // towards infinity
|
||||
ToNegativeInf // towards negative infinity
|
||||
ToZero // towards zero
|
||||
AwayFromZero // away from zero
|
||||
numModes
|
||||
)
|
||||
|
||||
const maxIntDigits = 20
|
||||
|
||||
// A Decimal represents a floating point number in decimal format.
|
||||
// Digits represents a number [0, 1.0), and the absolute value represented by
|
||||
// Decimal is Digits * 10^Exp. Leading and trailing zeros may be omitted and Exp
|
||||
// may point outside a valid position in Digits.
|
||||
//
|
||||
// Examples:
|
||||
// Number Decimal
|
||||
// 12345 Digits: [1, 2, 3, 4, 5], Exp: 5
|
||||
// 12.345 Digits: [1, 2, 3, 4, 5], Exp: 2
|
||||
// 12000 Digits: [1, 2], Exp: 5
|
||||
// 12000.00 Digits: [1, 2], Exp: 5
|
||||
// 0.00123 Digits: [1, 2, 3], Exp: -2
|
||||
// 0 Digits: [], Exp: 0
|
||||
type Decimal struct {
|
||||
digits
|
||||
|
||||
buf [maxIntDigits]byte
|
||||
}
|
||||
|
||||
type digits struct {
|
||||
Digits []byte // mantissa digits, big-endian
|
||||
Exp int32 // exponent
|
||||
Neg bool
|
||||
Inf bool // Takes precedence over Digits and Exp.
|
||||
NaN bool // Takes precedence over Inf.
|
||||
}
|
||||
|
||||
// Digits represents a floating point number represented in digits of the
|
||||
// base in which a number is to be displayed. It is similar to Decimal, but
|
||||
// keeps track of trailing fraction zeros and the comma placement for
|
||||
// engineering notation. Digits must have at least one digit.
|
||||
//
|
||||
// Examples:
|
||||
// Number Decimal
|
||||
// decimal
|
||||
// 12345 Digits: [1, 2, 3, 4, 5], Exp: 5 End: 5
|
||||
// 12.345 Digits: [1, 2, 3, 4, 5], Exp: 2 End: 5
|
||||
// 12000 Digits: [1, 2], Exp: 5 End: 5
|
||||
// 12000.00 Digits: [1, 2], Exp: 5 End: 7
|
||||
// 0.00123 Digits: [1, 2, 3], Exp: -2 End: 3
|
||||
// 0 Digits: [], Exp: 0 End: 1
|
||||
// scientific (actual exp is Exp - Comma)
|
||||
// 0e0 Digits: [0], Exp: 1, End: 1, Comma: 1
|
||||
// .0e0 Digits: [0], Exp: 0, End: 1, Comma: 0
|
||||
// 0.0e0 Digits: [0], Exp: 1, End: 2, Comma: 1
|
||||
// 1.23e4 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 1
|
||||
// .123e5 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 0
|
||||
// engineering
|
||||
// 12.3e3 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 2
|
||||
type Digits struct {
|
||||
digits
|
||||
// End indicates the end position of the number.
|
||||
End int32 // For decimals Exp <= End. For scientific len(Digits) <= End.
|
||||
// Comma is used for the comma position for scientific (always 0 or 1) and
|
||||
// engineering notation (always 0, 1, 2, or 3).
|
||||
Comma uint8
|
||||
// IsScientific indicates whether this number is to be rendered as a
|
||||
// scientific number.
|
||||
IsScientific bool
|
||||
}
|
||||
|
||||
func (d *Digits) NumFracDigits() int {
|
||||
if d.Exp >= d.End {
|
||||
return 0
|
||||
}
|
||||
return int(d.End - d.Exp)
|
||||
}
|
||||
|
||||
// normalize returns a new Decimal with leading and trailing zeros removed.
|
||||
func (d *Decimal) normalize() (n Decimal) {
|
||||
n = *d
|
||||
b := n.Digits
|
||||
// Strip leading zeros. Resulting number of digits is significant digits.
|
||||
for len(b) > 0 && b[0] == 0 {
|
||||
b = b[1:]
|
||||
n.Exp--
|
||||
}
|
||||
// Strip trailing zeros
|
||||
for len(b) > 0 && b[len(b)-1] == 0 {
|
||||
b = b[:len(b)-1]
|
||||
}
|
||||
if len(b) == 0 {
|
||||
n.Exp = 0
|
||||
}
|
||||
n.Digits = b
|
||||
return n
|
||||
}
|
||||
|
||||
func (d *Decimal) clear() {
|
||||
b := d.Digits
|
||||
if b == nil {
|
||||
b = d.buf[:0]
|
||||
}
|
||||
*d = Decimal{}
|
||||
d.Digits = b[:0]
|
||||
}
|
||||
|
||||
func (x *Decimal) String() string {
|
||||
if x.NaN {
|
||||
return "NaN"
|
||||
}
|
||||
var buf []byte
|
||||
if x.Neg {
|
||||
buf = append(buf, '-')
|
||||
}
|
||||
if x.Inf {
|
||||
buf = append(buf, "Inf"...)
|
||||
return string(buf)
|
||||
}
|
||||
switch {
|
||||
case len(x.Digits) == 0:
|
||||
buf = append(buf, '0')
|
||||
case x.Exp <= 0:
|
||||
// 0.00ddd
|
||||
buf = append(buf, "0."...)
|
||||
buf = appendZeros(buf, -int(x.Exp))
|
||||
buf = appendDigits(buf, x.Digits)
|
||||
|
||||
case /* 0 < */ int(x.Exp) < len(x.Digits):
|
||||
// dd.ddd
|
||||
buf = appendDigits(buf, x.Digits[:x.Exp])
|
||||
buf = append(buf, '.')
|
||||
buf = appendDigits(buf, x.Digits[x.Exp:])
|
||||
|
||||
default: // len(x.Digits) <= x.Exp
|
||||
// ddd00
|
||||
buf = appendDigits(buf, x.Digits)
|
||||
buf = appendZeros(buf, int(x.Exp)-len(x.Digits))
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
func appendDigits(buf []byte, digits []byte) []byte {
|
||||
for _, c := range digits {
|
||||
buf = append(buf, c+'0')
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
// appendZeros appends n 0 digits to buf and returns buf.
|
||||
func appendZeros(buf []byte, n int) []byte {
|
||||
for ; n > 0; n-- {
|
||||
buf = append(buf, '0')
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func (d *digits) round(mode RoundingMode, n int) {
|
||||
if n >= len(d.Digits) {
|
||||
return
|
||||
}
|
||||
// Make rounding decision: The result mantissa is truncated ("rounded down")
|
||||
// by default. Decide if we need to increment, or "round up", the (unsigned)
|
||||
// mantissa.
|
||||
inc := false
|
||||
switch mode {
|
||||
case ToNegativeInf:
|
||||
inc = d.Neg
|
||||
case ToPositiveInf:
|
||||
inc = !d.Neg
|
||||
case ToZero:
|
||||
// nothing to do
|
||||
case AwayFromZero:
|
||||
inc = true
|
||||
case ToNearestEven:
|
||||
inc = d.Digits[n] > 5 || d.Digits[n] == 5 &&
|
||||
(len(d.Digits) > n+1 || n == 0 || d.Digits[n-1]&1 != 0)
|
||||
case ToNearestAway:
|
||||
inc = d.Digits[n] >= 5
|
||||
case ToNearestZero:
|
||||
inc = d.Digits[n] > 5 || d.Digits[n] == 5 && len(d.Digits) > n+1
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
if inc {
|
||||
d.roundUp(n)
|
||||
} else {
|
||||
d.roundDown(n)
|
||||
}
|
||||
}
|
||||
|
||||
// roundFloat rounds a floating point number.
|
||||
func (r RoundingMode) roundFloat(x float64) float64 {
|
||||
// Make rounding decision: The result mantissa is truncated ("rounded down")
|
||||
// by default. Decide if we need to increment, or "round up", the (unsigned)
|
||||
// mantissa.
|
||||
abs := x
|
||||
if x < 0 {
|
||||
abs = -x
|
||||
}
|
||||
i, f := math.Modf(abs)
|
||||
if f == 0.0 {
|
||||
return x
|
||||
}
|
||||
inc := false
|
||||
switch r {
|
||||
case ToNegativeInf:
|
||||
inc = x < 0
|
||||
case ToPositiveInf:
|
||||
inc = x >= 0
|
||||
case ToZero:
|
||||
// nothing to do
|
||||
case AwayFromZero:
|
||||
inc = true
|
||||
case ToNearestEven:
|
||||
// TODO: check overflow
|
||||
inc = f > 0.5 || f == 0.5 && int64(i)&1 != 0
|
||||
case ToNearestAway:
|
||||
inc = f >= 0.5
|
||||
case ToNearestZero:
|
||||
inc = f > 0.5
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
if inc {
|
||||
i += 1
|
||||
}
|
||||
if abs != x {
|
||||
i = -i
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (x *digits) roundUp(n int) {
|
||||
if n < 0 || n >= len(x.Digits) {
|
||||
return // nothing to do
|
||||
}
|
||||
// find first digit < 9
|
||||
for n > 0 && x.Digits[n-1] >= 9 {
|
||||
n--
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
// all digits are 9s => round up to 1 and update exponent
|
||||
x.Digits[0] = 1 // ok since len(x.Digits) > n
|
||||
x.Digits = x.Digits[:1]
|
||||
x.Exp++
|
||||
return
|
||||
}
|
||||
x.Digits[n-1]++
|
||||
x.Digits = x.Digits[:n]
|
||||
// x already trimmed
|
||||
}
|
||||
|
||||
func (x *digits) roundDown(n int) {
|
||||
if n < 0 || n >= len(x.Digits) {
|
||||
return // nothing to do
|
||||
}
|
||||
x.Digits = x.Digits[:n]
|
||||
trim(x)
|
||||
}
|
||||
|
||||
// trim cuts off any trailing zeros from x's mantissa;
|
||||
// they are meaningless for the value of x.
|
||||
func trim(x *digits) {
|
||||
i := len(x.Digits)
|
||||
for i > 0 && x.Digits[i-1] == 0 {
|
||||
i--
|
||||
}
|
||||
x.Digits = x.Digits[:i]
|
||||
if i == 0 {
|
||||
x.Exp = 0
|
||||
}
|
||||
}
|
||||
|
||||
// A Converter converts a number into decimals according to the given rounding
|
||||
// criteria.
|
||||
type Converter interface {
|
||||
Convert(d *Decimal, r RoundingContext)
|
||||
}
|
||||
|
||||
const (
|
||||
signed = true
|
||||
unsigned = false
|
||||
)
|
||||
|
||||
// Convert converts the given number to the decimal representation using the
|
||||
// supplied RoundingContext.
|
||||
func (d *Decimal) Convert(r RoundingContext, number interface{}) {
|
||||
switch f := number.(type) {
|
||||
case Converter:
|
||||
d.clear()
|
||||
f.Convert(d, r)
|
||||
case float32:
|
||||
d.ConvertFloat(r, float64(f), 32)
|
||||
case float64:
|
||||
d.ConvertFloat(r, f, 64)
|
||||
case int:
|
||||
d.ConvertInt(r, signed, uint64(f))
|
||||
case int8:
|
||||
d.ConvertInt(r, signed, uint64(f))
|
||||
case int16:
|
||||
d.ConvertInt(r, signed, uint64(f))
|
||||
case int32:
|
||||
d.ConvertInt(r, signed, uint64(f))
|
||||
case int64:
|
||||
d.ConvertInt(r, signed, uint64(f))
|
||||
case uint:
|
||||
d.ConvertInt(r, unsigned, uint64(f))
|
||||
case uint8:
|
||||
d.ConvertInt(r, unsigned, uint64(f))
|
||||
case uint16:
|
||||
d.ConvertInt(r, unsigned, uint64(f))
|
||||
case uint32:
|
||||
d.ConvertInt(r, unsigned, uint64(f))
|
||||
case uint64:
|
||||
d.ConvertInt(r, unsigned, f)
|
||||
|
||||
default:
|
||||
d.NaN = true
|
||||
// TODO:
|
||||
// case string: if produced by strconv, allows for easy arbitrary pos.
|
||||
// case reflect.Value:
|
||||
// case big.Float
|
||||
// case big.Int
|
||||
// case big.Rat?
|
||||
// catch underlyings using reflect or will this already be done by the
|
||||
// message package?
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertInt converts an integer to decimals.
|
||||
func (d *Decimal) ConvertInt(r RoundingContext, signed bool, x uint64) {
|
||||
if r.Increment > 0 {
|
||||
// TODO: if uint64 is too large, fall back to float64
|
||||
if signed {
|
||||
d.ConvertFloat(r, float64(int64(x)), 64)
|
||||
} else {
|
||||
d.ConvertFloat(r, float64(x), 64)
|
||||
}
|
||||
return
|
||||
}
|
||||
d.clear()
|
||||
if signed && int64(x) < 0 {
|
||||
x = uint64(-int64(x))
|
||||
d.Neg = true
|
||||
}
|
||||
d.fillIntDigits(x)
|
||||
d.Exp = int32(len(d.Digits))
|
||||
}
|
||||
|
||||
// ConvertFloat converts a floating point number to decimals.
|
||||
func (d *Decimal) ConvertFloat(r RoundingContext, x float64, size int) {
|
||||
d.clear()
|
||||
if math.IsNaN(x) {
|
||||
d.NaN = true
|
||||
return
|
||||
}
|
||||
// Simple case: decimal notation
|
||||
if r.Increment > 0 {
|
||||
scale := int(r.IncrementScale)
|
||||
mult := 1.0
|
||||
if scale > len(scales) {
|
||||
mult = math.Pow(10, float64(scale))
|
||||
} else {
|
||||
mult = scales[scale]
|
||||
}
|
||||
// We multiply x instead of dividing inc as it gives less rounding
|
||||
// issues.
|
||||
x *= mult
|
||||
x /= float64(r.Increment)
|
||||
x = r.Mode.roundFloat(x)
|
||||
x *= float64(r.Increment)
|
||||
x /= mult
|
||||
}
|
||||
|
||||
abs := x
|
||||
if x < 0 {
|
||||
d.Neg = true
|
||||
abs = -x
|
||||
}
|
||||
if math.IsInf(abs, 1) {
|
||||
d.Inf = true
|
||||
return
|
||||
}
|
||||
|
||||
// By default we get the exact decimal representation.
|
||||
verb := byte('g')
|
||||
prec := -1
|
||||
// As the strconv API does not return the rounding accuracy, we can only
|
||||
// round using ToNearestEven.
|
||||
if r.Mode == ToNearestEven {
|
||||
if n := r.RoundSignificantDigits(); n >= 0 {
|
||||
prec = n
|
||||
} else if n = r.RoundFractionDigits(); n >= 0 {
|
||||
prec = n
|
||||
verb = 'f'
|
||||
}
|
||||
} else {
|
||||
// TODO: At this point strconv's rounding is imprecise to the point that
|
||||
// it is not useable for this purpose.
|
||||
// See https://github.com/golang/go/issues/21714
|
||||
// If rounding is requested, we ask for a large number of digits and
|
||||
// round from there to simulate rounding only once.
|
||||
// Ideally we would have strconv export an AppendDigits that would take
|
||||
// a rounding mode and/or return an accuracy. Something like this would
|
||||
// work:
|
||||
// AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int)
|
||||
hasPrec := r.RoundSignificantDigits() >= 0
|
||||
hasScale := r.RoundFractionDigits() >= 0
|
||||
if hasPrec || hasScale {
|
||||
// prec is the number of mantissa bits plus some extra for safety.
|
||||
// We need at least the number of mantissa bits as decimals to
|
||||
// accurately represent the floating point without rounding, as each
|
||||
// bit requires one more decimal to represent: 0.5, 0.25, 0.125, ...
|
||||
prec = 60
|
||||
}
|
||||
}
|
||||
|
||||
b := strconv.AppendFloat(d.Digits[:0], abs, verb, prec, size)
|
||||
i := 0
|
||||
k := 0
|
||||
beforeDot := 1
|
||||
for i < len(b) {
|
||||
if c := b[i]; '0' <= c && c <= '9' {
|
||||
b[k] = c - '0'
|
||||
k++
|
||||
d.Exp += int32(beforeDot)
|
||||
} else if c == '.' {
|
||||
beforeDot = 0
|
||||
d.Exp = int32(k)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
d.Digits = b[:k]
|
||||
if i != len(b) {
|
||||
i += len("e")
|
||||
pSign := i
|
||||
exp := 0
|
||||
for i++; i < len(b); i++ {
|
||||
exp *= 10
|
||||
exp += int(b[i] - '0')
|
||||
}
|
||||
if b[pSign] == '-' {
|
||||
exp = -exp
|
||||
}
|
||||
d.Exp = int32(exp) + 1
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decimal) fillIntDigits(x uint64) {
|
||||
if cap(d.Digits) < maxIntDigits {
|
||||
d.Digits = d.buf[:]
|
||||
} else {
|
||||
d.Digits = d.buf[:maxIntDigits]
|
||||
}
|
||||
i := 0
|
||||
for ; x > 0; x /= 10 {
|
||||
d.Digits[i] = byte(x % 10)
|
||||
i++
|
||||
}
|
||||
d.Digits = d.Digits[:i]
|
||||
for p := 0; p < i; p++ {
|
||||
i--
|
||||
d.Digits[p], d.Digits[i] = d.Digits[i], d.Digits[p]
|
||||
}
|
||||
}
|
||||
|
||||
var scales [70]float64
|
||||
|
||||
func init() {
|
||||
x := 1.0
|
||||
for i := range scales {
|
||||
scales[i] = x
|
||||
x *= 10
|
||||
}
|
||||
}
|
329
vendor/golang.org/x/text/internal/number/decimal_test.go
generated
vendored
Normal file
329
vendor/golang.org/x/text/internal/number/decimal_test.go
generated
vendored
Normal file
|
@ -0,0 +1,329 @@
|
|||
// Copyright 2017 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 (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func mkfloat(num string) float64 {
|
||||
u, _ := strconv.ParseUint(num, 10, 32)
|
||||
return float64(u)
|
||||
}
|
||||
|
||||
// mkdec creates a decimal from a string. All ASCII digits are converted to
|
||||
// digits in the decimal. The dot is used to indicate the scale by which the
|
||||
// digits are shifted. Numbers may have an additional exponent or be the special
|
||||
// value NaN, Inf, or -Inf.
|
||||
func mkdec(num string) (d Decimal) {
|
||||
var r RoundingContext
|
||||
d.Convert(r, dec(num))
|
||||
return
|
||||
}
|
||||
|
||||
type dec string
|
||||
|
||||
func (s dec) Convert(d *Decimal, _ RoundingContext) {
|
||||
num := string(s)
|
||||
if num[0] == '-' {
|
||||
d.Neg = true
|
||||
num = num[1:]
|
||||
}
|
||||
switch num {
|
||||
case "NaN":
|
||||
d.NaN = true
|
||||
return
|
||||
case "Inf":
|
||||
d.Inf = true
|
||||
return
|
||||
}
|
||||
if p := strings.IndexAny(num, "eE"); p != -1 {
|
||||
i64, err := strconv.ParseInt(num[p+1:], 10, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
d.Exp = int32(i64)
|
||||
num = num[:p]
|
||||
}
|
||||
if p := strings.IndexByte(num, '.'); p != -1 {
|
||||
d.Exp += int32(p)
|
||||
num = num[:p] + num[p+1:]
|
||||
} else {
|
||||
d.Exp += int32(len(num))
|
||||
}
|
||||
d.Digits = []byte(num)
|
||||
for i := range d.Digits {
|
||||
d.Digits[i] -= '0'
|
||||
}
|
||||
*d = d.normalize()
|
||||
}
|
||||
|
||||
func byteNum(s string) []byte {
|
||||
b := make([]byte, len(s))
|
||||
for i := 0; i < len(s); i++ {
|
||||
if c := s[i]; '0' <= c && c <= '9' {
|
||||
b[i] = s[i] - '0'
|
||||
} else {
|
||||
b[i] = s[i] - 'a' + 10
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func strNum(s string) string {
|
||||
return string(byteNum(s))
|
||||
}
|
||||
|
||||
func TestDecimalString(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
x Decimal
|
||||
want string
|
||||
}{
|
||||
{want: "0"},
|
||||
{Decimal{digits: digits{Digits: nil, Exp: 1000}}, "0"}, // exponent of 1000 is ignored
|
||||
{Decimal{digits: digits{Digits: byteNum("12345"), Exp: 0}}, "0.12345"},
|
||||
{Decimal{digits: digits{Digits: byteNum("12345"), Exp: -3}}, "0.00012345"},
|
||||
{Decimal{digits: digits{Digits: byteNum("12345"), Exp: +3}}, "123.45"},
|
||||
{Decimal{digits: digits{Digits: byteNum("12345"), Exp: +10}}, "1234500000"},
|
||||
} {
|
||||
if got := test.x.String(); got != test.want {
|
||||
t.Errorf("%v == %q; want %q", test.x, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRounding(t *testing.T) {
|
||||
testCases := []struct {
|
||||
x string
|
||||
n int
|
||||
// modes is the result for modes. Signs are left out of the result.
|
||||
// The results are stored in the following order:
|
||||
// zero, negInf
|
||||
// nearZero, nearEven, nearAway
|
||||
// away, posInf
|
||||
modes [numModes]string
|
||||
}{
|
||||
{"0", 1, [numModes]string{
|
||||
"0", "0",
|
||||
"0", "0", "0",
|
||||
"0", "0"}},
|
||||
{"1", 1, [numModes]string{
|
||||
"1", "1",
|
||||
"1", "1", "1",
|
||||
"1", "1"}},
|
||||
{"5", 1, [numModes]string{
|
||||
"5", "5",
|
||||
"5", "5", "5",
|
||||
"5", "5"}},
|
||||
{"15", 1, [numModes]string{
|
||||
"10", "10",
|
||||
"10", "20", "20",
|
||||
"20", "20"}},
|
||||
{"45", 1, [numModes]string{
|
||||
"40", "40",
|
||||
"40", "40", "50",
|
||||
"50", "50"}},
|
||||
{"95", 1, [numModes]string{
|
||||
"90", "90",
|
||||
"90", "100", "100",
|
||||
"100", "100"}},
|
||||
|
||||
{"12344999", 4, [numModes]string{
|
||||
"12340000", "12340000",
|
||||
"12340000", "12340000", "12340000",
|
||||
"12350000", "12350000"}},
|
||||
{"12345000", 4, [numModes]string{
|
||||
"12340000", "12340000",
|
||||
"12340000", "12340000", "12350000",
|
||||
"12350000", "12350000"}},
|
||||
{"12345001", 4, [numModes]string{
|
||||
"12340000", "12340000",
|
||||
"12350000", "12350000", "12350000",
|
||||
"12350000", "12350000"}},
|
||||
{"12345100", 4, [numModes]string{
|
||||
"12340000", "12340000",
|
||||
"12350000", "12350000", "12350000",
|
||||
"12350000", "12350000"}},
|
||||
{"23454999", 4, [numModes]string{
|
||||
"23450000", "23450000",
|
||||
"23450000", "23450000", "23450000",
|
||||
"23460000", "23460000"}},
|
||||
{"23455000", 4, [numModes]string{
|
||||
"23450000", "23450000",
|
||||
"23450000", "23460000", "23460000",
|
||||
"23460000", "23460000"}},
|
||||
{"23455001", 4, [numModes]string{
|
||||
"23450000", "23450000",
|
||||
"23460000", "23460000", "23460000",
|
||||
"23460000", "23460000"}},
|
||||
{"23455100", 4, [numModes]string{
|
||||
"23450000", "23450000",
|
||||
"23460000", "23460000", "23460000",
|
||||
"23460000", "23460000"}},
|
||||
|
||||
{"99994999", 4, [numModes]string{
|
||||
"99990000", "99990000",
|
||||
"99990000", "99990000", "99990000",
|
||||
"100000000", "100000000"}},
|
||||
{"99995000", 4, [numModes]string{
|
||||
"99990000", "99990000",
|
||||
"99990000", "100000000", "100000000",
|
||||
"100000000", "100000000"}},
|
||||
{"99999999", 4, [numModes]string{
|
||||
"99990000", "99990000",
|
||||
"100000000", "100000000", "100000000",
|
||||
"100000000", "100000000"}},
|
||||
|
||||
{"12994999", 4, [numModes]string{
|
||||
"12990000", "12990000",
|
||||
"12990000", "12990000", "12990000",
|
||||
"13000000", "13000000"}},
|
||||
{"12995000", 4, [numModes]string{
|
||||
"12990000", "12990000",
|
||||
"12990000", "13000000", "13000000",
|
||||
"13000000", "13000000"}},
|
||||
{"12999999", 4, [numModes]string{
|
||||
"12990000", "12990000",
|
||||
"13000000", "13000000", "13000000",
|
||||
"13000000", "13000000"}},
|
||||
}
|
||||
modes := []RoundingMode{
|
||||
ToZero, ToNegativeInf,
|
||||
ToNearestZero, ToNearestEven, ToNearestAway,
|
||||
AwayFromZero, ToPositiveInf,
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
// Create negative counterpart tests: the sign is reversed and
|
||||
// ToPositiveInf and ToNegativeInf swapped.
|
||||
negModes := tc.modes
|
||||
negModes[1], negModes[6] = negModes[6], negModes[1]
|
||||
for i, res := range negModes {
|
||||
negModes[i] = "-" + res
|
||||
}
|
||||
for i, m := range modes {
|
||||
t.Run(fmt.Sprintf("x:%s/n:%d/%s", tc.x, tc.n, m), func(t *testing.T) {
|
||||
d := mkdec(tc.x)
|
||||
d.round(m, tc.n)
|
||||
if got := d.String(); got != tc.modes[i] {
|
||||
t.Errorf("pos decimal: got %q; want %q", d.String(), tc.modes[i])
|
||||
}
|
||||
|
||||
mult := math.Pow(10, float64(len(tc.x)-tc.n))
|
||||
f := mkfloat(tc.x)
|
||||
f = m.roundFloat(f/mult) * mult
|
||||
if got := fmt.Sprintf("%.0f", f); got != tc.modes[i] {
|
||||
t.Errorf("pos float: got %q; want %q", got, tc.modes[i])
|
||||
}
|
||||
|
||||
// Test the negative case. This is the same as the positive
|
||||
// case, but with ToPositiveInf and ToNegativeInf swapped.
|
||||
d = mkdec(tc.x)
|
||||
d.Neg = true
|
||||
d.round(m, tc.n)
|
||||
if got, want := d.String(), negModes[i]; got != want {
|
||||
t.Errorf("neg decimal: got %q; want %q", d.String(), want)
|
||||
}
|
||||
|
||||
f = -mkfloat(tc.x)
|
||||
f = m.roundFloat(f/mult) * mult
|
||||
if got := fmt.Sprintf("%.0f", f); got != negModes[i] {
|
||||
t.Errorf("neg float: got %q; want %q", got, negModes[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvert(t *testing.T) {
|
||||
scale2 := RoundingContext{}
|
||||
scale2.SetScale(2)
|
||||
scale2away := RoundingContext{Mode: AwayFromZero}
|
||||
scale2away.SetScale(2)
|
||||
inc0_05 := RoundingContext{Increment: 5, IncrementScale: 2}
|
||||
inc0_05.SetScale(2)
|
||||
inc50 := RoundingContext{Increment: 50}
|
||||
prec3 := RoundingContext{}
|
||||
prec3.SetPrecision(3)
|
||||
roundShift := RoundingContext{DigitShift: 2, MaxFractionDigits: 2}
|
||||
testCases := []struct {
|
||||
x interface{}
|
||||
rc RoundingContext
|
||||
out string
|
||||
}{
|
||||
{-0.001, scale2, "-0.00"},
|
||||
{0.1234, prec3, "0.123"},
|
||||
{1234.0, prec3, "1230"},
|
||||
{1.2345e10, prec3, "12300000000"},
|
||||
|
||||
{int8(-34), scale2, "-34"},
|
||||
{int16(-234), scale2, "-234"},
|
||||
{int32(-234), scale2, "-234"},
|
||||
{int64(-234), scale2, "-234"},
|
||||
{int(-234), scale2, "-234"},
|
||||
{uint8(234), scale2, "234"},
|
||||
{uint16(234), scale2, "234"},
|
||||
{uint32(234), scale2, "234"},
|
||||
{uint64(234), scale2, "234"},
|
||||
{uint(234), scale2, "234"},
|
||||
{-1e9, scale2, "-1000000000.00"},
|
||||
// The following two causes this result to have a lot of digits:
|
||||
// 1) 0.234 cannot be accurately represented as a float64, and
|
||||
// 2) as strconv does not support the rounding AwayFromZero, Convert
|
||||
// leaves the rounding to caller.
|
||||
{0.234, scale2away,
|
||||
"0.2340000000000000135447209004269097931683063507080078125"},
|
||||
|
||||
{0.0249, inc0_05, "0.00"},
|
||||
{0.025, inc0_05, "0.00"},
|
||||
{0.0251, inc0_05, "0.05"},
|
||||
{0.03, inc0_05, "0.05"},
|
||||
{0.049, inc0_05, "0.05"},
|
||||
{0.05, inc0_05, "0.05"},
|
||||
{0.051, inc0_05, "0.05"},
|
||||
{0.0749, inc0_05, "0.05"},
|
||||
{0.075, inc0_05, "0.10"},
|
||||
{0.0751, inc0_05, "0.10"},
|
||||
{324, inc50, "300"},
|
||||
{325, inc50, "300"},
|
||||
{326, inc50, "350"},
|
||||
{349, inc50, "350"},
|
||||
{350, inc50, "350"},
|
||||
{351, inc50, "350"},
|
||||
{374, inc50, "350"},
|
||||
{375, inc50, "400"},
|
||||
{376, inc50, "400"},
|
||||
|
||||
// Here the scale is 2, but the digits get shifted left. As we use
|
||||
// AppendFloat to do the rounding an exta 0 gets added.
|
||||
{0.123, roundShift, "0.1230"},
|
||||
|
||||
{converter(3), scale2, "100"},
|
||||
|
||||
{math.Inf(1), inc50, "Inf"},
|
||||
{math.Inf(-1), inc50, "-Inf"},
|
||||
{math.NaN(), inc50, "NaN"},
|
||||
{"clearly not a number", scale2, "NaN"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
var d Decimal
|
||||
t.Run(fmt.Sprintf("%T:%v-%v", tc.x, tc.x, tc.rc), func(t *testing.T) {
|
||||
d.Convert(tc.rc, tc.x)
|
||||
if got := d.String(); got != tc.out {
|
||||
t.Errorf("got %q; want %q", got, tc.out)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type converter int
|
||||
|
||||
func (c converter) Convert(d *Decimal, r RoundingContext) {
|
||||
d.Digits = append(d.Digits, 1, 0, 0)
|
||||
d.Exp = 3
|
||||
}
|
540
vendor/golang.org/x/text/internal/number/format.go
generated
vendored
Normal file
540
vendor/golang.org/x/text/internal/number/format.go
generated
vendored
Normal file
|
@ -0,0 +1,540 @@
|
|||
// Copyright 2017 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 (
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// TODO:
|
||||
// - grouping of fractions
|
||||
// - allow user-defined superscript notation (such as <sup>4</sup>)
|
||||
// - same for non-breaking spaces, like
|
||||
|
||||
// A VisibleDigits computes digits, comma placement and trailing zeros as they
|
||||
// will be shown to the user.
|
||||
type VisibleDigits interface {
|
||||
Digits(buf []byte, t language.Tag, scale int) Digits
|
||||
// TODO: Do we also need to add the verb or pass a format.State?
|
||||
}
|
||||
|
||||
// Formatting proceeds along the following lines:
|
||||
// 0) Compose rounding information from format and context.
|
||||
// 1) Convert a number into a Decimal.
|
||||
// 2) Sanitize Decimal by adding trailing zeros, removing leading digits, and
|
||||
// (non-increment) rounding. The Decimal that results from this is suitable
|
||||
// for determining the plural form.
|
||||
// 3) Render the Decimal in the localized form.
|
||||
|
||||
// Formatter contains all the information needed to render a number.
|
||||
type Formatter struct {
|
||||
Pattern
|
||||
Info
|
||||
}
|
||||
|
||||
func (f *Formatter) init(t language.Tag, index []uint8) {
|
||||
f.Info = InfoFromTag(t)
|
||||
for ; ; t = t.Parent() {
|
||||
if ci, ok := language.CompactIndex(t); ok {
|
||||
f.Pattern = formats[index[ci]]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InitPattern initializes a Formatter for the given Pattern.
|
||||
func (f *Formatter) InitPattern(t language.Tag, pat *Pattern) {
|
||||
f.Info = InfoFromTag(t)
|
||||
f.Pattern = *pat
|
||||
}
|
||||
|
||||
// InitDecimal initializes a Formatter using the default Pattern for the given
|
||||
// language.
|
||||
func (f *Formatter) InitDecimal(t language.Tag) {
|
||||
f.init(t, tagToDecimal)
|
||||
}
|
||||
|
||||
// InitScientific initializes a Formatter using the default Pattern for the
|
||||
// given language.
|
||||
func (f *Formatter) InitScientific(t language.Tag) {
|
||||
f.init(t, tagToScientific)
|
||||
f.Pattern.MinFractionDigits = 0
|
||||
f.Pattern.MaxFractionDigits = -1
|
||||
}
|
||||
|
||||
// InitEngineering initializes a Formatter using the default Pattern for the
|
||||
// given language.
|
||||
func (f *Formatter) InitEngineering(t language.Tag) {
|
||||
f.init(t, tagToScientific)
|
||||
f.Pattern.MinFractionDigits = 0
|
||||
f.Pattern.MaxFractionDigits = -1
|
||||
f.Pattern.MaxIntegerDigits = 3
|
||||
f.Pattern.MinIntegerDigits = 1
|
||||
}
|
||||
|
||||
// InitPercent initializes a Formatter using the default Pattern for the given
|
||||
// language.
|
||||
func (f *Formatter) InitPercent(t language.Tag) {
|
||||
f.init(t, tagToPercent)
|
||||
}
|
||||
|
||||
// InitPerMille initializes a Formatter using the default Pattern for the given
|
||||
// language.
|
||||
func (f *Formatter) InitPerMille(t language.Tag) {
|
||||
f.init(t, tagToPercent)
|
||||
f.Pattern.DigitShift = 3
|
||||
}
|
||||
|
||||
func (f *Formatter) Append(dst []byte, x interface{}) []byte {
|
||||
var d Decimal
|
||||
r := f.RoundingContext
|
||||
d.Convert(r, x)
|
||||
return f.Render(dst, FormatDigits(&d, r))
|
||||
}
|
||||
|
||||
func FormatDigits(d *Decimal, r RoundingContext) Digits {
|
||||
if r.isScientific() {
|
||||
return scientificVisibleDigits(r, d)
|
||||
}
|
||||
return decimalVisibleDigits(r, d)
|
||||
}
|
||||
|
||||
func (f *Formatter) Format(dst []byte, d *Decimal) []byte {
|
||||
return f.Render(dst, FormatDigits(d, f.RoundingContext))
|
||||
}
|
||||
|
||||
func (f *Formatter) Render(dst []byte, d Digits) []byte {
|
||||
var result []byte
|
||||
var postPrefix, preSuffix int
|
||||
if d.IsScientific {
|
||||
result, postPrefix, preSuffix = appendScientific(dst, f, &d)
|
||||
} else {
|
||||
result, postPrefix, preSuffix = appendDecimal(dst, f, &d)
|
||||
}
|
||||
if f.PadRune == 0 {
|
||||
return result
|
||||
}
|
||||
width := int(f.FormatWidth)
|
||||
if count := utf8.RuneCount(result); count < width {
|
||||
insertPos := 0
|
||||
switch f.Flags & PadMask {
|
||||
case PadAfterPrefix:
|
||||
insertPos = postPrefix
|
||||
case PadBeforeSuffix:
|
||||
insertPos = preSuffix
|
||||
case PadAfterSuffix:
|
||||
insertPos = len(result)
|
||||
}
|
||||
num := width - count
|
||||
pad := [utf8.UTFMax]byte{' '}
|
||||
sz := 1
|
||||
if r := f.PadRune; r != 0 {
|
||||
sz = utf8.EncodeRune(pad[:], r)
|
||||
}
|
||||
extra := sz * num
|
||||
if n := len(result) + extra; n < cap(result) {
|
||||
result = result[:n]
|
||||
copy(result[insertPos+extra:], result[insertPos:])
|
||||
} else {
|
||||
buf := make([]byte, n)
|
||||
copy(buf, result[:insertPos])
|
||||
copy(buf[insertPos+extra:], result[insertPos:])
|
||||
result = buf
|
||||
}
|
||||
for ; num > 0; num-- {
|
||||
insertPos += copy(result[insertPos:], pad[:sz])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// decimalVisibleDigits converts d according to the RoundingContext. Note that
|
||||
// the exponent may change as a result of this operation.
|
||||
func decimalVisibleDigits(r RoundingContext, d *Decimal) Digits {
|
||||
if d.NaN || d.Inf {
|
||||
return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
|
||||
}
|
||||
n := Digits{digits: d.normalize().digits}
|
||||
|
||||
exp := n.Exp
|
||||
exp += int32(r.DigitShift)
|
||||
|
||||
// Cap integer digits. Remove *most-significant* digits.
|
||||
if r.MaxIntegerDigits > 0 {
|
||||
if p := int(exp) - int(r.MaxIntegerDigits); p > 0 {
|
||||
if p > len(n.Digits) {
|
||||
p = len(n.Digits)
|
||||
}
|
||||
if n.Digits = n.Digits[p:]; len(n.Digits) == 0 {
|
||||
exp = 0
|
||||
} else {
|
||||
exp -= int32(p)
|
||||
}
|
||||
// Strip leading zeros.
|
||||
for len(n.Digits) > 0 && n.Digits[0] == 0 {
|
||||
n.Digits = n.Digits[1:]
|
||||
exp--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rounding if not already done by Convert.
|
||||
p := len(n.Digits)
|
||||
if maxSig := int(r.MaxSignificantDigits); maxSig > 0 {
|
||||
p = maxSig
|
||||
}
|
||||
if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 {
|
||||
if cap := int(exp) + maxFrac; cap < p {
|
||||
p = int(exp) + maxFrac
|
||||
}
|
||||
if p < 0 {
|
||||
p = 0
|
||||
}
|
||||
}
|
||||
n.round(r.Mode, p)
|
||||
|
||||
// set End (trailing zeros)
|
||||
n.End = int32(len(n.Digits))
|
||||
if n.End == 0 {
|
||||
exp = 0
|
||||
if r.MinFractionDigits > 0 {
|
||||
n.End = int32(r.MinFractionDigits)
|
||||
}
|
||||
if p := int32(r.MinSignificantDigits) - 1; p > n.End {
|
||||
n.End = p
|
||||
}
|
||||
} else {
|
||||
if end := exp + int32(r.MinFractionDigits); end > n.End {
|
||||
n.End = end
|
||||
}
|
||||
if n.End < int32(r.MinSignificantDigits) {
|
||||
n.End = int32(r.MinSignificantDigits)
|
||||
}
|
||||
}
|
||||
n.Exp = exp
|
||||
return n
|
||||
}
|
||||
|
||||
// appendDecimal appends a formatted number to dst. It returns two possible
|
||||
// insertion points for padding.
|
||||
func appendDecimal(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) {
|
||||
if dst, ok := f.renderSpecial(dst, n); ok {
|
||||
return dst, 0, len(dst)
|
||||
}
|
||||
digits := n.Digits
|
||||
exp := n.Exp
|
||||
|
||||
// Split in integer and fraction part.
|
||||
var intDigits, fracDigits []byte
|
||||
numInt := 0
|
||||
numFrac := int(n.End - n.Exp)
|
||||
if exp > 0 {
|
||||
numInt = int(exp)
|
||||
if int(exp) >= len(digits) { // ddddd | ddddd00
|
||||
intDigits = digits
|
||||
} else { // ddd.dd
|
||||
intDigits = digits[:exp]
|
||||
fracDigits = digits[exp:]
|
||||
}
|
||||
} else {
|
||||
fracDigits = digits
|
||||
}
|
||||
|
||||
neg := n.Neg
|
||||
affix, suffix := f.getAffixes(neg)
|
||||
dst = appendAffix(dst, f, affix, neg)
|
||||
savedLen := len(dst)
|
||||
|
||||
minInt := int(f.MinIntegerDigits)
|
||||
if minInt == 0 && f.MinSignificantDigits > 0 {
|
||||
minInt = 1
|
||||
}
|
||||
// add leading zeros
|
||||
for i := minInt; i > numInt; i-- {
|
||||
dst = f.AppendDigit(dst, 0)
|
||||
if f.needsSep(i) {
|
||||
dst = append(dst, f.Symbol(SymGroup)...)
|
||||
}
|
||||
}
|
||||
i := 0
|
||||
for ; i < len(intDigits); i++ {
|
||||
dst = f.AppendDigit(dst, intDigits[i])
|
||||
if f.needsSep(numInt - i) {
|
||||
dst = append(dst, f.Symbol(SymGroup)...)
|
||||
}
|
||||
}
|
||||
for ; i < numInt; i++ {
|
||||
dst = f.AppendDigit(dst, 0)
|
||||
if f.needsSep(numInt - i) {
|
||||
dst = append(dst, f.Symbol(SymGroup)...)
|
||||
}
|
||||
}
|
||||
|
||||
if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
|
||||
dst = append(dst, f.Symbol(SymDecimal)...)
|
||||
}
|
||||
// Add trailing zeros
|
||||
i = 0
|
||||
for n := -int(n.Exp); i < n; i++ {
|
||||
dst = f.AppendDigit(dst, 0)
|
||||
}
|
||||
for _, d := range fracDigits {
|
||||
i++
|
||||
dst = f.AppendDigit(dst, d)
|
||||
}
|
||||
for ; i < numFrac; i++ {
|
||||
dst = f.AppendDigit(dst, 0)
|
||||
}
|
||||
return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
|
||||
}
|
||||
|
||||
func scientificVisibleDigits(r RoundingContext, d *Decimal) Digits {
|
||||
if d.NaN || d.Inf {
|
||||
return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
|
||||
}
|
||||
n := Digits{digits: d.normalize().digits, IsScientific: true}
|
||||
|
||||
// Normalize to have at least one digit. This simplifies engineering
|
||||
// notation.
|
||||
if len(n.Digits) == 0 {
|
||||
n.Digits = append(n.Digits, 0)
|
||||
n.Exp = 1
|
||||
}
|
||||
|
||||
// Significant digits are transformed by the parser for scientific notation
|
||||
// and do not need to be handled here.
|
||||
maxInt, numInt := int(r.MaxIntegerDigits), int(r.MinIntegerDigits)
|
||||
if numInt == 0 {
|
||||
numInt = 1
|
||||
}
|
||||
|
||||
// If a maximum number of integers is specified, the minimum must be 1
|
||||
// and the exponent is grouped by this number (e.g. for engineering)
|
||||
if maxInt > numInt {
|
||||
// Correct the exponent to reflect a single integer digit.
|
||||
numInt = 1
|
||||
// engineering
|
||||
// 0.01234 ([12345]e-1) -> 1.2345e-2 12.345e-3
|
||||
// 12345 ([12345]e+5) -> 1.2345e4 12.345e3
|
||||
d := int(n.Exp-1) % maxInt
|
||||
if d < 0 {
|
||||
d += maxInt
|
||||
}
|
||||
numInt += d
|
||||
}
|
||||
|
||||
p := len(n.Digits)
|
||||
if maxSig := int(r.MaxSignificantDigits); maxSig > 0 {
|
||||
p = maxSig
|
||||
}
|
||||
if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 && numInt+maxFrac < p {
|
||||
p = numInt + maxFrac
|
||||
}
|
||||
n.round(r.Mode, p)
|
||||
|
||||
n.Comma = uint8(numInt)
|
||||
n.End = int32(len(n.Digits))
|
||||
if minSig := int32(r.MinFractionDigits) + int32(numInt); n.End < minSig {
|
||||
n.End = minSig
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// appendScientific appends a formatted number to dst. It returns two possible
|
||||
// insertion points for padding.
|
||||
func appendScientific(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) {
|
||||
if dst, ok := f.renderSpecial(dst, n); ok {
|
||||
return dst, 0, 0
|
||||
}
|
||||
digits := n.Digits
|
||||
numInt := int(n.Comma)
|
||||
numFrac := int(n.End) - int(n.Comma)
|
||||
|
||||
var intDigits, fracDigits []byte
|
||||
if numInt <= len(digits) {
|
||||
intDigits = digits[:numInt]
|
||||
fracDigits = digits[numInt:]
|
||||
} else {
|
||||
intDigits = digits
|
||||
}
|
||||
neg := n.Neg
|
||||
affix, suffix := f.getAffixes(neg)
|
||||
dst = appendAffix(dst, f, affix, neg)
|
||||
savedLen := len(dst)
|
||||
|
||||
i := 0
|
||||
for ; i < len(intDigits); i++ {
|
||||
dst = f.AppendDigit(dst, intDigits[i])
|
||||
if f.needsSep(numInt - i) {
|
||||
dst = append(dst, f.Symbol(SymGroup)...)
|
||||
}
|
||||
}
|
||||
for ; i < numInt; i++ {
|
||||
dst = f.AppendDigit(dst, 0)
|
||||
if f.needsSep(numInt - i) {
|
||||
dst = append(dst, f.Symbol(SymGroup)...)
|
||||
}
|
||||
}
|
||||
|
||||
if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
|
||||
dst = append(dst, f.Symbol(SymDecimal)...)
|
||||
}
|
||||
i = 0
|
||||
for ; i < len(fracDigits); i++ {
|
||||
dst = f.AppendDigit(dst, fracDigits[i])
|
||||
}
|
||||
for ; i < numFrac; i++ {
|
||||
dst = f.AppendDigit(dst, 0)
|
||||
}
|
||||
|
||||
// exp
|
||||
buf := [12]byte{}
|
||||
// TODO: use exponential if superscripting is not available (no Latin
|
||||
// numbers or no tags) and use exponential in all other cases.
|
||||
exp := n.Exp - int32(n.Comma)
|
||||
exponential := f.Symbol(SymExponential)
|
||||
if exponential == "E" {
|
||||
dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
|
||||
dst = append(dst, f.Symbol(SymSuperscriptingExponent)...)
|
||||
dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
|
||||
dst = f.AppendDigit(dst, 1)
|
||||
dst = f.AppendDigit(dst, 0)
|
||||
switch {
|
||||
case exp < 0:
|
||||
dst = append(dst, superMinus...)
|
||||
exp = -exp
|
||||
case f.Flags&AlwaysExpSign != 0:
|
||||
dst = append(dst, superPlus...)
|
||||
}
|
||||
b = strconv.AppendUint(buf[:0], uint64(exp), 10)
|
||||
for i := len(b); i < int(f.MinExponentDigits); i++ {
|
||||
dst = append(dst, superDigits[0]...)
|
||||
}
|
||||
for _, c := range b {
|
||||
dst = append(dst, superDigits[c-'0']...)
|
||||
}
|
||||
} else {
|
||||
dst = append(dst, exponential...)
|
||||
switch {
|
||||
case exp < 0:
|
||||
dst = append(dst, f.Symbol(SymMinusSign)...)
|
||||
exp = -exp
|
||||
case f.Flags&AlwaysExpSign != 0:
|
||||
dst = append(dst, f.Symbol(SymPlusSign)...)
|
||||
}
|
||||
b = strconv.AppendUint(buf[:0], uint64(exp), 10)
|
||||
for i := len(b); i < int(f.MinExponentDigits); i++ {
|
||||
dst = f.AppendDigit(dst, 0)
|
||||
}
|
||||
for _, c := range b {
|
||||
dst = f.AppendDigit(dst, c-'0')
|
||||
}
|
||||
}
|
||||
return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
|
||||
}
|
||||
|
||||
const (
|
||||
superMinus = "\u207B" // SUPERSCRIPT HYPHEN-MINUS
|
||||
superPlus = "\u207A" // SUPERSCRIPT PLUS SIGN
|
||||
)
|
||||
|
||||
var (
|
||||
// Note: the digits are not sequential!!!
|
||||
superDigits = []string{
|
||||
"\u2070", // SUPERSCRIPT DIGIT ZERO
|
||||
"\u00B9", // SUPERSCRIPT DIGIT ONE
|
||||
"\u00B2", // SUPERSCRIPT DIGIT TWO
|
||||
"\u00B3", // SUPERSCRIPT DIGIT THREE
|
||||
"\u2074", // SUPERSCRIPT DIGIT FOUR
|
||||
"\u2075", // SUPERSCRIPT DIGIT FIVE
|
||||
"\u2076", // SUPERSCRIPT DIGIT SIX
|
||||
"\u2077", // SUPERSCRIPT DIGIT SEVEN
|
||||
"\u2078", // SUPERSCRIPT DIGIT EIGHT
|
||||
"\u2079", // SUPERSCRIPT DIGIT NINE
|
||||
}
|
||||
)
|
||||
|
||||
func (f *Formatter) getAffixes(neg bool) (affix, suffix string) {
|
||||
str := f.Affix
|
||||
if str != "" {
|
||||
if f.NegOffset > 0 {
|
||||
if neg {
|
||||
str = str[f.NegOffset:]
|
||||
} else {
|
||||
str = str[:f.NegOffset]
|
||||
}
|
||||
}
|
||||
sufStart := 1 + str[0]
|
||||
affix = str[1:sufStart]
|
||||
suffix = str[sufStart+1:]
|
||||
}
|
||||
// TODO: introduce a NeedNeg sign to indicate if the left pattern already
|
||||
// has a sign marked?
|
||||
if f.NegOffset == 0 && (neg || f.Flags&AlwaysSign != 0) {
|
||||
affix = "-" + affix
|
||||
}
|
||||
return affix, suffix
|
||||
}
|
||||
|
||||
func (f *Formatter) renderSpecial(dst []byte, d *Digits) (b []byte, ok bool) {
|
||||
if d.NaN {
|
||||
return fmtNaN(dst, f), true
|
||||
}
|
||||
if d.Inf {
|
||||
return fmtInfinite(dst, f, d), true
|
||||
}
|
||||
return dst, false
|
||||
}
|
||||
|
||||
func fmtNaN(dst []byte, f *Formatter) []byte {
|
||||
return append(dst, f.Symbol(SymNan)...)
|
||||
}
|
||||
|
||||
func fmtInfinite(dst []byte, f *Formatter, d *Digits) []byte {
|
||||
affix, suffix := f.getAffixes(d.Neg)
|
||||
dst = appendAffix(dst, f, affix, d.Neg)
|
||||
dst = append(dst, f.Symbol(SymInfinity)...)
|
||||
dst = appendAffix(dst, f, suffix, d.Neg)
|
||||
return dst
|
||||
}
|
||||
|
||||
func appendAffix(dst []byte, f *Formatter, affix string, neg bool) []byte {
|
||||
quoting := false
|
||||
escaping := false
|
||||
for _, r := range affix {
|
||||
switch {
|
||||
case escaping:
|
||||
// escaping occurs both inside and outside of quotes
|
||||
dst = append(dst, string(r)...)
|
||||
escaping = false
|
||||
case r == '\\':
|
||||
escaping = true
|
||||
case r == '\'':
|
||||
quoting = !quoting
|
||||
case quoting:
|
||||
dst = append(dst, string(r)...)
|
||||
case r == '%':
|
||||
if f.DigitShift == 3 {
|
||||
dst = append(dst, f.Symbol(SymPerMille)...)
|
||||
} else {
|
||||
dst = append(dst, f.Symbol(SymPercentSign)...)
|
||||
}
|
||||
case r == '-' || r == '+':
|
||||
if neg {
|
||||
dst = append(dst, f.Symbol(SymMinusSign)...)
|
||||
} else if f.Flags&ElideSign == 0 {
|
||||
dst = append(dst, f.Symbol(SymPlusSign)...)
|
||||
} else {
|
||||
dst = append(dst, ' ')
|
||||
}
|
||||
default:
|
||||
dst = append(dst, string(r)...)
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
522
vendor/golang.org/x/text/internal/number/format_test.go
generated
vendored
Normal file
522
vendor/golang.org/x/text/internal/number/format_test.go
generated
vendored
Normal file
|
@ -0,0 +1,522 @@
|
|||
// Copyright 2017 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 (
|
||||
"fmt"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func TestAppendDecimal(t *testing.T) {
|
||||
type pairs map[string]string // alternates with decimal input and result
|
||||
|
||||
testCases := []struct {
|
||||
pattern string
|
||||
// We want to be able to test some forms of patterns that cannot be
|
||||
// represented as a string.
|
||||
pat *Pattern
|
||||
|
||||
test pairs
|
||||
}{{
|
||||
pattern: "0",
|
||||
test: pairs{
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"-1": "-1",
|
||||
".00": "0",
|
||||
"10.": "10",
|
||||
"12": "12",
|
||||
"1.2": "1",
|
||||
"NaN": "NaN",
|
||||
"-Inf": "-∞",
|
||||
},
|
||||
}, {
|
||||
pattern: "+0;+0",
|
||||
test: pairs{
|
||||
"0": "+0",
|
||||
"1": "+1",
|
||||
"-1": "-1",
|
||||
".00": "+0",
|
||||
"10.": "+10",
|
||||
"12": "+12",
|
||||
"1.2": "+1",
|
||||
"NaN": "NaN",
|
||||
"-Inf": "-∞",
|
||||
"Inf": "+∞",
|
||||
},
|
||||
}, {
|
||||
pattern: "0 +;0 +",
|
||||
test: pairs{
|
||||
"0": "0 +",
|
||||
"1": "1 +",
|
||||
"-1": "1 -",
|
||||
".00": "0 +",
|
||||
},
|
||||
}, {
|
||||
pattern: "0;0-",
|
||||
test: pairs{
|
||||
"-1": "1-",
|
||||
"NaN": "NaN",
|
||||
"-Inf": "∞-",
|
||||
"Inf": "∞",
|
||||
},
|
||||
}, {
|
||||
pattern: "0000",
|
||||
test: pairs{
|
||||
"0": "0000",
|
||||
"1": "0001",
|
||||
"12": "0012",
|
||||
"12345": "12345",
|
||||
},
|
||||
}, {
|
||||
pattern: ".0",
|
||||
test: pairs{
|
||||
"0": ".0",
|
||||
"1": "1.0",
|
||||
"1.2": "1.2",
|
||||
"1.2345": "1.2",
|
||||
},
|
||||
}, {
|
||||
pattern: "#.0",
|
||||
test: pairs{
|
||||
"0": ".0",
|
||||
},
|
||||
}, {
|
||||
pattern: "#.0#",
|
||||
test: pairs{
|
||||
"0": ".0",
|
||||
"1": "1.0",
|
||||
},
|
||||
}, {
|
||||
pattern: "0.0#",
|
||||
test: pairs{
|
||||
"0": "0.0",
|
||||
},
|
||||
}, {
|
||||
pattern: "#0.###",
|
||||
test: pairs{
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"1.2": "1.2",
|
||||
"1.2345": "1.234", // rounding should have been done earlier
|
||||
"1234.5": "1234.5",
|
||||
"1234.567": "1234.567",
|
||||
},
|
||||
}, {
|
||||
pattern: "#0.######",
|
||||
test: pairs{
|
||||
"0": "0",
|
||||
"1234.5678": "1234.5678",
|
||||
"0.123456789": "0.123457",
|
||||
"NaN": "NaN",
|
||||
"Inf": "∞",
|
||||
},
|
||||
|
||||
// Test separators.
|
||||
}, {
|
||||
pattern: "#,#.00",
|
||||
test: pairs{
|
||||
"100": "1,0,0.00",
|
||||
},
|
||||
}, {
|
||||
pattern: "#,0.##",
|
||||
test: pairs{
|
||||
"10": "1,0",
|
||||
},
|
||||
}, {
|
||||
pattern: "#,0",
|
||||
test: pairs{
|
||||
"10": "1,0",
|
||||
},
|
||||
}, {
|
||||
pattern: "#,##,#.00",
|
||||
test: pairs{
|
||||
"1000": "1,00,0.00",
|
||||
},
|
||||
}, {
|
||||
pattern: "#,##0.###",
|
||||
test: pairs{
|
||||
"0": "0",
|
||||
"1234.5678": "1,234.568",
|
||||
"0.123456789": "0.123",
|
||||
},
|
||||
}, {
|
||||
pattern: "#,##,##0.###",
|
||||
test: pairs{
|
||||
"0": "0",
|
||||
"123456789012": "1,23,45,67,89,012",
|
||||
"0.123456789": "0.123",
|
||||
},
|
||||
}, {
|
||||
pattern: "0,00,000.###",
|
||||
test: pairs{
|
||||
"0": "0,00,000",
|
||||
"123456789012": "1,23,45,67,89,012",
|
||||
"12.3456789": "0,00,012.346",
|
||||
"0.123456789": "0,00,000.123",
|
||||
},
|
||||
|
||||
// Support for ill-formed patterns.
|
||||
}, {
|
||||
pattern: "#",
|
||||
test: pairs{
|
||||
".00": "", // This is the behavior of fmt.
|
||||
"0": "", // This is the behavior of fmt.
|
||||
"1": "1",
|
||||
"10.": "10",
|
||||
},
|
||||
}, {
|
||||
pattern: ".#",
|
||||
test: pairs{
|
||||
"0": "", // This is the behavior of fmt.
|
||||
"1": "1",
|
||||
"1.2": "1.2",
|
||||
"1.2345": "1.2",
|
||||
},
|
||||
}, {
|
||||
pattern: "#,#.##",
|
||||
test: pairs{
|
||||
"10": "1,0",
|
||||
},
|
||||
}, {
|
||||
pattern: "#,#",
|
||||
test: pairs{
|
||||
"10": "1,0",
|
||||
},
|
||||
|
||||
// Special patterns
|
||||
}, {
|
||||
pattern: "#,max_int=2",
|
||||
pat: &Pattern{
|
||||
RoundingContext: RoundingContext{
|
||||
MaxIntegerDigits: 2,
|
||||
},
|
||||
},
|
||||
test: pairs{
|
||||
"2017": "17",
|
||||
},
|
||||
}, {
|
||||
pattern: "0,max_int=2",
|
||||
pat: &Pattern{
|
||||
RoundingContext: RoundingContext{
|
||||
MaxIntegerDigits: 2,
|
||||
MinIntegerDigits: 1,
|
||||
},
|
||||
},
|
||||
test: pairs{
|
||||
"2000": "0",
|
||||
"2001": "1",
|
||||
"2017": "17",
|
||||
},
|
||||
}, {
|
||||
pattern: "00,max_int=2",
|
||||
pat: &Pattern{
|
||||
RoundingContext: RoundingContext{
|
||||
MaxIntegerDigits: 2,
|
||||
MinIntegerDigits: 2,
|
||||
},
|
||||
},
|
||||
test: pairs{
|
||||
"2000": "00",
|
||||
"2001": "01",
|
||||
"2017": "17",
|
||||
},
|
||||
}, {
|
||||
pattern: "@@@@,max_int=2",
|
||||
pat: &Pattern{
|
||||
RoundingContext: RoundingContext{
|
||||
MaxIntegerDigits: 2,
|
||||
MinSignificantDigits: 4,
|
||||
},
|
||||
},
|
||||
test: pairs{
|
||||
"2017": "17.00",
|
||||
"2000": "0.000",
|
||||
"2001": "1.000",
|
||||
},
|
||||
|
||||
// Significant digits
|
||||
}, {
|
||||
pattern: "@@##",
|
||||
test: pairs{
|
||||
"1": "1.0",
|
||||
"0.1": "0.10", // leading zero does not count as significant digit
|
||||
"123": "123",
|
||||
"1234": "1234",
|
||||
"12345": "12340",
|
||||
},
|
||||
}, {
|
||||
pattern: "@@@@",
|
||||
test: pairs{
|
||||
"1": "1.000",
|
||||
".1": "0.1000",
|
||||
".001": "0.001000",
|
||||
"123": "123.0",
|
||||
"1234": "1234",
|
||||
"12345": "12340", // rounding down
|
||||
"NaN": "NaN",
|
||||
"-Inf": "-∞",
|
||||
},
|
||||
|
||||
// TODO: rounding
|
||||
// {"@@@@": "23456": "23460"}, // rounding up
|
||||
// TODO: padding
|
||||
|
||||
// Scientific and Engineering notation
|
||||
}, {
|
||||
pattern: "#E0",
|
||||
test: pairs{
|
||||
"0": "0\u202f×\u202f10⁰",
|
||||
"1": "1\u202f×\u202f10⁰",
|
||||
"123.456": "1\u202f×\u202f10²",
|
||||
},
|
||||
}, {
|
||||
pattern: "#E+0",
|
||||
test: pairs{
|
||||
"0": "0\u202f×\u202f10⁺⁰",
|
||||
"1000": "1\u202f×\u202f10⁺³",
|
||||
"1E100": "1\u202f×\u202f10⁺¹⁰⁰",
|
||||
"1E-100": "1\u202f×\u202f10⁻¹⁰⁰",
|
||||
"NaN": "NaN",
|
||||
"-Inf": "-∞",
|
||||
},
|
||||
}, {
|
||||
pattern: "##0E00",
|
||||
test: pairs{
|
||||
"100": "100\u202f×\u202f10⁰⁰",
|
||||
"12345": "12\u202f×\u202f10⁰³",
|
||||
"123.456": "123\u202f×\u202f10⁰⁰",
|
||||
},
|
||||
}, {
|
||||
pattern: "##0.###E00",
|
||||
test: pairs{
|
||||
"100": "100\u202f×\u202f10⁰⁰",
|
||||
"12345": "12.345\u202f×\u202f10⁰³",
|
||||
"123456": "123.456\u202f×\u202f10⁰³",
|
||||
"123.456": "123.456\u202f×\u202f10⁰⁰",
|
||||
"123.4567": "123.457\u202f×\u202f10⁰⁰",
|
||||
},
|
||||
}, {
|
||||
pattern: "##0.000E00",
|
||||
test: pairs{
|
||||
"100": "100.000\u202f×\u202f10⁰⁰",
|
||||
"12345": "12.345\u202f×\u202f10⁰³",
|
||||
"123.456": "123.456\u202f×\u202f10⁰⁰",
|
||||
"12.3456": "12.346\u202f×\u202f10⁰⁰",
|
||||
},
|
||||
}, {
|
||||
pattern: "@@E0",
|
||||
test: pairs{
|
||||
"0": "0.0\u202f×\u202f10⁰",
|
||||
"99": "9.9\u202f×\u202f10¹",
|
||||
"0.99": "9.9\u202f×\u202f10⁻¹",
|
||||
},
|
||||
}, {
|
||||
pattern: "@###E00",
|
||||
test: pairs{
|
||||
"0": "0\u202f×\u202f10⁰⁰",
|
||||
"1": "1\u202f×\u202f10⁰⁰",
|
||||
"11": "1.1\u202f×\u202f10⁰¹",
|
||||
"111": "1.11\u202f×\u202f10⁰²",
|
||||
"1111": "1.111\u202f×\u202f10⁰³",
|
||||
"11111": "1.111\u202f×\u202f10⁰⁴",
|
||||
"0.1": "1\u202f×\u202f10⁻⁰¹",
|
||||
"0.11": "1.1\u202f×\u202f10⁻⁰¹",
|
||||
"0.001": "1\u202f×\u202f10⁻⁰³",
|
||||
},
|
||||
}, {
|
||||
pattern: "*x##0",
|
||||
test: pairs{
|
||||
"0": "xx0",
|
||||
"10": "x10",
|
||||
"100": "100",
|
||||
"1000": "1000",
|
||||
},
|
||||
}, {
|
||||
pattern: "##0*x",
|
||||
test: pairs{
|
||||
"0": "0xx",
|
||||
"10": "10x",
|
||||
"100": "100",
|
||||
"1000": "1000",
|
||||
},
|
||||
}, {
|
||||
pattern: "* ###0.000",
|
||||
test: pairs{
|
||||
"0": " 0.000",
|
||||
"123": " 123.000",
|
||||
"123.456": " 123.456",
|
||||
"1234.567": "1234.567",
|
||||
},
|
||||
}, {
|
||||
pattern: "**0.0#######E00",
|
||||
test: pairs{
|
||||
"0": "***0.0\u202f×\u202f10⁰⁰",
|
||||
"10": "***1.0\u202f×\u202f10⁰¹",
|
||||
"11": "***1.1\u202f×\u202f10⁰¹",
|
||||
"111": "**1.11\u202f×\u202f10⁰²",
|
||||
"1111": "*1.111\u202f×\u202f10⁰³",
|
||||
"11111": "1.1111\u202f×\u202f10⁰⁴",
|
||||
"11110": "*1.111\u202f×\u202f10⁰⁴",
|
||||
"11100": "**1.11\u202f×\u202f10⁰⁴",
|
||||
"11000": "***1.1\u202f×\u202f10⁰⁴",
|
||||
"10000": "***1.0\u202f×\u202f10⁰⁴",
|
||||
},
|
||||
}, {
|
||||
pattern: "*xpre0suf",
|
||||
test: pairs{
|
||||
"0": "pre0suf",
|
||||
"10": "pre10suf",
|
||||
},
|
||||
}, {
|
||||
pattern: "*∞ pre ###0 suf",
|
||||
test: pairs{
|
||||
"0": "∞∞∞ pre 0 suf",
|
||||
"10": "∞∞ pre 10 suf",
|
||||
"100": "∞ pre 100 suf",
|
||||
"1000": " pre 1000 suf",
|
||||
},
|
||||
}, {
|
||||
pattern: "pre *∞###0 suf",
|
||||
test: pairs{
|
||||
"0": "pre ∞∞∞0 suf",
|
||||
"10": "pre ∞∞10 suf",
|
||||
"100": "pre ∞100 suf",
|
||||
"1000": "pre 1000 suf",
|
||||
},
|
||||
}, {
|
||||
pattern: "pre ###0*∞ suf",
|
||||
test: pairs{
|
||||
"0": "pre 0∞∞∞ suf",
|
||||
"10": "pre 10∞∞ suf",
|
||||
"100": "pre 100∞ suf",
|
||||
"1000": "pre 1000 suf",
|
||||
},
|
||||
}, {
|
||||
pattern: "pre ###0 suf *∞",
|
||||
test: pairs{
|
||||
"0": "pre 0 suf ∞∞∞",
|
||||
"10": "pre 10 suf ∞∞",
|
||||
"100": "pre 100 suf ∞",
|
||||
"1000": "pre 1000 suf ",
|
||||
},
|
||||
}, {
|
||||
// Take width of positive pattern.
|
||||
pattern: "**###0;**-#####0x",
|
||||
test: pairs{
|
||||
"0": "***0",
|
||||
"-1": "*-1x",
|
||||
},
|
||||
}, {
|
||||
pattern: "0.00%",
|
||||
test: pairs{
|
||||
"0.1": "10.00%",
|
||||
},
|
||||
}, {
|
||||
pattern: "0.##%",
|
||||
test: pairs{
|
||||
"0.1": "10%",
|
||||
"0.11": "11%",
|
||||
"0.111": "11.1%",
|
||||
"0.1111": "11.11%",
|
||||
"0.11111": "11.11%",
|
||||
},
|
||||
}, {
|
||||
pattern: "‰ 0.0#",
|
||||
test: pairs{
|
||||
"0.1": "‰ 100.0",
|
||||
"0.11": "‰ 110.0",
|
||||
"0.111": "‰ 111.0",
|
||||
"0.1111": "‰ 111.1",
|
||||
"0.11111": "‰ 111.11",
|
||||
"0.111111": "‰ 111.11",
|
||||
},
|
||||
}}
|
||||
|
||||
// TODO:
|
||||
// "#,##0.00¤",
|
||||
// "#,##0.00 ¤;(#,##0.00 ¤)",
|
||||
|
||||
for _, tc := range testCases {
|
||||
pat := tc.pat
|
||||
if pat == nil {
|
||||
var err error
|
||||
if pat, err = ParsePattern(tc.pattern); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
var f Formatter
|
||||
f.InitPattern(language.English, pat)
|
||||
for num, want := range tc.test {
|
||||
buf := make([]byte, 100)
|
||||
t.Run(tc.pattern+"/"+num, func(t *testing.T) {
|
||||
var d Decimal
|
||||
d.Convert(f.RoundingContext, dec(num))
|
||||
buf = f.Format(buf[:0], &d)
|
||||
if got := string(buf); got != want {
|
||||
t.Errorf("\n got %[1]q (%[1]s)\nwant %[2]q (%[2]s)", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocales(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tag language.Tag
|
||||
num string
|
||||
want string
|
||||
}{
|
||||
{language.Make("en"), "123456.78", "123,456.78"},
|
||||
{language.Make("de"), "123456.78", "123.456,78"},
|
||||
{language.Make("de-CH"), "123456.78", "123’456.78"},
|
||||
{language.Make("fr"), "123456.78", "123 456,78"},
|
||||
{language.Make("bn"), "123456.78", "১,২৩,৪৫৬.৭৮"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprint(tc.tag, "/", tc.num), func(t *testing.T) {
|
||||
var f Formatter
|
||||
f.InitDecimal(tc.tag)
|
||||
var d Decimal
|
||||
d.Convert(f.RoundingContext, dec(tc.num))
|
||||
b := f.Format(nil, &d)
|
||||
if got := string(b); got != tc.want {
|
||||
t.Errorf("got %[1]q (%[1]s); want %[2]q (%[2]s)", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatters(t *testing.T) {
|
||||
var f Formatter
|
||||
testCases := []struct {
|
||||
init func(t language.Tag)
|
||||
num string
|
||||
want string
|
||||
}{
|
||||
{f.InitDecimal, "123456.78", "123,456.78"},
|
||||
{f.InitScientific, "123456.78", "1.23\u202f×\u202f10⁵"},
|
||||
{f.InitEngineering, "123456.78", "123.46\u202f×\u202f10³"},
|
||||
{f.InitEngineering, "1234", "1.23\u202f×\u202f10³"},
|
||||
|
||||
{f.InitPercent, "0.1234", "12.34%"},
|
||||
{f.InitPerMille, "0.1234", "123.40‰"},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
t.Run(fmt.Sprint(i, "/", tc.num), func(t *testing.T) {
|
||||
tc.init(language.English)
|
||||
f.SetScale(2)
|
||||
var d Decimal
|
||||
d.Convert(f.RoundingContext, dec(tc.num))
|
||||
b := f.Format(nil, &d)
|
||||
if got := string(b); got != tc.want {
|
||||
t.Errorf("got %[1]q (%[1]s); want %[2]q (%[2]s)", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
458
vendor/golang.org/x/text/internal/number/gen.go
generated
vendored
Normal file
458
vendor/golang.org/x/text/internal/number/gen.go
generated
vendored
Normal file
|
@ -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) symOffset {
|
||||
for {
|
||||
if sym := symbolMap[key{langIndex, ns}]; sym != nil {
|
||||
return symOffset(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]symOffset
|
||||
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 >= hasNonLatnMask {
|
||||
log.Fatalf("Number of alternative assignments >= %x", hasNonLatnMask)
|
||||
}
|
||||
// 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] = hasNonLatnMask | symOffset(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)
|
||||
}
|
55
vendor/golang.org/x/text/internal/number/gen_common.go
generated
vendored
Normal file
55
vendor/golang.org/x/text/internal/number/gen_common.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
// 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
|
||||
)
|
||||
|
||||
const hasNonLatnMask = 0x8000
|
||||
|
||||
// symOffset is an offset into altSymData if the bit indicated by hasNonLatnMask
|
||||
// is not 0 (with this bit masked out), and an offset into symIndex otherwise.
|
||||
//
|
||||
// TODO: this type can be a byte again if we use an indirection into altsymData
|
||||
// and introduce an alt -> offset slice (the length of this will be number of
|
||||
// alternatives plus 1). This also allows getting rid of the compactTag field
|
||||
// in altSymData. In total this will save about 1K.
|
||||
type symOffset uint16
|
||||
|
||||
type altSymData struct {
|
||||
compactTag uint16
|
||||
symIndex symOffset
|
||||
system system
|
||||
}
|
156
vendor/golang.org/x/text/internal/number/number.go
generated
vendored
Normal file
156
vendor/golang.org/x/text/internal/number/number.go
generated
vendored
Normal file
|
@ -0,0 +1,156 @@
|
|||
// 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 symOffset // 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 := symOffset(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&hasNonLatnMask == 0 { // Latn digits.
|
||||
pSymIndex = p
|
||||
} else { // Non-Latn or multiple numbering systems.
|
||||
// Take the first entry from the alternatives list.
|
||||
data := langToAlt[p&^hasNonLatnMask]
|
||||
pSymIndex = data.symIndex
|
||||
system = data.system
|
||||
}
|
||||
} else {
|
||||
langIndex := compactIndex
|
||||
ns := system
|
||||
outerLoop:
|
||||
for ; ; p = langToDefaults[langIndex] {
|
||||
if p&hasNonLatnMask == 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])
|
||||
} else {
|
||||
// The index points to a list of symbol data indexes.
|
||||
for _, e := range langToAlt[p&^hasNonLatnMask:] {
|
||||
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). Fall back to the default entry
|
||||
// for Latn if there is no data for the numbering
|
||||
// system of this language.
|
||||
if ns == 0 {
|
||||
break
|
||||
}
|
||||
// Fall back to Latin and start from the original
|
||||
// language. See
|
||||
// http://unicode.org/reports/tr35/#Locale_Inheritance.
|
||||
ns = numLatn
|
||||
langIndex = compactIndex
|
||||
continue outerLoop
|
||||
}
|
||||
// Fall back to parent.
|
||||
langIndex = int(internal.Parent[langIndex])
|
||||
} else 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)
|
||||
}
|
||||
|
||||
// AppendDigit appends the UTF-8 sequence for n corresponding to the given 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) AppendDigit(dst []byte, digit byte) []byte {
|
||||
dst = append(dst, n.system.zero[:n.system.digitSize]...)
|
||||
dst[len(dst)-1] += digit
|
||||
return dst
|
||||
}
|
||||
|
||||
// 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]]
|
||||
}
|
||||
}
|
||||
}
|
104
vendor/golang.org/x/text/internal/number/number_test.go
generated
vendored
Normal file
104
vendor/golang.org/x/text/internal/number/number_test.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
// 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 (
|
||||
"fmt"
|
||||
"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
|
||||
|
||||
{"bn-u-nu-beng", SymGroup, ",", '\u09ef'},
|
||||
{"bn-u-nu-deva", SymGroup, ",", '\u096f'},
|
||||
{"bn-u-nu-latn", SymGroup, ",", '9'},
|
||||
|
||||
{"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 {
|
||||
t.Run(fmt.Sprintf("%s:%v", tc.lang, tc.sym), func(t *testing.T) {
|
||||
info := InfoFromTag(language.MustParse(tc.lang))
|
||||
if got := info.Symbol(tc.sym); got != tc.wantSym {
|
||||
t.Errorf("sym: got %q; want %q", got, tc.wantSym)
|
||||
}
|
||||
if got := info.Digit('9'); got != tc.wantNine {
|
||||
t.Errorf("Digit(9): got %+q; want %+q", got, tc.wantNine)
|
||||
}
|
||||
var buf [4]byte
|
||||
if got := string(buf[:info.WriteDigit(buf[:], '9')]); got != string(tc.wantNine) {
|
||||
t.Errorf("WriteDigit(9): got %+q; want %+q", got, tc.wantNine)
|
||||
}
|
||||
if got := string(info.AppendDigit([]byte{}, 9)); got != string(tc.wantNine) {
|
||||
t.Errorf("AppendDigit(9): got %+q; want %+q", 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
485
vendor/golang.org/x/text/internal/number/pattern.go
generated
vendored
Normal file
485
vendor/golang.org/x/text/internal/number/pattern.go
generated
vendored
Normal file
|
@ -0,0 +1,485 @@
|
|||
// 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 {
|
||||
RoundingContext
|
||||
|
||||
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.
|
||||
PadRune rune
|
||||
FormatWidth uint16
|
||||
|
||||
GroupingSize [2]uint8
|
||||
Flags PatternFlag
|
||||
}
|
||||
|
||||
// A RoundingContext indicates how a number should be converted to digits.
|
||||
// It contains all information needed to determine the "visible digits" as
|
||||
// required by the pluralization rules.
|
||||
type RoundingContext struct {
|
||||
// TODO: unify these two fields so that there is a more unambiguous meaning
|
||||
// of how precision is handled.
|
||||
MaxSignificantDigits int16 // -1 is unlimited
|
||||
MaxFractionDigits int16 // -1 is unlimited
|
||||
|
||||
Increment uint32
|
||||
IncrementScale uint8 // May differ from printed scale.
|
||||
|
||||
Mode RoundingMode
|
||||
|
||||
DigitShift uint8 // Number of decimals to shift. Used for % and ‰.
|
||||
|
||||
// Number of digits.
|
||||
MinIntegerDigits uint8
|
||||
|
||||
MaxIntegerDigits uint8
|
||||
MinFractionDigits uint8
|
||||
MinSignificantDigits uint8
|
||||
|
||||
MinExponentDigits uint8
|
||||
}
|
||||
|
||||
// RoundSignificantDigits returns the number of significant digits an
|
||||
// implementation of Convert may round to or n < 0 if there is no maximum or
|
||||
// a maximum is not recommended.
|
||||
func (r *RoundingContext) RoundSignificantDigits() (n int) {
|
||||
if r.MaxFractionDigits == 0 && r.MaxSignificantDigits > 0 {
|
||||
return int(r.MaxSignificantDigits)
|
||||
} else if r.isScientific() && r.MaxIntegerDigits == 1 {
|
||||
if r.MaxSignificantDigits == 0 ||
|
||||
int(r.MaxFractionDigits+1) == int(r.MaxSignificantDigits) {
|
||||
// Note: don't add DigitShift: it is only used for decimals.
|
||||
return int(r.MaxFractionDigits) + 1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// RoundFractionDigits returns the number of fraction digits an implementation
|
||||
// of Convert may round to or n < 0 if there is no maximum or a maximum is not
|
||||
// recommended.
|
||||
func (r *RoundingContext) RoundFractionDigits() (n int) {
|
||||
if r.MinExponentDigits == 0 &&
|
||||
r.MaxSignificantDigits == 0 &&
|
||||
r.MaxFractionDigits >= 0 {
|
||||
return int(r.MaxFractionDigits) + int(r.DigitShift)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// SetScale fixes the RoundingContext to a fixed number of fraction digits.
|
||||
func (r *RoundingContext) SetScale(scale int) {
|
||||
r.MinFractionDigits = uint8(scale)
|
||||
r.MaxFractionDigits = int16(scale)
|
||||
}
|
||||
|
||||
func (r *RoundingContext) SetPrecision(prec int) {
|
||||
r.MaxSignificantDigits = int16(prec)
|
||||
}
|
||||
|
||||
func (r *RoundingContext) isScientific() bool {
|
||||
return r.MinExponentDigits > 0
|
||||
}
|
||||
|
||||
func (f *Pattern) needsSep(pos int) bool {
|
||||
p := pos - 1
|
||||
size := int(f.GroupingSize[0])
|
||||
if size == 0 || p == 0 {
|
||||
return false
|
||||
}
|
||||
if p == size {
|
||||
return true
|
||||
}
|
||||
if p -= size; p < 0 {
|
||||
return false
|
||||
}
|
||||
// TODO: make second groupingsize the same as first if 0 so that we can
|
||||
// avoid this check.
|
||||
if x := int(f.GroupingSize[1]); x != 0 {
|
||||
size = x
|
||||
}
|
||||
return p%size == 0
|
||||
}
|
||||
|
||||
// A PatternFlag is a bit mask for the flag field of a Pattern.
|
||||
type PatternFlag uint8
|
||||
|
||||
const (
|
||||
AlwaysSign PatternFlag = 1 << iota
|
||||
ElideSign // Use space instead of plus sign. AlwaysSign must be true.
|
||||
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 &&
|
||||
0 < p.groupingCount && 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
|
||||
}
|
||||
if p.Increment == 0 {
|
||||
p.IncrementScale = 0
|
||||
}
|
||||
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)
|
||||
p.updateGrouping()
|
||||
|
||||
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 '\'':
|
||||
p.FormatWidth--
|
||||
return p.escapeFirst
|
||||
case '%':
|
||||
if p.DigitShift != 0 {
|
||||
p.setError(errDuplicatePercentSign)
|
||||
}
|
||||
p.DigitShift = 2
|
||||
case '\u2030': // ‰ Per mille
|
||||
if p.DigitShift != 0 {
|
||||
p.setError(errDuplicatePermilleSign)
|
||||
}
|
||||
p.DigitShift = 3
|
||||
// TODO: handle currency somehow: ¤, ¤¤, ¤¤¤, ¤¤¤¤
|
||||
}
|
||||
p.buf = append(p.buf, string(r)...)
|
||||
return p.affix
|
||||
}
|
||||
|
||||
func (p *parser) escapeFirst(r rune) state {
|
||||
switch r {
|
||||
case '\'':
|
||||
p.buf = append(p.buf, "\\'"...)
|
||||
return p.affix
|
||||
default:
|
||||
p.buf = append(p.buf, '\'')
|
||||
p.buf = append(p.buf, string(r)...)
|
||||
}
|
||||
return p.escape
|
||||
}
|
||||
|
||||
func (p *parser) escape(r rune) state {
|
||||
switch r {
|
||||
case '\'':
|
||||
p.FormatWidth--
|
||||
p.buf = append(p.buf, '\'')
|
||||
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
|
||||
p.MaxFractionDigits = -1
|
||||
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
|
||||
case ',':
|
||||
next = p.integer
|
||||
}
|
||||
p.updateGrouping()
|
||||
return next
|
||||
}
|
||||
p.Increment = p.Increment*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.Increment = p.Increment*10 + uint32(r-'0')
|
||||
p.IncrementScale++
|
||||
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
|
||||
}
|
438
vendor/golang.org/x/text/internal/number/pattern_test.go
generated
vendored
Normal file
438
vendor/golang.org/x/text/internal/number/pattern_test.go
generated
vendored
Normal file
|
@ -0,0 +1,438 @@
|
|||
// 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,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"+0",
|
||||
&Pattern{
|
||||
Affix: "\x01+\x00",
|
||||
FormatWidth: 2,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"0+",
|
||||
&Pattern{
|
||||
Affix: "\x00\x01+",
|
||||
FormatWidth: 2,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"0000",
|
||||
&Pattern{
|
||||
FormatWidth: 4,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
".#",
|
||||
&Pattern{
|
||||
FormatWidth: 2,
|
||||
RoundingContext: RoundingContext{
|
||||
MaxFractionDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"#0.###",
|
||||
&Pattern{
|
||||
FormatWidth: 6,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 3,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"#0.######",
|
||||
&Pattern{
|
||||
FormatWidth: 9,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 6,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"#,0",
|
||||
&Pattern{
|
||||
FormatWidth: 3,
|
||||
GroupingSize: [2]uint8{1, 0},
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"#,0.00",
|
||||
&Pattern{
|
||||
FormatWidth: 6,
|
||||
GroupingSize: [2]uint8{1, 0},
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MinFractionDigits: 2,
|
||||
MaxFractionDigits: 2,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"#,##0.###",
|
||||
&Pattern{
|
||||
FormatWidth: 9,
|
||||
GroupingSize: [2]uint8{3, 0},
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 3,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"#,##,##0.###",
|
||||
&Pattern{
|
||||
FormatWidth: 12,
|
||||
GroupingSize: [2]uint8{3, 2},
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 3,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
// Ignore additional separators.
|
||||
"#,####,##,##0.###",
|
||||
&Pattern{
|
||||
FormatWidth: 17,
|
||||
GroupingSize: [2]uint8{3, 2},
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 3,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"#E0",
|
||||
&Pattern{
|
||||
FormatWidth: 3,
|
||||
RoundingContext: RoundingContext{
|
||||
MaxIntegerDigits: 1,
|
||||
MinExponentDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
// At least one exponent digit is required. As long as this is true, one can
|
||||
// determine that scientific rendering is needed if MinExponentDigits > 0.
|
||||
"#E#",
|
||||
nil,
|
||||
}, {
|
||||
"0E0",
|
||||
&Pattern{
|
||||
FormatWidth: 3,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MinExponentDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"##0.###E00",
|
||||
&Pattern{
|
||||
FormatWidth: 10,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MaxIntegerDigits: 3,
|
||||
MaxFractionDigits: 3,
|
||||
MinExponentDigits: 2,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"##00.0#E0",
|
||||
&Pattern{
|
||||
FormatWidth: 9,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 2,
|
||||
MaxIntegerDigits: 4,
|
||||
MinFractionDigits: 1,
|
||||
MaxFractionDigits: 2,
|
||||
MinExponentDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"#00.0E+0",
|
||||
&Pattern{
|
||||
FormatWidth: 8,
|
||||
Flags: AlwaysExpSign,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 2,
|
||||
MaxIntegerDigits: 3,
|
||||
MinFractionDigits: 1,
|
||||
MaxFractionDigits: 1,
|
||||
MinExponentDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"0.0E++0",
|
||||
nil,
|
||||
}, {
|
||||
"#0E+",
|
||||
nil,
|
||||
}, {
|
||||
// significant digits
|
||||
"@",
|
||||
&Pattern{
|
||||
FormatWidth: 1,
|
||||
RoundingContext: RoundingContext{
|
||||
MinSignificantDigits: 1,
|
||||
MaxSignificantDigits: 1,
|
||||
MaxFractionDigits: -1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
// significant digits
|
||||
"@@@@",
|
||||
&Pattern{
|
||||
FormatWidth: 4,
|
||||
RoundingContext: RoundingContext{
|
||||
MinSignificantDigits: 4,
|
||||
MaxSignificantDigits: 4,
|
||||
MaxFractionDigits: -1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"@###",
|
||||
&Pattern{
|
||||
FormatWidth: 4,
|
||||
RoundingContext: RoundingContext{
|
||||
MinSignificantDigits: 1,
|
||||
MaxSignificantDigits: 4,
|
||||
MaxFractionDigits: -1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
// Exponents in significant digits mode gets normalized.
|
||||
"@@E0",
|
||||
&Pattern{
|
||||
FormatWidth: 4,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MaxIntegerDigits: 1,
|
||||
MinFractionDigits: 1,
|
||||
MaxFractionDigits: 1,
|
||||
MinExponentDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"@###E00",
|
||||
&Pattern{
|
||||
FormatWidth: 7,
|
||||
RoundingContext: RoundingContext{
|
||||
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,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MaxFractionDigits: 3,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
// Rounding increment
|
||||
"1.05",
|
||||
&Pattern{
|
||||
FormatWidth: 4,
|
||||
RoundingContext: RoundingContext{
|
||||
Increment: 105,
|
||||
IncrementScale: 2,
|
||||
MinIntegerDigits: 1,
|
||||
MinFractionDigits: 2,
|
||||
MaxFractionDigits: 2,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
// Rounding increment with grouping
|
||||
"1,05",
|
||||
&Pattern{
|
||||
FormatWidth: 4,
|
||||
GroupingSize: [2]uint8{2, 0},
|
||||
RoundingContext: RoundingContext{
|
||||
Increment: 105,
|
||||
IncrementScale: 0,
|
||||
MinIntegerDigits: 3,
|
||||
MinFractionDigits: 0,
|
||||
MaxFractionDigits: 0,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"0.0%",
|
||||
&Pattern{
|
||||
Affix: "\x00\x01%",
|
||||
FormatWidth: 4,
|
||||
RoundingContext: RoundingContext{
|
||||
DigitShift: 2,
|
||||
MinIntegerDigits: 1,
|
||||
MinFractionDigits: 1,
|
||||
MaxFractionDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"0.0‰",
|
||||
&Pattern{
|
||||
Affix: "\x00\x03‰",
|
||||
FormatWidth: 4,
|
||||
RoundingContext: RoundingContext{
|
||||
DigitShift: 3,
|
||||
MinIntegerDigits: 1,
|
||||
MinFractionDigits: 1,
|
||||
MaxFractionDigits: 1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"#,##0.00¤",
|
||||
&Pattern{
|
||||
Affix: "\x00\x02¤",
|
||||
FormatWidth: 9,
|
||||
GroupingSize: [2]uint8{3, 0},
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 1,
|
||||
MinFractionDigits: 2,
|
||||
MaxFractionDigits: 2,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"#,##0.00 ¤;(#,##0.00 ¤)",
|
||||
&Pattern{Affix: "\x00\x04\u00a0¤\x01(\x05\u00a0¤)",
|
||||
NegOffset: 6,
|
||||
FormatWidth: 10,
|
||||
GroupingSize: [2]uint8{3, 0},
|
||||
RoundingContext: RoundingContext{
|
||||
DigitShift: 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,
|
||||
},
|
||||
}, {
|
||||
`* #0 o''clock`,
|
||||
&Pattern{Affix: "\x00\x09 o\\'clock",
|
||||
FormatWidth: 10,
|
||||
PadRune: 32,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 0x1,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
`'123'* #0'456'`,
|
||||
&Pattern{Affix: "\x05'123'\x05'456'",
|
||||
FormatWidth: 8,
|
||||
PadRune: 32,
|
||||
RoundingContext: RoundingContext{
|
||||
MinIntegerDigits: 0x1,
|
||||
},
|
||||
Flags: PadAfterPrefix},
|
||||
}, {
|
||||
// no duplicate padding
|
||||
"*xpre#suf*x", nil,
|
||||
}, {
|
||||
// no duplicate padding
|
||||
"*xpre#suf*x", nil,
|
||||
}}
|
||||
|
||||
func TestParsePattern(t *testing.T) {
|
||||
for i, tc := range testCases {
|
||||
t.Run(tc.pat, func(t *testing.T) {
|
||||
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 > 56 {
|
||||
t.Errorf("got %d; want <= 56", sz)
|
||||
}
|
||||
|
||||
}
|
16
vendor/golang.org/x/text/internal/number/roundingmode_string.go
generated
vendored
Normal file
16
vendor/golang.org/x/text/internal/number/roundingmode_string.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Code generated by "stringer -type RoundingMode"; DO NOT EDIT.
|
||||
|
||||
package number
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _RoundingMode_name = "ToNearestEvenToNearestZeroToNearestAwayToPositiveInfToNegativeInfToZeroAwayFromZeronumModes"
|
||||
|
||||
var _RoundingMode_index = [...]uint8{0, 13, 26, 39, 52, 65, 71, 83, 91}
|
||||
|
||||
func (i RoundingMode) String() string {
|
||||
if i >= RoundingMode(len(_RoundingMode_index)-1) {
|
||||
return fmt.Sprintf("RoundingMode(%d)", i)
|
||||
}
|
||||
return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]]
|
||||
}
|
1211
vendor/golang.org/x/text/internal/number/tables.go
generated
vendored
Normal file
1211
vendor/golang.org/x/text/internal/number/tables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
125
vendor/golang.org/x/text/internal/number/tables_test.go
generated
vendored
Normal file
125
vendor/golang.org/x/text/internal/number/tables_test.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue