blob: f2d90b78999f30232bf7ad5ce590344a2d5dc6b3 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// 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}