-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathparser.go
151 lines (142 loc) · 4.01 KB
/
parser.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
//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//
package velocypack
import (
"bytes"
"encoding/json"
"io"
"strconv"
"strings"
)
// ParserOptions controls how the Parser builds Velocypack.
type ParserOptions struct {
// If set, all Array's will be unindexed.
BuildUnindexedArrays bool
// If set, all Objects's will be unindexed.
BuildUnindexedObjects bool
}
// Parser is used to build VPack structures from JSON.
type Parser struct {
options ParserOptions
decoder *json.Decoder
builder *Builder
}
// ParseJSON parses JSON from the given reader and returns the
// VPack equivalent.
func ParseJSON(r io.Reader, options ...ParserOptions) (Slice, error) {
builder := &Builder{}
p := NewParser(r, builder, options...)
if err := p.Parse(); err != nil {
return nil, WithStack(err)
}
slice, err := builder.Slice()
if err != nil {
return nil, WithStack(err)
}
return slice, nil
}
// ParseJSONFromString parses the given JSON string and returns the
// VPack equivalent.
func ParseJSONFromString(json string, options ...ParserOptions) (Slice, error) {
return ParseJSON(strings.NewReader(json), options...)
}
// ParseJSONFromUTF8 parses the given JSON string and returns the
// VPack equivalent.
func ParseJSONFromUTF8(json []byte, options ...ParserOptions) (Slice, error) {
return ParseJSON(bytes.NewReader(json), options...)
}
// NewParser initializes a new Parser with JSON from the given reader and
// it will store the parsers output in the given builder.
func NewParser(r io.Reader, builder *Builder, options ...ParserOptions) *Parser {
d := json.NewDecoder(r)
d.UseNumber()
p := &Parser{
decoder: d,
builder: builder,
}
if len(options) > 0 {
p.options = options[0]
}
return p
}
// Parse JSON from the parsers reader and build VPack structures in the
// parsers builder.
func (p *Parser) Parse() error {
for {
t, err := p.decoder.Token()
if err == io.EOF {
break
} else if serr, ok := err.(*json.SyntaxError); ok {
return WithStack(&ParseError{msg: err.Error(), Offset: serr.Offset})
} else if err != nil {
return WithStack(&ParseError{msg: err.Error()})
}
switch x := t.(type) {
case nil:
if err := p.builder.AddValue(NewNullValue()); err != nil {
return WithStack(err)
}
case bool:
if err := p.builder.AddValue(NewBoolValue(x)); err != nil {
return WithStack(err)
}
case json.Number:
if xu, err := strconv.ParseUint(string(x), 10, 64); err == nil {
if err := p.builder.AddValue(NewUIntValue(xu)); err != nil {
return WithStack(err)
}
} else if xi, err := x.Int64(); err == nil {
if err := p.builder.AddValue(NewIntValue(xi)); err != nil {
return WithStack(err)
}
} else {
if xf, err := x.Float64(); err == nil {
if err := p.builder.AddValue(NewDoubleValue(xf)); err != nil {
return WithStack(err)
}
} else {
return WithStack(&ParseError{msg: err.Error()})
}
}
case string:
if err := p.builder.AddValue(NewStringValue(x)); err != nil {
return WithStack(err)
}
case json.Delim:
switch x {
case '[':
if err := p.builder.OpenArray(p.options.BuildUnindexedArrays); err != nil {
return WithStack(err)
}
case '{':
if err := p.builder.OpenObject(p.options.BuildUnindexedObjects); err != nil {
return WithStack(err)
}
case ']', '}':
if err := p.builder.Close(); err != nil {
return WithStack(err)
}
}
}
}
return nil
}