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