v2 / vlib / x / json2 / check.v
321 lines · 258 sloc · 8.64 KB · 3d60410b605d001e54f280070d5f952da9de1112
Raw
1module json2
2
3// increment checks eof and increments checker by one
4@[inline; markused]
5fn (mut checker Decoder) increment(message string) ! {
6 if checker.checker_idx + 1 == checker.json.len {
7 if message == '' {
8 return Error{}
9 }
10 checker.checker_error('EOF: ' + message)!
11 }
12 checker.checker_idx++
13}
14
15// skip_whitespace checks eof and increments checker until next non whitespace character
16@[inline; markused]
17fn (mut checker Decoder) skip_whitespace(message string) ! {
18 for checker.json[checker.checker_idx] in whitespace_chars {
19 checker.increment(message)!
20 }
21}
22
23// check_json_format checks if the JSON string is valid and updates the decoder state.
24@[markused]
25fn (mut checker Decoder) check_json_format() ! {
26 checker.skip_whitespace('empty json')!
27
28 start_idx_position := checker.checker_idx
29
30 mut actual_value_info_pointer := unsafe { &ValueInfo(nil) }
31
32 match checker.json[checker.checker_idx] {
33 `"` {
34 checker.values_info.push(ValueInfo{
35 position: checker.checker_idx
36 value_kind: .string
37 })
38
39 actual_value_info_pointer = checker.values_info.last()
40
41 checker.check_string()!
42 }
43 `-`, `0`...`9` {
44 checker.values_info.push(ValueInfo{
45 position: checker.checker_idx
46 value_kind: .number
47 })
48
49 actual_value_info_pointer = checker.values_info.last()
50
51 checker.check_number()!
52 }
53 `t`, `f` {
54 checker.values_info.push(ValueInfo{
55 position: checker.checker_idx
56 value_kind: .boolean
57 })
58
59 actual_value_info_pointer = checker.values_info.last()
60
61 checker.check_boolean()!
62 }
63 `n` {
64 checker.values_info.push(ValueInfo{
65 position: checker.checker_idx
66 value_kind: .null
67 })
68
69 actual_value_info_pointer = checker.values_info.last()
70
71 checker.check_null()!
72 }
73 `[` {
74 checker.values_info.push(ValueInfo{
75 position: checker.checker_idx
76 value_kind: .array
77 })
78
79 actual_value_info_pointer = checker.values_info.last()
80
81 checker.check_array()!
82 }
83 `{` {
84 checker.values_info.push(ValueInfo{
85 position: checker.checker_idx
86 value_kind: .object
87 })
88
89 actual_value_info_pointer = checker.values_info.last()
90
91 checker.check_object()!
92 }
93 else {
94 checker.checker_error('unknown value kind')!
95 }
96 }
97
98 actual_value_info_pointer.length = checker.checker_idx + 1 - start_idx_position
99
100 checker.increment('') or { return }
101 checker.skip_whitespace('') or { return }
102
103 if checker.json[checker.checker_idx] !in [`,`, `:`, `}`, `]`] {
104 checker.checker_error('invalid value. Unexpected character after ${actual_value_info_pointer.value_kind} end')!
105 }
106}
107
108@[markused]
109fn (mut checker Decoder) check_string() ! {
110 checker.increment('string not closed')!
111
112 // check if the JSON string is a valid escape sequence
113 for checker.json[checker.checker_idx] != `"` {
114 if checker.json[checker.checker_idx] == `\\` {
115 checker.increment('invalid escape sequence')!
116 escaped_char := checker.json[checker.checker_idx]
117 match escaped_char {
118 `/`, `b`, `f`, `n`, `r`, `t`, `"`, `\\` {}
119 `u` {
120 // check if the JSON string is a valid unicode escape sequence
121 escaped_char_last_index := checker.checker_idx + 4
122
123 if escaped_char_last_index < checker.json.len {
124 // 2 bytes for the unicode escape sequence `\u`
125 checker.increment('invalid escape sequence')!
126
127 for checker.checker_idx < escaped_char_last_index {
128 match checker.json[checker.checker_idx] {
129 `0`...`9`, `a`...`f`, `A`...`F` {
130 checker.increment('invalid unicode escape sequence')!
131 }
132 else {
133 return checker.checker_error('invalid unicode escape sequence')
134 }
135 }
136 }
137 continue
138 } else {
139 return checker.checker_error('short unicode escape sequence ${checker.json[checker.checker_idx - 1..checker.json.len - 1]}')
140 }
141 }
142 else {
143 return checker.checker_error('unknown escape sequence')
144 }
145 }
146 }
147 checker.increment('string not closed')!
148 }
149}
150
151@[markused]
152fn (mut checker Decoder) check_number() ! {
153 // check if the JSON string is a valid float or integer
154 if checker.json[checker.checker_idx] == `-` {
155 checker.increment('expected digit')!
156 }
157
158 // integer part
159 if checker.json[checker.checker_idx] == `0` {
160 checker.increment('') or { return }
161 } else if checker.json[checker.checker_idx] >= `1` && checker.json[checker.checker_idx] <= `9` {
162 checker.increment('') or { return }
163
164 for checker.json[checker.checker_idx] >= `0` && checker.json[checker.checker_idx] <= `9` {
165 checker.increment('') or { return }
166 }
167 } else {
168 return checker.checker_error('expected digit got ${checker.json[checker.checker_idx].ascii_str()}')
169 }
170
171 // fraction part
172 if checker.json[checker.checker_idx] == `.` {
173 checker.increment('expected digit')!
174
175 if !(checker.json[checker.checker_idx] >= `0` && checker.json[checker.checker_idx] <= `9`) {
176 return checker.checker_error('expected digit got ${checker.json[checker.checker_idx].ascii_str()}')
177 }
178
179 for checker.json[checker.checker_idx] >= `0` && checker.json[checker.checker_idx] <= `9` {
180 checker.increment('') or { return }
181 }
182 }
183
184 // exponent part
185 if checker.json[checker.checker_idx] == `e` || checker.json[checker.checker_idx] == `E` {
186 checker.increment('expected digit')!
187
188 if checker.json[checker.checker_idx] == `-` || checker.json[checker.checker_idx] == `+` {
189 checker.increment('expected digit')!
190 }
191
192 if !(checker.json[checker.checker_idx] >= `0` && checker.json[checker.checker_idx] <= `9`) {
193 return checker.checker_error('expected digit got ${checker.json[checker.checker_idx].ascii_str()}')
194 }
195
196 for checker.json[checker.checker_idx] >= `0` && checker.json[checker.checker_idx] <= `9` {
197 checker.increment('') or { return }
198 }
199 }
200
201 checker.checker_idx--
202}
203
204@[markused]
205fn (mut checker Decoder) check_boolean() ! {
206 // check if the JSON string is a valid boolean
207 match checker.json[checker.checker_idx] {
208 `t` {
209 if checker.json.len - checker.checker_idx <= 3 {
210 return checker.checker_error('EOF error: expecting `true`')
211 }
212
213 is_not_ok := unsafe {
214 vmemcmp(checker.json.str + checker.checker_idx, c'true', 'true'.len)
215 }
216
217 if is_not_ok != 0 {
218 return checker.checker_error('invalid boolean value. Got `${checker.json[checker.checker_idx..
219 checker.checker_idx + 4]}` instead of `true`')
220 }
221 checker.checker_idx += 3
222 }
223 `f` {
224 if checker.json.len - checker.checker_idx <= 4 {
225 return checker.checker_error('EOF error: expecting `false`')
226 }
227
228 is_not_ok := unsafe {
229 vmemcmp(checker.json.str + checker.checker_idx, c'false', 'false'.len)
230 }
231
232 if is_not_ok != 0 {
233 return checker.checker_error('invalid boolean value. Got `${checker.json[checker.checker_idx..
234 checker.checker_idx + 5]}` instead of `false`')
235 }
236
237 checker.checker_idx += 4
238 }
239 else {
240 return checker.checker_error('invalid boolean')
241 }
242 }
243}
244
245@[markused]
246fn (mut checker Decoder) check_null() ! {
247 // check if the JSON string is a null value
248 if checker.json.len - checker.checker_idx <= 3 {
249 return checker.checker_error('EOF error: expecting `null`')
250 }
251
252 is_not_ok := unsafe {
253 vmemcmp(checker.json.str + checker.checker_idx, c'null', 'null'.len)
254 }
255
256 if is_not_ok != 0 {
257 return checker.checker_error('invalid null value. Got `${checker.json[checker.checker_idx..
258 checker.checker_idx + 4]}` instead of `null`')
259 }
260 checker.checker_idx += 3
261}
262
263@[markused]
264fn (mut checker Decoder) check_array() ! {
265 checker.increment('expected array end')!
266
267 checker.skip_whitespace('expected array end')!
268
269 for checker.json[checker.checker_idx] != `]` {
270 checker.check_json_format()!
271
272 checker.skip_whitespace('expected array end')!
273
274 if checker.json[checker.checker_idx] == `,` {
275 checker.increment('expected array value')!
276 checker.skip_whitespace('') or {}
277
278 if checker.json[checker.checker_idx] == `]` {
279 return checker.checker_error('Cannot use `,`, before `]`')
280 }
281 }
282 }
283}
284
285@[markused]
286fn (mut checker Decoder) check_object() ! {
287 checker.increment('expected object end')!
288
289 checker.skip_whitespace('expected object end')!
290
291 for checker.json[checker.checker_idx] != `}` {
292 if checker.json[checker.checker_idx] != `"` {
293 checker.checker_error('Expecting object key')!
294 }
295
296 checker.check_json_format()!
297
298 checker.skip_whitespace('expected `:`')!
299
300 if checker.json[checker.checker_idx] != `:` {
301 checker.checker_error('expected `:`, got `${checker.json[checker.checker_idx].ascii_str()}`')!
302 }
303
304 checker.increment('expected object value')!
305
306 checker.skip_whitespace('expected object value')!
307
308 checker.check_json_format()!
309
310 checker.skip_whitespace('expected object end')!
311
312 if checker.json[checker.checker_idx] == `,` {
313 checker.increment('expected object key')!
314 checker.skip_whitespace('') or {}
315
316 if checker.json[checker.checker_idx] == `}` {
317 return checker.checker_error('Cannot use `,`, before `}`')
318 }
319 }
320 }
321}
322