-
Notifications
You must be signed in to change notification settings - Fork 0
/
token.go
203 lines (173 loc) · 4.32 KB
/
token.go
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
193
194
195
196
197
198
199
200
201
202
203
package main
import (
"bytes"
"encoding/json"
"fmt"
"unicode"
)
// A token is a chunk of text from a statement with a type
type token struct {
text string
typ tokenTyp
}
// A tokenTyp identifies what kind of token something is
type tokenTyp int
const (
// A bare word is a unquoted key; like 'foo' in json.foo = 1;
typBare tokenTyp = iota
// Numeric key; like '2' in json[2] = "foo";
typNumericKey
// A quoted key; like 'foo bar' in json["foo bar"] = 2;
typQuotedKey
// Punctuation types
typDot // .
typLBrace // [
typRBrace // ]
typEquals // =
typSemi // ;
typComma // ,
// Value types
typString // "foo"
typNumber // 4
typTrue // true
typFalse // false
typNull // null
typEmptyArray // []
typEmptyObject // {}
// Ignored token
typIgnored
// Error token
typError
)
// a sprintFn adds color to its input
type sprintFn func(...interface{}) string
// mapping of token types to the appropriate color sprintFn
var sprintFns = map[tokenTyp]sprintFn{
typBare: bareColor.SprintFunc(),
typNumericKey: numColor.SprintFunc(),
typQuotedKey: strColor.SprintFunc(),
typLBrace: braceColor.SprintFunc(),
typRBrace: braceColor.SprintFunc(),
typString: strColor.SprintFunc(),
typNumber: numColor.SprintFunc(),
typTrue: boolColor.SprintFunc(),
typFalse: boolColor.SprintFunc(),
typNull: boolColor.SprintFunc(),
typEmptyArray: braceColor.SprintFunc(),
typEmptyObject: braceColor.SprintFunc(),
}
// isValue returns true if the token is a valid value type
func (t token) isValue() bool {
switch t.typ {
case typString, typNumber, typTrue, typFalse, typNull, typEmptyArray, typEmptyObject:
return true
default:
return false
}
}
// isPunct returns true if the token is a punctuation type
func (t token) isPunct() bool {
switch t.typ {
case typDot, typLBrace, typRBrace, typEquals, typSemi, typComma:
return true
default:
return false
}
}
// format returns the formatted version of the token text
func (t token) format() string {
if t.typ == typEquals {
return " " + t.text + " "
}
return t.text
}
// formatColor returns the colored formatted version of the token text
func (t token) formatColor() string {
text := t.text
if t.typ == typEquals {
text = " " + text + " "
}
fn, ok := sprintFns[t.typ]
if ok {
return fn(text)
}
return text
}
// valueTokenFromInterface takes any valid value and
// returns a value token to represent it
func valueTokenFromInterface(v interface{}) token {
switch vv := v.(type) {
case map[string]interface{}:
return token{"{}", typEmptyObject}
case []interface{}:
return token{"[]", typEmptyArray}
case json.Number:
return token{vv.String(), typNumber}
case string:
return token{quoteString(vv), typString}
case bool:
if vv {
return token{"true", typTrue}
}
return token{"false", typFalse}
case nil:
return token{"null", typNull}
default:
return token{"", typError}
}
}
// quoteString takes a string and returns a quoted and
// escaped string valid for use in gron output
func quoteString(s string) string {
out := &bytes.Buffer{}
// bytes.Buffer never returns errors on these methods.
// errors are explicitly ignored to keep the linter
// happy. A price worth paying so that the linter
// remains useful.
_ = out.WriteByte('"')
for _, r := range s {
if r == '\\' || r == '"' {
_ = out.WriteByte('\\')
_, _ = out.WriteRune(r)
continue
}
// \u2028 and \u2029 are separator runes that are not valid
// in javascript strings so they must be escaped.
// See http://timelessrepo.com/json-isnt-a-javascript-subset
if r == '\u2028' {
_, _ = out.WriteString(`\u2028`)
continue
}
if r == '\u2029' {
_, _ = out.WriteString(`\u2029`)
continue
}
// Any other control runes must be escaped
if unicode.IsControl(r) {
switch r {
case '\b':
_ = out.WriteByte('\\')
_ = out.WriteByte('b')
case '\f':
_ = out.WriteByte('\\')
_ = out.WriteByte('f')
case '\n':
_ = out.WriteByte('\\')
_ = out.WriteByte('n')
case '\r':
_ = out.WriteByte('\\')
_ = out.WriteByte('r')
case '\t':
_ = out.WriteByte('\\')
_ = out.WriteByte('t')
default:
_, _ = out.WriteString(fmt.Sprintf(`\u%04X`, r))
}
continue
}
// Unescaped rune
_, _ = out.WriteRune(r)
}
_ = out.WriteByte('"')
return out.String()
}