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