cuelang.org/go@v0.10.1/cue/literal/num.go (about) 1 // Copyright 2020 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package literal 16 17 import ( 18 "cuelang.org/go/cue/errors" 19 "cuelang.org/go/cue/token" 20 "github.com/cockroachdb/apd/v3" 21 ) 22 23 // We avoid cuelang.org/go/internal.Context as that would be an import cycle. 24 var baseContext apd.Context 25 26 func init() { 27 baseContext = apd.BaseContext 28 baseContext.Precision = 34 29 } 30 31 // NumInfo contains information about a parsed numbers. 32 // 33 // Reusing a NumInfo across parses may avoid memory allocations. 34 type NumInfo struct { 35 pos token.Pos 36 src string 37 p int 38 ch byte 39 buf []byte 40 41 mul Multiplier 42 base byte 43 neg bool 44 UseSep bool 45 isFloat bool 46 err error 47 } 48 49 // String returns a canonical string representation of the number so that 50 // it can be parsed with math.Float.Parse. 51 func (p *NumInfo) String() string { 52 if len(p.buf) > 0 && p.base == 10 && p.mul == 0 { 53 return string(p.buf) 54 } 55 var d apd.Decimal 56 _ = p.decimal(&d) 57 return d.String() 58 } 59 60 type decimal = apd.Decimal 61 62 // Decimal is for internal use. 63 func (p *NumInfo) Decimal(v *decimal) error { 64 return p.decimal(v) 65 } 66 67 func (p *NumInfo) decimal(v *apd.Decimal) error { 68 if p.base != 10 { 69 _, _, _ = v.SetString("0") 70 b := p.buf 71 if p.buf[0] == '-' { 72 v.Negative = p.neg 73 b = p.buf[1:] 74 } 75 v.Coeff.SetString(string(b), int(p.base)) 76 return nil 77 } 78 _ = v.UnmarshalText(p.buf) 79 if p.mul != 0 { 80 _, _ = baseContext.Mul(v, v, mulToRat[p.mul]) 81 cond, _ := baseContext.RoundToIntegralExact(v, v) 82 if cond.Inexact() { 83 return p.errorf("number cannot be represented as int") 84 } 85 } 86 return nil 87 } 88 89 // Multiplier reports which multiplier was used in an integral number. 90 func (p *NumInfo) Multiplier() Multiplier { 91 return p.mul 92 } 93 94 // IsInt reports whether the number is an integral number. 95 func (p *NumInfo) IsInt() bool { 96 return !p.isFloat 97 } 98 99 // ParseNum parses s and populates NumInfo with the result. 100 func ParseNum(s string, n *NumInfo) error { 101 *n = NumInfo{pos: n.pos, src: s, buf: n.buf[:0]} 102 if !n.next() { 103 return n.errorf("invalid number %q", s) 104 } 105 switch n.ch { 106 case '-': 107 n.neg = true 108 n.buf = append(n.buf, '-') 109 n.next() 110 case '+': 111 n.next() 112 } 113 seenDecimalPoint := false 114 if n.ch == '.' { 115 n.next() 116 seenDecimalPoint = true 117 } 118 err := n.scanNumber(seenDecimalPoint) 119 if err != nil { 120 return err 121 } 122 if n.err != nil { 123 return n.err 124 } 125 if n.p < len(n.src) { 126 return n.errorf("invalid number %q", s) 127 } 128 if len(n.buf) == 0 { 129 n.buf = append(n.buf, '0') 130 } 131 return nil 132 } 133 134 func (p *NumInfo) errorf(format string, args ...interface{}) error { 135 return errors.Newf(p.pos, format, args...) 136 } 137 138 // A Multiplier indicates a multiplier indicator used in the literal. 139 type Multiplier byte 140 141 const ( 142 mul1 Multiplier = 1 + iota 143 mul2 144 mul3 145 mul4 146 mul5 147 mul6 148 mul7 149 mul8 150 151 mulBin = 0x10 152 mulDec = 0x20 153 154 K = mulDec | mul1 155 M = mulDec | mul2 156 G = mulDec | mul3 157 T = mulDec | mul4 158 P = mulDec | mul5 159 E = mulDec | mul6 160 Z = mulDec | mul7 161 Y = mulDec | mul8 162 163 Ki = mulBin | mul1 164 Mi = mulBin | mul2 165 Gi = mulBin | mul3 166 Ti = mulBin | mul4 167 Pi = mulBin | mul5 168 Ei = mulBin | mul6 169 Zi = mulBin | mul7 170 Yi = mulBin | mul8 171 ) 172 173 func (p *NumInfo) next() bool { 174 if p.p >= len(p.src) { 175 p.ch = 0 176 return false 177 } 178 p.ch = p.src[p.p] 179 p.p++ 180 if p.ch == '.' { 181 if len(p.buf) == 0 { 182 p.buf = append(p.buf, '0') 183 } 184 p.buf = append(p.buf, '.') 185 } 186 return true 187 } 188 189 func (p *NumInfo) digitVal(ch byte) (d int) { 190 switch { 191 case '0' <= ch && ch <= '9': 192 d = int(ch - '0') 193 case ch == '_': 194 p.UseSep = true 195 return 0 196 case 'a' <= ch && ch <= 'f': 197 d = int(ch - 'a' + 10) 198 case 'A' <= ch && ch <= 'F': 199 d = int(ch - 'A' + 10) 200 default: 201 return 16 // larger than any legal digit val 202 } 203 return d 204 } 205 206 func (p *NumInfo) scanMantissa(base int) bool { 207 hasDigit := false 208 var last byte 209 for p.digitVal(p.ch) < base { 210 if p.ch != '_' { 211 p.buf = append(p.buf, p.ch) 212 hasDigit = true 213 } 214 last = p.ch 215 p.next() 216 } 217 if last == '_' { 218 p.err = p.errorf("illegal '_' in number") 219 } 220 return hasDigit 221 } 222 223 func (p *NumInfo) scanNumber(seenDecimalPoint bool) error { 224 p.base = 10 225 226 if seenDecimalPoint { 227 p.isFloat = true 228 if !p.scanMantissa(10) { 229 return p.errorf("illegal fraction %q", p.src) 230 } 231 goto exponent 232 } 233 234 if p.ch == '0' { 235 // int or float 236 p.next() 237 switch p.ch { 238 case 'x', 'X': 239 p.base = 16 240 // hexadecimal int 241 p.next() 242 if !p.scanMantissa(16) { 243 // only scanned "0x" or "0X" 244 return p.errorf("illegal hexadecimal number %q", p.src) 245 } 246 case 'b': 247 p.base = 2 248 // binary int 249 p.next() 250 if !p.scanMantissa(2) { 251 // only scanned "0b" 252 return p.errorf("illegal binary number %q", p.src) 253 } 254 case 'o': 255 p.base = 8 256 // octal int 257 p.next() 258 if !p.scanMantissa(8) { 259 // only scanned "0o" 260 return p.errorf("illegal octal number %q", p.src) 261 } 262 default: 263 // int (base 8 or 10) or float 264 p.scanMantissa(8) 265 if p.ch == '8' || p.ch == '9' { 266 p.scanMantissa(10) 267 if p.ch != '.' && p.ch != 'e' && p.ch != 'E' { 268 return p.errorf("illegal integer number %q", p.src) 269 } 270 } 271 switch p.ch { 272 case 'e', 'E': 273 if len(p.buf) == 0 { 274 p.buf = append(p.buf, '0') 275 } 276 fallthrough 277 case '.': 278 goto fraction 279 } 280 if len(p.buf) > 0 { 281 p.base = 8 282 } 283 } 284 goto exit 285 } 286 287 // decimal int or float 288 if !p.scanMantissa(10) { 289 return p.errorf("illegal number start %q", p.src) 290 } 291 292 fraction: 293 if p.ch == '.' { 294 p.isFloat = true 295 p.next() 296 p.scanMantissa(10) 297 } 298 299 exponent: 300 switch p.ch { 301 case 'K', 'M', 'G', 'T', 'P': 302 p.mul = charToMul[p.ch] 303 p.next() 304 if p.ch == 'i' { 305 p.mul |= mulBin 306 p.next() 307 } else { 308 p.mul |= mulDec 309 } 310 var v apd.Decimal 311 p.isFloat = false 312 return p.decimal(&v) 313 314 case 'e', 'E': 315 p.isFloat = true 316 p.next() 317 p.buf = append(p.buf, 'e') 318 if p.ch == '-' || p.ch == '+' { 319 p.buf = append(p.buf, p.ch) 320 p.next() 321 } 322 if !p.scanMantissa(10) { 323 return p.errorf("illegal exponent %q", p.src) 324 } 325 } 326 327 exit: 328 return nil 329 } 330 331 var charToMul = map[byte]Multiplier{ 332 'K': mul1, 333 'M': mul2, 334 'G': mul3, 335 'T': mul4, 336 'P': mul5, 337 'E': mul6, 338 'Z': mul7, 339 'Y': mul8, 340 } 341 342 var mulToRat = map[Multiplier]*apd.Decimal{} 343 344 func init() { 345 d := apd.New(1, 0) 346 b := apd.New(1, 0) 347 dm := apd.New(1000, 0) 348 bm := apd.New(1024, 0) 349 350 c := apd.BaseContext 351 for i := Multiplier(1); int(i) < len(charToMul); i++ { 352 // TODO: may we write to one of the sources? 353 var bn, dn apd.Decimal 354 _, _ = c.Mul(&dn, d, dm) 355 d = &dn 356 _, _ = c.Mul(&bn, b, bm) 357 b = &bn 358 mulToRat[mulDec|i] = d 359 mulToRat[mulBin|i] = b 360 } 361 }