123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- // Copyright 2018 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 text
- // parseNumberValue parses a number from the input and returns a Token object.
- func (d *Decoder) parseNumberValue() (Token, bool) {
- in := d.in
- num := parseNumber(in)
- if num.size == 0 {
- return Token{}, false
- }
- numAttrs := num.kind
- if num.neg {
- numAttrs |= isNegative
- }
- tok := Token{
- kind: Scalar,
- attrs: numberValue,
- pos: len(d.orig) - len(d.in),
- raw: d.in[:num.size],
- str: num.string(d.in),
- numAttrs: numAttrs,
- }
- d.consume(num.size)
- return tok, true
- }
- const (
- numDec uint8 = (1 << iota) / 2
- numHex
- numOct
- numFloat
- )
- // number is the result of parsing out a valid number from parseNumber. It
- // contains data for doing float or integer conversion via the strconv package
- // in conjunction with the input bytes.
- type number struct {
- kind uint8
- neg bool
- size int
- // if neg, this is the length of whitespace and comments between
- // the minus sign and the rest fo the number literal
- sep int
- }
- func (num number) string(data []byte) string {
- strSize := num.size
- last := num.size - 1
- if num.kind == numFloat && (data[last] == 'f' || data[last] == 'F') {
- strSize = last
- }
- if num.neg && num.sep > 0 {
- // strip whitespace/comments between negative sign and the rest
- strLen := strSize - num.sep
- str := make([]byte, strLen)
- str[0] = data[0]
- copy(str[1:], data[num.sep+1:strSize])
- return string(str)
- }
- return string(data[:strSize])
- }
- // parseNumber constructs a number object from given input. It allows for the
- // following patterns:
- //
- // integer: ^-?([1-9][0-9]*|0[xX][0-9a-fA-F]+|0[0-7]*)
- // float: ^-?((0|[1-9][0-9]*)?([.][0-9]*)?([eE][+-]?[0-9]+)?[fF]?)
- //
- // It also returns the number of parsed bytes for the given number, 0 if it is
- // not a number.
- func parseNumber(input []byte) number {
- kind := numDec
- var size int
- var neg bool
- s := input
- if len(s) == 0 {
- return number{}
- }
- // Optional -
- var sep int
- if s[0] == '-' {
- neg = true
- s = s[1:]
- size++
- // Consume any whitespace or comments between the
- // negative sign and the rest of the number
- lenBefore := len(s)
- s = consume(s, 0)
- sep = lenBefore - len(s)
- size += sep
- if len(s) == 0 {
- return number{}
- }
- }
- switch {
- case s[0] == '0':
- if len(s) > 1 {
- switch {
- case s[1] == 'x' || s[1] == 'X':
- // Parse as hex number.
- kind = numHex
- n := 2
- s = s[2:]
- for len(s) > 0 && (('0' <= s[0] && s[0] <= '9') ||
- ('a' <= s[0] && s[0] <= 'f') ||
- ('A' <= s[0] && s[0] <= 'F')) {
- s = s[1:]
- n++
- }
- if n == 2 {
- return number{}
- }
- size += n
- case '0' <= s[1] && s[1] <= '7':
- // Parse as octal number.
- kind = numOct
- n := 2
- s = s[2:]
- for len(s) > 0 && '0' <= s[0] && s[0] <= '7' {
- s = s[1:]
- n++
- }
- size += n
- }
- if kind&(numHex|numOct) > 0 {
- if len(s) > 0 && !isDelim(s[0]) {
- return number{}
- }
- return number{kind: kind, neg: neg, size: size, sep: sep}
- }
- }
- s = s[1:]
- size++
- case '1' <= s[0] && s[0] <= '9':
- n := 1
- s = s[1:]
- for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
- s = s[1:]
- n++
- }
- size += n
- case s[0] == '.':
- // Set kind to numFloat to signify the intent to parse as float. And
- // that it needs to have other digits after '.'.
- kind = numFloat
- default:
- return number{}
- }
- // . followed by 0 or more digits.
- if len(s) > 0 && s[0] == '.' {
- n := 1
- s = s[1:]
- // If decimal point was before any digits, it should be followed by
- // other digits.
- if len(s) == 0 && kind == numFloat {
- return number{}
- }
- for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
- s = s[1:]
- n++
- }
- size += n
- kind = numFloat
- }
- // e or E followed by an optional - or + and 1 or more digits.
- if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
- kind = numFloat
- s = s[1:]
- n := 1
- if s[0] == '+' || s[0] == '-' {
- s = s[1:]
- n++
- if len(s) == 0 {
- return number{}
- }
- }
- for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
- s = s[1:]
- n++
- }
- size += n
- }
- // Optional suffix f or F for floats.
- if len(s) > 0 && (s[0] == 'f' || s[0] == 'F') {
- kind = numFloat
- s = s[1:]
- size++
- }
- // Check that next byte is a delimiter or it is at the end.
- if len(s) > 0 && !isDelim(s[0]) {
- return number{}
- }
- return number{kind: kind, neg: neg, size: size, sep: sep}
- }
|