...

Source file src/cuelang.org/go/cue/literal/num.go

Documentation: cuelang.org/go/cue/literal

     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  	if n.ch == '-' {
   106  		n.neg = true
   107  		n.buf = append(n.buf, '-')
   108  		n.next()
   109  	}
   110  	seenDecimalPoint := false
   111  	if n.ch == '.' {
   112  		n.next()
   113  		seenDecimalPoint = true
   114  	}
   115  	err := n.scanNumber(seenDecimalPoint)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	if n.err != nil {
   120  		return n.err
   121  	}
   122  	if n.p < len(n.src) {
   123  		return n.errorf("invalid number %q", s)
   124  	}
   125  	if len(n.buf) == 0 {
   126  		n.buf = append(n.buf, '0')
   127  	}
   128  	return nil
   129  }
   130  
   131  func (p *NumInfo) errorf(format string, args ...interface{}) error {
   132  	return errors.Newf(p.pos, format, args...)
   133  }
   134  
   135  // A Multiplier indicates a multiplier indicator used in the literal.
   136  type Multiplier byte
   137  
   138  const (
   139  	mul1 Multiplier = 1 + iota
   140  	mul2
   141  	mul3
   142  	mul4
   143  	mul5
   144  	mul6
   145  	mul7
   146  	mul8
   147  
   148  	mulBin = 0x10
   149  	mulDec = 0x20
   150  
   151  	K = mulDec | mul1
   152  	M = mulDec | mul2
   153  	G = mulDec | mul3
   154  	T = mulDec | mul4
   155  	P = mulDec | mul5
   156  	E = mulDec | mul6
   157  	Z = mulDec | mul7
   158  	Y = mulDec | mul8
   159  
   160  	Ki = mulBin | mul1
   161  	Mi = mulBin | mul2
   162  	Gi = mulBin | mul3
   163  	Ti = mulBin | mul4
   164  	Pi = mulBin | mul5
   165  	Ei = mulBin | mul6
   166  	Zi = mulBin | mul7
   167  	Yi = mulBin | mul8
   168  )
   169  
   170  func (p *NumInfo) next() bool {
   171  	if p.p >= len(p.src) {
   172  		p.ch = 0
   173  		return false
   174  	}
   175  	p.ch = p.src[p.p]
   176  	p.p++
   177  	if p.ch == '.' {
   178  		if len(p.buf) == 0 {
   179  			p.buf = append(p.buf, '0')
   180  		}
   181  		p.buf = append(p.buf, '.')
   182  	}
   183  	return true
   184  }
   185  
   186  func (p *NumInfo) digitVal(ch byte) (d int) {
   187  	switch {
   188  	case '0' <= ch && ch <= '9':
   189  		d = int(ch - '0')
   190  	case ch == '_':
   191  		p.UseSep = true
   192  		return 0
   193  	case 'a' <= ch && ch <= 'f':
   194  		d = int(ch - 'a' + 10)
   195  	case 'A' <= ch && ch <= 'F':
   196  		d = int(ch - 'A' + 10)
   197  	default:
   198  		return 16 // larger than any legal digit val
   199  	}
   200  	return d
   201  }
   202  
   203  func (p *NumInfo) scanMantissa(base int) bool {
   204  	hasDigit := false
   205  	var last byte
   206  	for p.digitVal(p.ch) < base {
   207  		if p.ch != '_' {
   208  			p.buf = append(p.buf, p.ch)
   209  			hasDigit = true
   210  		}
   211  		last = p.ch
   212  		p.next()
   213  	}
   214  	if last == '_' {
   215  		p.err = p.errorf("illegal '_' in number")
   216  	}
   217  	return hasDigit
   218  }
   219  
   220  func (p *NumInfo) scanNumber(seenDecimalPoint bool) error {
   221  	p.base = 10
   222  
   223  	if seenDecimalPoint {
   224  		p.isFloat = true
   225  		if !p.scanMantissa(10) {
   226  			return p.errorf("illegal fraction %q", p.src)
   227  		}
   228  		goto exponent
   229  	}
   230  
   231  	if p.ch == '0' {
   232  		// int or float
   233  		p.next()
   234  		switch p.ch {
   235  		case 'x', 'X':
   236  			p.base = 16
   237  			// hexadecimal int
   238  			p.next()
   239  			if !p.scanMantissa(16) {
   240  				// only scanned "0x" or "0X"
   241  				return p.errorf("illegal hexadecimal number %q", p.src)
   242  			}
   243  		case 'b':
   244  			p.base = 2
   245  			// binary int
   246  			p.next()
   247  			if !p.scanMantissa(2) {
   248  				// only scanned "0b"
   249  				return p.errorf("illegal binary number %q", p.src)
   250  			}
   251  		case 'o':
   252  			p.base = 8
   253  			// octal int
   254  			p.next()
   255  			if !p.scanMantissa(8) {
   256  				// only scanned "0o"
   257  				return p.errorf("illegal octal number %q", p.src)
   258  			}
   259  		default:
   260  			// int (base 8 or 10) or float
   261  			p.scanMantissa(8)
   262  			if p.ch == '8' || p.ch == '9' {
   263  				p.scanMantissa(10)
   264  				if p.ch != '.' && p.ch != 'e' && p.ch != 'E' {
   265  					return p.errorf("illegal integer number %q", p.src)
   266  				}
   267  			}
   268  			switch p.ch {
   269  			case 'e', 'E':
   270  				if len(p.buf) == 0 {
   271  					p.buf = append(p.buf, '0')
   272  				}
   273  				fallthrough
   274  			case '.':
   275  				goto fraction
   276  			}
   277  			if len(p.buf) > 0 {
   278  				p.base = 8
   279  			}
   280  		}
   281  		goto exit
   282  	}
   283  
   284  	// decimal int or float
   285  	if !p.scanMantissa(10) {
   286  		return p.errorf("illegal number start %q", p.src)
   287  	}
   288  
   289  fraction:
   290  	if p.ch == '.' {
   291  		p.isFloat = true
   292  		p.next()
   293  		p.scanMantissa(10)
   294  	}
   295  
   296  exponent:
   297  	switch p.ch {
   298  	case 'K', 'M', 'G', 'T', 'P':
   299  		p.mul = charToMul[p.ch]
   300  		p.next()
   301  		if p.ch == 'i' {
   302  			p.mul |= mulBin
   303  			p.next()
   304  		} else {
   305  			p.mul |= mulDec
   306  		}
   307  		var v apd.Decimal
   308  		p.isFloat = false
   309  		return p.decimal(&v)
   310  
   311  	case 'e', 'E':
   312  		p.isFloat = true
   313  		p.next()
   314  		p.buf = append(p.buf, 'e')
   315  		if p.ch == '-' || p.ch == '+' {
   316  			p.buf = append(p.buf, p.ch)
   317  			p.next()
   318  		}
   319  		if !p.scanMantissa(10) {
   320  			return p.errorf("illegal exponent %q", p.src)
   321  		}
   322  	}
   323  
   324  exit:
   325  	return nil
   326  }
   327  
   328  var charToMul = map[byte]Multiplier{
   329  	'K': mul1,
   330  	'M': mul2,
   331  	'G': mul3,
   332  	'T': mul4,
   333  	'P': mul5,
   334  	'E': mul6,
   335  	'Z': mul7,
   336  	'Y': mul8,
   337  }
   338  
   339  var mulToRat = map[Multiplier]*apd.Decimal{}
   340  
   341  func init() {
   342  	d := apd.New(1, 0)
   343  	b := apd.New(1, 0)
   344  	dm := apd.New(1000, 0)
   345  	bm := apd.New(1024, 0)
   346  
   347  	c := apd.BaseContext
   348  	for i := Multiplier(1); int(i) < len(charToMul); i++ {
   349  		// TODO: may we write to one of the sources?
   350  		var bn, dn apd.Decimal
   351  		_, _ = c.Mul(&dn, d, dm)
   352  		d = &dn
   353  		_, _ = c.Mul(&bn, b, bm)
   354  		b = &bn
   355  		mulToRat[mulDec|i] = d
   356  		mulToRat[mulBin|i] = b
   357  	}
   358  }
   359  

View as plain text