aboutsummaryrefslogtreecommitdiff
path: root/internal/encoding/json/decode_token.go
blob: 50578d6593c10c14c1d3923cb72a8017775f79af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package json

import (
	"bytes"
	"fmt"
	"strconv"
)

// Kind represents a token kind expressible in the JSON format.
type Kind uint16

const (
	Invalid Kind = (1 << iota) / 2
	EOF
	Null
	Bool
	Number
	String
	Name
	ObjectOpen
	ObjectClose
	ArrayOpen
	ArrayClose

	// comma is only for parsing in between tokens and
	// does not need to be exported.
	comma
)

func (k Kind) String() string {
	switch k {
	case EOF:
		return "eof"
	case Null:
		return "null"
	case Bool:
		return "bool"
	case Number:
		return "number"
	case String:
		return "string"
	case ObjectOpen:
		return "{"
	case ObjectClose:
		return "}"
	case Name:
		return "name"
	case ArrayOpen:
		return "["
	case ArrayClose:
		return "]"
	case comma:
		return ","
	}
	return "<invalid>"
}

// Token provides a parsed token kind and value.
//
// Values are provided by the difference accessor methods. The accessor methods
// Name, Bool, and ParsedString will panic if called on the wrong kind. There
// are different accessor methods for the Number kind for converting to the
// appropriate Go numeric type and those methods have the ok return value.
type Token struct {
	// Token kind.
	kind Kind
	// pos provides the position of the token in the original input.
	pos int
	// raw bytes of the serialized token.
	// This is a subslice into the original input.
	raw []byte
	// boo is parsed boolean value.
	boo bool
	// str is parsed string value.
	str string
}

// Kind returns the token kind.
func (t Token) Kind() Kind {
	return t.kind
}

// RawString returns the read value in string.
func (t Token) RawString() string {
	return string(t.raw)
}

// Pos returns the token position from the input.
func (t Token) Pos() int {
	return t.pos
}

// Name returns the object name if token is Name, else it panics.
func (t Token) Name() string {
	if t.kind == Name {
		return t.str
	}
	panic(fmt.Sprintf("Token is not a Name: %v", t.RawString()))
}

// Bool returns the bool value if token kind is Bool, else it panics.
func (t Token) Bool() bool {
	if t.kind == Bool {
		return t.boo
	}
	panic(fmt.Sprintf("Token is not a Bool: %v", t.RawString()))
}

// ParsedString returns the string value for a JSON string token or the read
// value in string if token is not a string.
func (t Token) ParsedString() string {
	if t.kind == String {
		return t.str
	}
	panic(fmt.Sprintf("Token is not a String: %v", t.RawString()))
}

// Float returns the floating-point number if token kind is Number.
//
// The floating-point precision is specified by the bitSize parameter: 32 for
// float32 or 64 for float64. If bitSize=32, the result still has type float64,
// but it will be convertible to float32 without changing its value. It will
// return false if the number exceeds the floating point limits for given
// bitSize.
func (t Token) Float(bitSize int) (float64, bool) {
	if t.kind != Number {
		return 0, false
	}
	f, err := strconv.ParseFloat(t.RawString(), bitSize)
	if err != nil {
		return 0, false
	}
	return f, true
}

// Int returns the signed integer number if token is Number.
//
// The given bitSize specifies the integer type that the result must fit into.
// It returns false if the number is not an integer value or if the result
// exceeds the limits for given bitSize.
func (t Token) Int(bitSize int) (int64, bool) {
	s, ok := t.getIntStr()
	if !ok {
		return 0, false
	}
	n, err := strconv.ParseInt(s, 10, bitSize)
	if err != nil {
		return 0, false
	}
	return n, true
}

// Uint returns the signed integer number if token is Number.
//
// The given bitSize specifies the unsigned integer type that the result must
// fit into. It returns false if the number is not an unsigned integer value
// or if the result exceeds the limits for given bitSize.
func (t Token) Uint(bitSize int) (uint64, bool) {
	s, ok := t.getIntStr()
	if !ok {
		return 0, false
	}
	n, err := strconv.ParseUint(s, 10, bitSize)
	if err != nil {
		return 0, false
	}
	return n, true
}

func (t Token) getIntStr() (string, bool) {
	if t.kind != Number {
		return "", false
	}
	parts, ok := parseNumberParts(t.raw)
	if !ok {
		return "", false
	}
	return normalizeToIntString(parts)
}

// TokenEquals returns true if given Tokens are equal, else false.
func TokenEquals(x, y Token) bool {
	return x.kind == y.kind &&
		x.pos == y.pos &&
		bytes.Equal(x.raw, y.raw) &&
		x.boo == y.boo &&
		x.str == y.str
}