Source file
src/tawesoft.co.uk/go/lxstrconv/decimalformat.go
Documentation:
src/tawesoft.co.uk/go/lxstrconv/decimalformat.go
1 package lxstrconv
2
3 import (
4 "math"
5 "strconv"
6 "unicode"
7 "unicode/utf8"
8
9 "golang.org/x/text/language"
10 "golang.org/x/text/message"
11 "golang.org/x/text/number"
12 )
13
14
15
16 func acceptRune(r rune, s string) int {
17 if f, ok := firstRune(s); ok && (f == r) {
18 return utf8.RuneLen(r)
19 } else {
20 return 0
21 }
22 }
23
24
25 func firstRune(s string) (rune, bool) {
26 for _, c := range s {
27 return c, true
28 }
29 return runeNone, false
30 }
31
32
33
34 func guessDecimalGroupSeparator(p *message.Printer) rune {
35
36 s := p.Sprint(number.Decimal(1234567890))
37 return repeatingRune(s)
38 }
39
40
41
42 func guessDecimalPoint(p *message.Printer) rune {
43
44
45 s1 := p.Sprint(number.Decimal(1.23))
46 s2 := p.Sprint(number.Decimal(4.56))
47 s := s1 + s2
48 return repeatingRune(s)
49 }
50
51
52
53 func guessDecimalDigits(p *message.Printer, out *[10]rune) {
54 for i := 0; i < 10; i++ {
55 s := []rune(p.Sprint(number.Decimal(i)))
56 if len(s) == 1 {
57 out[i] = s[0]
58 } else {
59 out[i] = runeNone
60 }
61 }
62 }
63
64
65
66
67
68
69
70 type decimalFormat struct {
71
72
73 GroupSeparator rune
74
75
76
77 Point rune
78
79
80 Digits [10]rune
81 }
82
83 func (f decimalFormat) ParseInt(s string) (int64, error) {
84 if len(s) == 0 { return 0, strconv.ErrSyntax }
85
86 value, length, err := f.AcceptInt(s)
87
88 if err != nil { return 0, err }
89 if len(s) != length { return 0, strconv.ErrSyntax }
90
91 return value, nil
92 }
93
94 func (f decimalFormat) ParseFloat(s string) (float64, error) {
95 if len(s) == 0 { return 0, strconv.ErrSyntax }
96
97 value, length, err := f.AcceptFloat(s)
98
99 if err != nil { return 0, err }
100 if len(s) != length { return 0, strconv.ErrSyntax }
101
102 return value, nil
103 }
104
105
106
107
108 func NewDecimalFormat(tag language.Tag) NumberFormat {
109
110
111
112
113
114
115 p := message.NewPrinter(tag)
116
117 format := decimalFormat{
118 GroupSeparator: guessDecimalGroupSeparator(p),
119 Point: guessDecimalPoint(p),
120 }
121
122 guessDecimalDigits(p, &format.Digits)
123
124 return format
125 }
126
127
128 func decimalRuneToInt(d rune, digits *[10]rune) (int, bool) {
129 for i := 0; i < 10; i++ {
130 if d == digits[i] { return i, true }
131 }
132 return 0, false
133 }
134
135
136
137
138
139
140
141 func (f decimalFormat) AcceptInt(s string) (value int64, length int, err error) {
142
143 if len(s) == 0 { return 0, 0, nil }
144
145 if s[0] == '-' {
146
147 v, l, _ := f.AcceptUint(s[1:])
148
149 if l > 0 {
150 return int64(v) * -1, l + 1, nil
151 } else {
152 return 0, 0, nil
153 }
154 }
155
156
157 v, l, err := f.AcceptUint(s)
158 return int64(v), l, nil
159 }
160
161
162 func (f decimalFormat) AcceptUint(s string) (value uint64, length int, err error) {
163 var accu uint64
164
165 for i, c := range s {
166 if c == f.GroupSeparator {
167
168 } else if unicode.IsSpace(c) {
169
170 } else if d, ok := decimalRuneToInt(c, &f.Digits); ok {
171 accu *= 10
172 accu += uint64(d)
173
174 } else {
175
176 return accu, i, nil
177 }
178 }
179
180 return accu, len(s), nil
181 }
182
183
184
185
186
187
188
189 func (f decimalFormat) AcceptFloat(s string) (value float64, length int, err error) {
190 var left, right int64
191 var leftLen, rightLen, pointLen int
192 var fLeft, fRight float64
193
194
195 if first, ok := firstRune(s); ok && first != f.Point {
196 left, leftLen, err = f.AcceptInt(s)
197
198 if leftLen == 0 { return 0, 0, nil }
199 fLeft = float64(left)
200 }
201
202 pointLen = acceptRune(f.Point, s[leftLen:])
203
204 if pointLen > 0 && (s[leftLen +pointLen] != '-') {
205 right, rightLen, err = f.AcceptInt(s[leftLen +pointLen:])
206
207 }
208
209 if right > 0.0 {
210 fRight = float64(right)
211 places := float64(1.0 + math.Floor(math.Log10(fRight)))
212 fRight *= math.Pow(0.1, places)
213 fRight = math.Copysign(fRight, fLeft)
214 }
215
216 value = fLeft + fRight
217 length = leftLen + pointLen + rightLen
218
219 return value, length, nil
220 }
221
View as plain text