blob: 3e58d069eaf72d9ae9a2ac40ac8ac86b6ec16a26 [file] [log] [blame]
Joe Tsai879b18d2018-08-03 17:22:24 -07001// 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 json
6
7import (
8 "bytes"
Herbie Ongd3f8f2d2019-03-06 00:28:23 -08009 "fmt"
Joe Tsai879b18d2018-08-03 17:22:24 -070010 "io"
11 "regexp"
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080012 "strconv"
Joe Tsai879b18d2018-08-03 17:22:24 -070013 "unicode/utf8"
14
Damien Neile89e6242019-05-13 23:55:40 -070015 "google.golang.org/protobuf/internal/errors"
Joe Tsai879b18d2018-08-03 17:22:24 -070016)
17
Herbie Ongc96a79d2019-03-08 10:49:17 -080018// call specifies which Decoder method was invoked.
19type call uint8
20
21const (
22 readCall call = iota
23 peekCall
24)
25
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080026// Decoder is a token-based JSON decoder.
27type Decoder struct {
Herbie Ong8ac9dd22019-03-27 12:20:50 -070028 // lastCall is last method called, either readCall or peekCall.
29 // Initial value is readCall.
Herbie Ongc96a79d2019-03-08 10:49:17 -080030 lastCall call
31
32 // value contains the last read value.
33 value Value
34
35 // err contains the last read error.
36 err error
Joe Tsai879b18d2018-08-03 17:22:24 -070037
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080038 // startStack is a stack containing StartObject and StartArray types. The
39 // top of stack represents the object or the array the current value is
40 // directly located in.
41 startStack []Type
42
43 // orig is used in reporting line and column.
44 orig []byte
45 // in contains the unconsumed input.
46 in []byte
Joe Tsai879b18d2018-08-03 17:22:24 -070047}
48
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080049// NewDecoder returns a Decoder to read the given []byte.
50func NewDecoder(b []byte) *Decoder {
51 return &Decoder{orig: b, in: b}
52}
53
Herbie Ongc96a79d2019-03-08 10:49:17 -080054// Peek looks ahead and returns the next JSON type without advancing a read.
55func (d *Decoder) Peek() Type {
56 defer func() { d.lastCall = peekCall }()
57 if d.lastCall == readCall {
58 d.value, d.err = d.Read()
59 }
60 return d.value.typ
61}
62
63// Read returns the next JSON value. It will return an error if there is no
Herbie Ongdecef412019-04-17 15:47:43 -070064// valid value. For String types containing invalid UTF8 characters, a non-fatal
65// error is returned and caller can call Read for the next value.
Herbie Ongc96a79d2019-03-08 10:49:17 -080066func (d *Decoder) Read() (Value, error) {
67 defer func() { d.lastCall = readCall }()
68 if d.lastCall == peekCall {
69 return d.value, d.err
70 }
71
Herbie Ongdecef412019-04-17 15:47:43 -070072 value, err := d.parseNext()
Damien Neil8c86fc52019-06-19 09:28:29 -070073 if err != nil {
Joe Tsai879b18d2018-08-03 17:22:24 -070074 return Value{}, err
75 }
Herbie Ongdecef412019-04-17 15:47:43 -070076 n := value.size
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080077
78 switch value.typ {
79 case EOF:
80 if len(d.startStack) != 0 ||
Herbie Ongc96a79d2019-03-08 10:49:17 -080081 d.value.typ&Null|Bool|Number|String|EndObject|EndArray == 0 {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080082 return Value{}, io.ErrUnexpectedEOF
83 }
84
85 case Null:
86 if !d.isValueNext() {
87 return Value{}, d.newSyntaxError("unexpected value null")
88 }
89
90 case Bool, Number:
91 if !d.isValueNext() {
Herbie Ong8ac9dd22019-03-27 12:20:50 -070092 return Value{}, d.newSyntaxError("unexpected value %v", value.Raw())
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080093 }
94
95 case String:
96 if d.isValueNext() {
97 break
98 }
99 // Check if this is for an object name.
Herbie Ongc96a79d2019-03-08 10:49:17 -0800100 if d.value.typ&(StartObject|comma) == 0 {
Herbie Ong8ac9dd22019-03-27 12:20:50 -0700101 return Value{}, d.newSyntaxError("unexpected value %v", value.Raw())
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800102 }
103 d.in = d.in[n:]
104 d.consume(0)
Damien Neil5ba0c292019-12-21 09:45:00 -0800105 if len(d.in) == 0 {
106 return Value{}, d.newSyntaxError(`unexpected EOF, missing ":" after object name`)
107 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800108 if c := d.in[0]; c != ':' {
109 return Value{}, d.newSyntaxError(`unexpected character %v, missing ":" after object name`, string(c))
110 }
111 n = 1
112 value.typ = Name
113
114 case StartObject, StartArray:
115 if !d.isValueNext() {
Herbie Ong8ac9dd22019-03-27 12:20:50 -0700116 return Value{}, d.newSyntaxError("unexpected character %v", value.Raw())
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800117 }
118 d.startStack = append(d.startStack, value.typ)
119
120 case EndObject:
121 if len(d.startStack) == 0 ||
Herbie Ongc96a79d2019-03-08 10:49:17 -0800122 d.value.typ == comma ||
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800123 d.startStack[len(d.startStack)-1] != StartObject {
124 return Value{}, d.newSyntaxError("unexpected character }")
125 }
126 d.startStack = d.startStack[:len(d.startStack)-1]
127
128 case EndArray:
129 if len(d.startStack) == 0 ||
Herbie Ongc96a79d2019-03-08 10:49:17 -0800130 d.value.typ == comma ||
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800131 d.startStack[len(d.startStack)-1] != StartArray {
132 return Value{}, d.newSyntaxError("unexpected character ]")
133 }
134 d.startStack = d.startStack[:len(d.startStack)-1]
135
136 case comma:
137 if len(d.startStack) == 0 ||
Herbie Ongc96a79d2019-03-08 10:49:17 -0800138 d.value.typ&(Null|Bool|Number|String|EndObject|EndArray) == 0 {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800139 return Value{}, d.newSyntaxError("unexpected character ,")
140 }
Joe Tsai879b18d2018-08-03 17:22:24 -0700141 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800142
Herbie Ongdecef412019-04-17 15:47:43 -0700143 // Update d.value only after validating value to be in the right sequence.
144 d.value = value
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800145 d.in = d.in[n:]
146
Herbie Ongc96a79d2019-03-08 10:49:17 -0800147 if d.value.typ == comma {
148 return d.Read()
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800149 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700150 return value, nil
Joe Tsai879b18d2018-08-03 17:22:24 -0700151}
152
Herbie Ongdecef412019-04-17 15:47:43 -0700153// Any sequence that looks like a non-delimiter (for error reporting).
154var errRegexp = regexp.MustCompile(`^([-+._a-zA-Z0-9]{1,32}|.)`)
Joe Tsai879b18d2018-08-03 17:22:24 -0700155
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800156// parseNext parses for the next JSON value. It returns a Value object for
Herbie Ongdecef412019-04-17 15:47:43 -0700157// different types, except for Name. It does not handle whether the next value
158// is in a valid sequence or not.
159func (d *Decoder) parseNext() (value Value, err error) {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800160 // Trim leading spaces.
161 d.consume(0)
Joe Tsai879b18d2018-08-03 17:22:24 -0700162
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800163 in := d.in
164 if len(in) == 0 {
Herbie Ongdecef412019-04-17 15:47:43 -0700165 return d.newValue(EOF, nil, 0), nil
Joe Tsai879b18d2018-08-03 17:22:24 -0700166 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800167
168 switch in[0] {
Herbie Ongdecef412019-04-17 15:47:43 -0700169 case 'n':
170 n := matchWithDelim("null", in)
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800171 if n == 0 {
Herbie Ongdecef412019-04-17 15:47:43 -0700172 return Value{}, d.newSyntaxError("invalid value %s", errRegexp.Find(in))
Joe Tsai879b18d2018-08-03 17:22:24 -0700173 }
Herbie Ongdecef412019-04-17 15:47:43 -0700174 return d.newValue(Null, in, n), nil
175
176 case 't':
177 n := matchWithDelim("true", in)
178 if n == 0 {
179 return Value{}, d.newSyntaxError("invalid value %s", errRegexp.Find(in))
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800180 }
Herbie Ongdecef412019-04-17 15:47:43 -0700181 return d.newBoolValue(in, n, true), nil
182
183 case 'f':
184 n := matchWithDelim("false", in)
185 if n == 0 {
186 return Value{}, d.newSyntaxError("invalid value %s", errRegexp.Find(in))
187 }
188 return d.newBoolValue(in, n, false), nil
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800189
Joe Tsai879b18d2018-08-03 17:22:24 -0700190 case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
Herbie Onga3421952019-03-21 18:12:26 -0700191 n, ok := consumeNumber(in)
192 if !ok {
Herbie Ongdecef412019-04-17 15:47:43 -0700193 return Value{}, d.newSyntaxError("invalid number %s", errRegexp.Find(in))
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800194 }
Herbie Ongdecef412019-04-17 15:47:43 -0700195 return d.newValue(Number, in, n), nil
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800196
Joe Tsai879b18d2018-08-03 17:22:24 -0700197 case '"':
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800198 s, n, err := d.parseString(in)
Damien Neil8c86fc52019-06-19 09:28:29 -0700199 if err != nil {
Herbie Ongdecef412019-04-17 15:47:43 -0700200 return Value{}, err
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800201 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700202 return d.newStringValue(in, n, s), nil
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800203
Joe Tsai879b18d2018-08-03 17:22:24 -0700204 case '{':
Herbie Ongdecef412019-04-17 15:47:43 -0700205 return d.newValue(StartObject, in, 1), nil
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800206
207 case '}':
Herbie Ongdecef412019-04-17 15:47:43 -0700208 return d.newValue(EndObject, in, 1), nil
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800209
210 case '[':
Herbie Ongdecef412019-04-17 15:47:43 -0700211 return d.newValue(StartArray, in, 1), nil
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800212
213 case ']':
Herbie Ongdecef412019-04-17 15:47:43 -0700214 return d.newValue(EndArray, in, 1), nil
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800215
216 case ',':
Herbie Ongdecef412019-04-17 15:47:43 -0700217 return d.newValue(comma, in, 1), nil
Joe Tsai879b18d2018-08-03 17:22:24 -0700218 }
Herbie Ongdecef412019-04-17 15:47:43 -0700219 return Value{}, d.newSyntaxError("invalid value %s", errRegexp.Find(in))
Joe Tsai879b18d2018-08-03 17:22:24 -0700220}
221
Herbie Ongdecef412019-04-17 15:47:43 -0700222// position returns line and column number of index in given orig slice.
223func position(orig []byte, idx int) (int, int) {
224 b := orig[:idx]
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800225 line := bytes.Count(b, []byte("\n")) + 1
226 if i := bytes.LastIndexByte(b, '\n'); i >= 0 {
227 b = b[i+1:]
Joe Tsai879b18d2018-08-03 17:22:24 -0700228 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800229 column := utf8.RuneCount(b) + 1 // ignore multi-rune characters
230 return line, column
Joe Tsai879b18d2018-08-03 17:22:24 -0700231}
232
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800233// newSyntaxError returns an error with line and column information useful for
234// syntax errors.
235func (d *Decoder) newSyntaxError(f string, x ...interface{}) error {
236 e := errors.New(f, x...)
Herbie Ongdecef412019-04-17 15:47:43 -0700237 line, column := position(d.orig, len(d.orig)-len(d.in))
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800238 return errors.New("syntax error (line %d:%d): %v", line, column, e)
Joe Tsai879b18d2018-08-03 17:22:24 -0700239}
240
Herbie Ongdecef412019-04-17 15:47:43 -0700241// matchWithDelim matches s with the input b and verifies that the match
Joe Tsai879b18d2018-08-03 17:22:24 -0700242// terminates with a delimiter of some form (e.g., r"[^-+_.a-zA-Z0-9]").
Herbie Ongdecef412019-04-17 15:47:43 -0700243// As a special case, EOF is considered a delimiter. It returns the length of s
244// if there is a match, else 0.
245func matchWithDelim(s string, b []byte) int {
246 if !bytes.HasPrefix(b, []byte(s)) {
247 return 0
248 }
249
250 n := len(s)
251 if n < len(b) && isNotDelim(b[n]) {
252 return 0
Joe Tsai879b18d2018-08-03 17:22:24 -0700253 }
254 return n
255}
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800256
257// isNotDelim returns true if given byte is a not delimiter character.
258func isNotDelim(c byte) bool {
259 return (c == '-' || c == '+' || c == '.' || c == '_' ||
260 ('a' <= c && c <= 'z') ||
261 ('A' <= c && c <= 'Z') ||
262 ('0' <= c && c <= '9'))
263}
264
265// consume consumes n bytes of input and any subsequent whitespace.
266func (d *Decoder) consume(n int) {
267 d.in = d.in[n:]
268 for len(d.in) > 0 {
269 switch d.in[0] {
270 case ' ', '\n', '\r', '\t':
271 d.in = d.in[1:]
272 default:
273 return
274 }
275 }
276}
277
278// isValueNext returns true if next type should be a JSON value: Null,
279// Number, String or Bool.
280func (d *Decoder) isValueNext() bool {
281 if len(d.startStack) == 0 {
Herbie Ongc96a79d2019-03-08 10:49:17 -0800282 return d.value.typ == 0
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800283 }
284
285 start := d.startStack[len(d.startStack)-1]
286 switch start {
287 case StartObject:
Herbie Ongc96a79d2019-03-08 10:49:17 -0800288 return d.value.typ&Name != 0
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800289 case StartArray:
Herbie Ongc96a79d2019-03-08 10:49:17 -0800290 return d.value.typ&(StartArray|comma) != 0
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800291 }
292 panic(fmt.Sprintf(
293 "unreachable logic in Decoder.isValueNext, lastType: %v, startStack: %v",
Herbie Ongc96a79d2019-03-08 10:49:17 -0800294 d.value.typ, start))
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800295}
296
Herbie Ong670d8082019-03-31 19:10:33 -0700297// newValue constructs a Value for given Type.
Herbie Ongdecef412019-04-17 15:47:43 -0700298func (d *Decoder) newValue(typ Type, input []byte, size int) Value {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800299 return Value{
Herbie Ongdecef412019-04-17 15:47:43 -0700300 typ: typ,
301 input: d.orig,
302 start: len(d.orig) - len(input),
303 size: size,
Herbie Ong670d8082019-03-31 19:10:33 -0700304 }
305}
306
307// newBoolValue constructs a Value for a JSON boolean.
Herbie Ongdecef412019-04-17 15:47:43 -0700308func (d *Decoder) newBoolValue(input []byte, size int, b bool) Value {
Herbie Ong670d8082019-03-31 19:10:33 -0700309 return Value{
Herbie Ongdecef412019-04-17 15:47:43 -0700310 typ: Bool,
311 input: d.orig,
312 start: len(d.orig) - len(input),
313 size: size,
314 boo: b,
Herbie Ong670d8082019-03-31 19:10:33 -0700315 }
316}
317
318// newStringValue constructs a Value for a JSON string.
Herbie Ongdecef412019-04-17 15:47:43 -0700319func (d *Decoder) newStringValue(input []byte, size int, s string) Value {
Herbie Ong670d8082019-03-31 19:10:33 -0700320 return Value{
Herbie Ongdecef412019-04-17 15:47:43 -0700321 typ: String,
322 input: d.orig,
323 start: len(d.orig) - len(input),
324 size: size,
325 str: s,
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800326 }
327}
328
Herbie Ong8ac9dd22019-03-27 12:20:50 -0700329// Clone returns a copy of the Decoder for use in reading ahead the next JSON
330// object, array or other values without affecting current Decoder.
331func (d *Decoder) Clone() *Decoder {
332 ret := *d
333 ret.startStack = append([]Type(nil), ret.startStack...)
334 return &ret
335}
336
Herbie Ongdecef412019-04-17 15:47:43 -0700337// Value provides a parsed JSON type and value.
338//
339// The original input slice is stored in this struct in order to compute for
340// position as needed. The raw JSON value is derived from the original input
341// slice given start and size.
342//
Herbie Ong670d8082019-03-31 19:10:33 -0700343// For JSON boolean and string, it holds the converted value in boo and str
Herbie Ongdecef412019-04-17 15:47:43 -0700344// fields respectively. For JSON number, the raw JSON value holds a valid number
345// which is converted only in Int or Float. Other JSON types do not require any
Herbie Ong670d8082019-03-31 19:10:33 -0700346// additional data.
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800347type Value struct {
Herbie Ongdecef412019-04-17 15:47:43 -0700348 typ Type
349 input []byte
350 start int
351 size int
352 boo bool
353 str string
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800354}
355
356func (v Value) newError(f string, x ...interface{}) error {
357 e := errors.New(f, x...)
Herbie Ongdecef412019-04-17 15:47:43 -0700358 line, col := v.Position()
359 return errors.New("error (line %d:%d): %v", line, col, e)
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800360}
361
362// Type returns the JSON type.
363func (v Value) Type() Type {
364 return v.typ
365}
366
367// Position returns the line and column of the value.
368func (v Value) Position() (int, int) {
Herbie Ongdecef412019-04-17 15:47:43 -0700369 return position(v.input, v.start)
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800370}
371
372// Bool returns the bool value if token is Bool, else it will return an error.
373func (v Value) Bool() (bool, error) {
374 if v.typ != Bool {
Herbie Ongdecef412019-04-17 15:47:43 -0700375 return false, v.newError("%s is not a bool", v.Raw())
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800376 }
Herbie Ong670d8082019-03-31 19:10:33 -0700377 return v.boo, nil
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800378}
379
380// String returns the string value for a JSON string token or the read value in
381// string if token is not a string.
382func (v Value) String() string {
383 if v.typ != String {
Herbie Ongdecef412019-04-17 15:47:43 -0700384 return v.Raw()
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800385 }
Herbie Ong670d8082019-03-31 19:10:33 -0700386 return v.str
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800387}
388
389// Name returns the object name if token is Name, else it will return an error.
390func (v Value) Name() (string, error) {
391 if v.typ != Name {
Herbie Ongdecef412019-04-17 15:47:43 -0700392 return "", v.newError("%s is not an object name", v.Raw())
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800393 }
Herbie Ong670d8082019-03-31 19:10:33 -0700394 return v.str, nil
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800395}
396
Herbie Ong8ac9dd22019-03-27 12:20:50 -0700397// Raw returns the read value in string.
398func (v Value) Raw() string {
Herbie Ongdecef412019-04-17 15:47:43 -0700399 return string(v.input[v.start : v.start+v.size])
Herbie Ong8ac9dd22019-03-27 12:20:50 -0700400}
401
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800402// Float returns the floating-point number if token is Number, else it will
403// return an error.
404//
405// The floating-point precision is specified by the bitSize parameter: 32 for
406// float32 or 64 for float64. If bitSize=32, the result still has type float64,
407// but it will be convertible to float32 without changing its value. It will
408// return an error if the number exceeds the floating point limits for given
409// bitSize.
410func (v Value) Float(bitSize int) (float64, error) {
411 if v.typ != Number {
Herbie Ongdecef412019-04-17 15:47:43 -0700412 return 0, v.newError("%s is not a number", v.Raw())
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800413 }
Herbie Ongdecef412019-04-17 15:47:43 -0700414 f, err := strconv.ParseFloat(v.Raw(), bitSize)
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800415 if err != nil {
416 return 0, v.newError("%v", err)
417 }
418 return f, nil
419}
420
421// Int returns the signed integer number if token is Number, else it will
422// return an error.
423//
424// The given bitSize specifies the integer type that the result must fit into.
425// It returns an error if the number is not an integer value or if the result
426// exceeds the limits for given bitSize.
427func (v Value) Int(bitSize int) (int64, error) {
428 s, err := v.getIntStr()
429 if err != nil {
430 return 0, err
431 }
432 n, err := strconv.ParseInt(s, 10, bitSize)
433 if err != nil {
434 return 0, v.newError("%v", err)
435 }
436 return n, nil
437}
438
439// Uint returns the signed integer number if token is Number, else it will
440// return an error.
441//
442// The given bitSize specifies the unsigned integer type that the result must
443// fit into. It returns an error if the number is not an unsigned integer value
444// or if the result exceeds the limits for given bitSize.
445func (v Value) Uint(bitSize int) (uint64, error) {
446 s, err := v.getIntStr()
447 if err != nil {
448 return 0, err
449 }
450 n, err := strconv.ParseUint(s, 10, bitSize)
451 if err != nil {
452 return 0, v.newError("%v", err)
453 }
454 return n, nil
455}
456
457func (v Value) getIntStr() (string, error) {
458 if v.typ != Number {
459 return "", v.newError("%s is not a number", v.input)
460 }
Herbie Ongdecef412019-04-17 15:47:43 -0700461 parts, ok := parseNumber(v.input[v.start : v.start+v.size])
Herbie Onga3421952019-03-21 18:12:26 -0700462 if !ok {
463 return "", v.newError("%s is not a number", v.input)
464 }
465 num, ok := normalizeToIntString(parts)
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800466 if !ok {
467 return "", v.newError("cannot convert %s to integer", v.input)
468 }
469 return num, nil
470}