-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.go
208 lines (180 loc) · 5.05 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
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
204
205
206
207
208
package json5
import (
"fmt"
"math"
"strconv"
"strings"
)
func UnMarshal(json5 string) (interface{}, error) {
tokens := Tokenize(json5)
tokenLen := len(tokens)
if tokenLen == 0 {
return nil, nil
}
i := 1 // start from the second token (skip the first one we already checked)
if tokens[0].Type == TOKEN_LBRACE {
// Parse the object
obj, err := parseObject(tokens, &i, tokenLen)
if err != nil {
return nil, err
}
return obj, nil
}
if tokens[0].Type == TOKEN_LBRACKET {
// Parse the array
arr, err := parseArray(tokens, &i, tokenLen)
if err != nil {
return nil, err
}
return arr, nil
}
if tokens[0].Type == TOKEN_STRING {
return tokens[0].Value, nil
}
if tokens[0].Type == TOKEN_NUMBER {
numberStr := tokens[0].Value
if num, err := strconv.Atoi(numberStr); err == nil {
return num, nil
} else if num, err := strconv.ParseFloat(numberStr, 64); err == nil {
return num, nil
} else {
return nil, fmt.Errorf("invalid number: '%s'", numberStr)
}
}
if tokens[0].Type == TOKEN_TRUE {
return true, nil
}
if tokens[0].Type == TOKEN_FALSE {
return false, nil
}
if tokens[0].Type == TOKEN_NULL {
return nil, nil
}
return nil, fmt.Errorf("expected '{', '[', number, null or boolean but found '%s'", tokens[0].Value)
}
// parseObject parses the tokens as a JSON5 object and returns a map[string]interface{}
func parseObject(tokens []Token, i *int, tokenLen int) (map[string]interface{}, error) {
result := make(map[string]interface{})
for *i < tokenLen {
// If we encounter a closing brace, we're done with the object
if tokens[*i].Type == TOKEN_RBRACE {
*i++ // Move past the closing brace
break
}
// Parse the key (it should be a string or unquoted identifier)
keyToken := tokens[*i]
if keyToken.Type != TOKEN_STRING {
return nil, fmt.Errorf("expected a string for key but found '%s'", keyToken.Value)
}
key := keyToken.Value
*i++
// Expect a colon after the key
if tokens[*i].Type != TOKEN_COLON {
return nil, fmt.Errorf("expected ':' after key '%s' but found '%s'", key, tokens[*i].Value)
}
*i++
// Parse the value for the key
value, err := parseValue(tokens, i, tokenLen)
if err != nil {
return nil, err
}
// Add the key-value pair to the result
result[key] = value
// After the value, we should either find a comma or a closing brace
if tokens[*i].Type == TOKEN_COMMA {
*i++ // Move past the comma
} else if tokens[*i].Type == TOKEN_RBRACE {
*i++ // Move past the closing brace
break
} else {
return nil, fmt.Errorf("expected ',' or '}' but found '%s'", tokens[*i].Value)
}
}
return result, nil
}
// parseArray parses the tokens as a JSON5 array and returns a []interface{}
func parseArray(tokens []Token, i *int, tokenLen int) ([]interface{}, error) {
var result []interface{}
for *i < tokenLen {
// If we encounter a closing bracket, we're done with the array
if tokens[*i].Type == TOKEN_RBRACKET {
*i++ // Move past the closing bracket
break
}
// Parse the next value
value, err := parseValue(tokens, i, tokenLen)
if err != nil {
return nil, err
}
result = append(result, value)
// After the value, we should either find a comma or a closing bracket
if tokens[*i].Type == TOKEN_COMMA {
*i++ // Move past the comma
} else if tokens[*i].Type == TOKEN_RBRACKET {
*i++ // Move past the closing bracket
break
} else {
return nil, fmt.Errorf("expected ',' or ']' but found '%s'", tokens[*i].Value)
}
}
return result, nil
}
// parseValue parses a value (string, number, boolean, null, object, or array)
func parseValue(tokens []Token, i *int, tokenLen int) (interface{}, error) {
if *i >= tokenLen {
return nil, fmt.Errorf("unexpected end of input")
}
switch tokens[*i].Type {
case TOKEN_STRING:
value := tokens[*i].Value
*i++
return value, nil
case TOKEN_NUMBER:
numberStr := tokens[*i].Value
*i++
// Check if the number is hexadecimal
if strings.HasPrefix(numberStr, "0x") || strings.HasPrefix(numberStr, "0X") {
// Parse the hexadecimal number
num, err := strconv.ParseInt(numberStr, 0, 64)
if err != nil {
return nil, fmt.Errorf("invalid hexadecimal number: '%s'", numberStr)
}
if abs(num) < math.MaxInt {
return int(num), nil
}
return num, nil
} else {
// Parse as a regular decimal number (int or float)
if num, err := strconv.Atoi(numberStr); err == nil {
return num, nil
} else if num, err := strconv.ParseFloat(numberStr, 64); err == nil {
return num, nil
} else {
return nil, fmt.Errorf("invalid number: '%s'", numberStr)
}
}
case TOKEN_TRUE:
*i++
return true, nil
case TOKEN_FALSE:
*i++
return false, nil
case TOKEN_NULL:
*i++
return nil, nil // Return nil when the value is the literal `null`
case TOKEN_LBRACE:
*i++ // Move past the opening brace
return parseObject(tokens, i, tokenLen)
case TOKEN_LBRACKET:
*i++ // Move past the opening bracket
return parseArray(tokens, i, tokenLen)
default:
return nil, fmt.Errorf("unexpected token: '%s'", tokens[*i].Value)
}
}
func abs(x int64) int64 {
if x < 0 {
return -x
}
return x
}