1// Copyright 2018 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package text 6 7// parseNumberValue parses a number from the input and returns a Token object. 8func (d *Decoder) parseNumberValue() (Token, bool) { 9 in := d.in 10 num := parseNumber(in) 11 if num.size == 0 { 12 return Token{}, false 13 } 14 numAttrs := num.kind 15 if num.neg { 16 numAttrs |= isNegative 17 } 18 tok := Token{ 19 kind: Scalar, 20 attrs: numberValue, 21 pos: len(d.orig) - len(d.in), 22 raw: d.in[:num.size], 23 str: num.string(d.in), 24 numAttrs: numAttrs, 25 } 26 d.consume(num.size) 27 return tok, true 28} 29 30const ( 31 numDec uint8 = (1 << iota) / 2 32 numHex 33 numOct 34 numFloat 35) 36 37// number is the result of parsing out a valid number from parseNumber. It 38// contains data for doing float or integer conversion via the strconv package 39// in conjunction with the input bytes. 40type number struct { 41 kind uint8 42 neg bool 43 size int 44 // if neg, this is the length of whitespace and comments between 45 // the minus sign and the rest fo the number literal 46 sep int 47} 48 49func (num number) string(data []byte) string { 50 strSize := num.size 51 last := num.size - 1 52 if num.kind == numFloat && (data[last] == 'f' || data[last] == 'F') { 53 strSize = last 54 } 55 if num.neg && num.sep > 0 { 56 // strip whitespace/comments between negative sign and the rest 57 strLen := strSize - num.sep 58 str := make([]byte, strLen) 59 str[0] = data[0] 60 copy(str[1:], data[num.sep+1:strSize]) 61 return string(str) 62 } 63 return string(data[:strSize]) 64 65} 66 67// parseNumber constructs a number object from given input. It allows for the 68// following patterns: 69// 70// integer: ^-?([1-9][0-9]*|0[xX][0-9a-fA-F]+|0[0-7]*) 71// float: ^-?((0|[1-9][0-9]*)?([.][0-9]*)?([eE][+-]?[0-9]+)?[fF]?) 72// 73// It also returns the number of parsed bytes for the given number, 0 if it is 74// not a number. 75func parseNumber(input []byte) number { 76 kind := numDec 77 var size int 78 var neg bool 79 80 s := input 81 if len(s) == 0 { 82 return number{} 83 } 84 85 // Optional - 86 var sep int 87 if s[0] == '-' { 88 neg = true 89 s = s[1:] 90 size++ 91 // Consume any whitespace or comments between the 92 // negative sign and the rest of the number 93 lenBefore := len(s) 94 s = consume(s, 0) 95 sep = lenBefore - len(s) 96 size += sep 97 if len(s) == 0 { 98 return number{} 99 } 100 } 101 102 switch { 103 case s[0] == '0': 104 if len(s) > 1 { 105 switch { 106 case s[1] == 'x' || s[1] == 'X': 107 // Parse as hex number. 108 kind = numHex 109 n := 2 110 s = s[2:] 111 for len(s) > 0 && (('0' <= s[0] && s[0] <= '9') || 112 ('a' <= s[0] && s[0] <= 'f') || 113 ('A' <= s[0] && s[0] <= 'F')) { 114 s = s[1:] 115 n++ 116 } 117 if n == 2 { 118 return number{} 119 } 120 size += n 121 122 case '0' <= s[1] && s[1] <= '7': 123 // Parse as octal number. 124 kind = numOct 125 n := 2 126 s = s[2:] 127 for len(s) > 0 && '0' <= s[0] && s[0] <= '7' { 128 s = s[1:] 129 n++ 130 } 131 size += n 132 } 133 134 if kind&(numHex|numOct) > 0 { 135 if len(s) > 0 && !isDelim(s[0]) { 136 return number{} 137 } 138 return number{kind: kind, neg: neg, size: size, sep: sep} 139 } 140 } 141 s = s[1:] 142 size++ 143 144 case '1' <= s[0] && s[0] <= '9': 145 n := 1 146 s = s[1:] 147 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { 148 s = s[1:] 149 n++ 150 } 151 size += n 152 153 case s[0] == '.': 154 // Set kind to numFloat to signify the intent to parse as float. And 155 // that it needs to have other digits after '.'. 156 kind = numFloat 157 158 default: 159 return number{} 160 } 161 162 // . followed by 0 or more digits. 163 if len(s) > 0 && s[0] == '.' { 164 n := 1 165 s = s[1:] 166 // If decimal point was before any digits, it should be followed by 167 // other digits. 168 if len(s) == 0 && kind == numFloat { 169 return number{} 170 } 171 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { 172 s = s[1:] 173 n++ 174 } 175 size += n 176 kind = numFloat 177 } 178 179 // e or E followed by an optional - or + and 1 or more digits. 180 if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { 181 kind = numFloat 182 s = s[1:] 183 n := 1 184 if s[0] == '+' || s[0] == '-' { 185 s = s[1:] 186 n++ 187 if len(s) == 0 { 188 return number{} 189 } 190 } 191 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { 192 s = s[1:] 193 n++ 194 } 195 size += n 196 } 197 198 // Optional suffix f or F for floats. 199 if len(s) > 0 && (s[0] == 'f' || s[0] == 'F') { 200 kind = numFloat 201 s = s[1:] 202 size++ 203 } 204 205 // Check that next byte is a delimiter or it is at the end. 206 if len(s) > 0 && !isDelim(s[0]) { 207 return number{} 208 } 209 210 return number{kind: kind, neg: neg, size: size, sep: sep} 211} 212