1// Copyright 2019 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"
9	"fmt"
10	"strconv"
11)
12
13// Kind represents a token kind expressible in the JSON format.
14type Kind uint16
15
16const (
17	Invalid Kind = (1 << iota) / 2
18	EOF
19	Null
20	Bool
21	Number
22	String
23	Name
24	ObjectOpen
25	ObjectClose
26	ArrayOpen
27	ArrayClose
28
29	// comma is only for parsing in between tokens and
30	// does not need to be exported.
31	comma
32)
33
34func (k Kind) String() string {
35	switch k {
36	case EOF:
37		return "eof"
38	case Null:
39		return "null"
40	case Bool:
41		return "bool"
42	case Number:
43		return "number"
44	case String:
45		return "string"
46	case ObjectOpen:
47		return "{"
48	case ObjectClose:
49		return "}"
50	case Name:
51		return "name"
52	case ArrayOpen:
53		return "["
54	case ArrayClose:
55		return "]"
56	case comma:
57		return ","
58	}
59	return "<invalid>"
60}
61
62// Token provides a parsed token kind and value.
63//
64// Values are provided by the difference accessor methods. The accessor methods
65// Name, Bool, and ParsedString will panic if called on the wrong kind. There
66// are different accessor methods for the Number kind for converting to the
67// appropriate Go numeric type and those methods have the ok return value.
68type Token struct {
69	// Token kind.
70	kind Kind
71	// pos provides the position of the token in the original input.
72	pos int
73	// raw bytes of the serialized token.
74	// This is a subslice into the original input.
75	raw []byte
76	// boo is parsed boolean value.
77	boo bool
78	// str is parsed string value.
79	str string
80}
81
82// Kind returns the token kind.
83func (t Token) Kind() Kind {
84	return t.kind
85}
86
87// RawString returns the read value in string.
88func (t Token) RawString() string {
89	return string(t.raw)
90}
91
92// Pos returns the token position from the input.
93func (t Token) Pos() int {
94	return t.pos
95}
96
97// Name returns the object name if token is Name, else it panics.
98func (t Token) Name() string {
99	if t.kind == Name {
100		return t.str
101	}
102	panic(fmt.Sprintf("Token is not a Name: %v", t.RawString()))
103}
104
105// Bool returns the bool value if token kind is Bool, else it panics.
106func (t Token) Bool() bool {
107	if t.kind == Bool {
108		return t.boo
109	}
110	panic(fmt.Sprintf("Token is not a Bool: %v", t.RawString()))
111}
112
113// ParsedString returns the string value for a JSON string token or the read
114// value in string if token is not a string.
115func (t Token) ParsedString() string {
116	if t.kind == String {
117		return t.str
118	}
119	panic(fmt.Sprintf("Token is not a String: %v", t.RawString()))
120}
121
122// Float returns the floating-point number if token kind is Number.
123//
124// The floating-point precision is specified by the bitSize parameter: 32 for
125// float32 or 64 for float64. If bitSize=32, the result still has type float64,
126// but it will be convertible to float32 without changing its value. It will
127// return false if the number exceeds the floating point limits for given
128// bitSize.
129func (t Token) Float(bitSize int) (float64, bool) {
130	if t.kind != Number {
131		return 0, false
132	}
133	f, err := strconv.ParseFloat(t.RawString(), bitSize)
134	if err != nil {
135		return 0, false
136	}
137	return f, true
138}
139
140// Int returns the signed integer number if token is Number.
141//
142// The given bitSize specifies the integer type that the result must fit into.
143// It returns false if the number is not an integer value or if the result
144// exceeds the limits for given bitSize.
145func (t Token) Int(bitSize int) (int64, bool) {
146	s, ok := t.getIntStr()
147	if !ok {
148		return 0, false
149	}
150	n, err := strconv.ParseInt(s, 10, bitSize)
151	if err != nil {
152		return 0, false
153	}
154	return n, true
155}
156
157// Uint returns the signed integer number if token is Number.
158//
159// The given bitSize specifies the unsigned integer type that the result must
160// fit into. It returns false if the number is not an unsigned integer value
161// or if the result exceeds the limits for given bitSize.
162func (t Token) Uint(bitSize int) (uint64, bool) {
163	s, ok := t.getIntStr()
164	if !ok {
165		return 0, false
166	}
167	n, err := strconv.ParseUint(s, 10, bitSize)
168	if err != nil {
169		return 0, false
170	}
171	return n, true
172}
173
174func (t Token) getIntStr() (string, bool) {
175	if t.kind != Number {
176		return "", false
177	}
178	parts, ok := parseNumberParts(t.raw)
179	if !ok {
180		return "", false
181	}
182	return normalizeToIntString(parts)
183}
184
185// TokenEquals returns true if given Tokens are equal, else false.
186func TokenEquals(x, y Token) bool {
187	return x.kind == y.kind &&
188		x.pos == y.pos &&
189		bytes.Equal(x.raw, y.raw) &&
190		x.boo == y.boo &&
191		x.str == y.str
192}
193