v/vlib/x/json2/decoder2/check.v
2025-08-05 10:07:03 +03:00

444 lines
12 KiB
V

module decoder2
// check_json_format checks if the JSON string is valid and updates the decoder state.
fn (mut checker Decoder) check_json_format() ! {
// skip whitespace
for checker.json[checker.checker_idx] in whitespace_chars {
if checker.checker_idx == checker.json.len {
break
}
checker.checker_idx++
}
start_idx_position := checker.checker_idx
mut actual_value_info_pointer := unsafe { &ValueInfo(nil) }
match checker.json[checker.checker_idx] {
`"` {
checker.values_info.push(ValueInfo{
position: checker.checker_idx
value_kind: .string_
})
actual_value_info_pointer = checker.values_info.last()
checker.check_string()!
}
`-`, `0`...`9` {
checker.values_info.push(ValueInfo{
position: checker.checker_idx
value_kind: .number
})
actual_value_info_pointer = checker.values_info.last()
checker.check_number()!
}
`t`, `f` {
checker.values_info.push(ValueInfo{
position: checker.checker_idx
value_kind: .boolean
})
actual_value_info_pointer = checker.values_info.last()
checker.check_boolean()!
}
`n` {
checker.values_info.push(ValueInfo{
position: checker.checker_idx
value_kind: .null
})
actual_value_info_pointer = checker.values_info.last()
checker.check_null()!
}
`[` {
checker.values_info.push(ValueInfo{
position: checker.checker_idx
value_kind: .array
})
actual_value_info_pointer = checker.values_info.last()
checker.check_array()!
}
`{` {
checker.values_info.push(ValueInfo{
position: checker.checker_idx
value_kind: .object
})
actual_value_info_pointer = checker.values_info.last()
checker.check_object()!
}
else {
checker.checker_error('unknown value kind')!
}
}
actual_value_info_pointer.length = checker.checker_idx + 1 - start_idx_position
if checker.checker_idx < checker.json.len {
checker.checker_idx++
}
for checker.checker_idx < checker.json.len
&& checker.json[checker.checker_idx] !in [`,`, `:`, `}`, `]`] {
// get trash characters after the value
if checker.json[checker.checker_idx] !in whitespace_chars {
checker.checker_error('invalid value. Unexpected character after ${actual_value_info_pointer.value_kind} end')!
} else {
// whitespace
}
checker.checker_idx++
}
}
fn (mut checker Decoder) check_string() ! {
// check if the JSON string is a valid string
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('EOF error: string not closed')
}
checker.checker_idx++
// check if the JSON string is a valid escape sequence
for checker.json[checker.checker_idx] != `"` {
if checker.json[checker.checker_idx] == `\\` {
if checker.checker_idx + 1 >= checker.json.len - 1 {
return checker.checker_error('invalid escape sequence')
}
escaped_char := checker.json[checker.checker_idx + 1]
match escaped_char {
`/`, `b`, `f`, `n`, `r`, `t`, `"`, `\\` {
checker.checker_idx++ // make sure escaped quotation marks are skipped
}
`u` {
// check if the JSON string is a valid unicode escape sequence
escaped_char_last_index := checker.checker_idx + 5
if escaped_char_last_index < checker.json.len {
// 2 bytes for the unicode escape sequence `\u`
checker.checker_idx += 2
for checker.checker_idx < escaped_char_last_index {
match checker.json[checker.checker_idx] {
`0`...`9`, `a`...`f`, `A`...`F` {
checker.checker_idx++
}
else {
return checker.checker_error('invalid unicode escape sequence')
}
}
}
continue
} else {
return checker.checker_error('short unicode escape sequence ${checker.json[checker.checker_idx..escaped_char_last_index]}')
}
}
else {
return checker.checker_error('unknown escape sequence')
}
}
}
checker.checker_idx++
}
}
fn (mut checker Decoder) check_number() ! {
// check if the JSON string is a valid float or integer
if checker.json[checker.checker_idx] == `-` {
checker.checker_idx++
}
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('expected digit got EOF')
}
// integer part
if checker.json[checker.checker_idx] == `0` {
checker.checker_idx++
} else if checker.json[checker.checker_idx] >= `1` && checker.json[checker.checker_idx] <= `9` {
checker.checker_idx++
for checker.checker_idx < checker.json.len && checker.json[checker.checker_idx] >= `0`
&& checker.json[checker.checker_idx] <= `9` {
checker.checker_idx++
}
} else {
return checker.checker_error('expected digit got ${checker.json[checker.checker_idx].ascii_str()}')
}
// fraction part
if checker.checker_idx != checker.json.len && checker.json[checker.checker_idx] == `.` {
checker.checker_idx++
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('expected digit got EOF')
}
if checker.json[checker.checker_idx] >= `0` && checker.json[checker.checker_idx] <= `9` {
for checker.checker_idx < checker.json.len && checker.json[checker.checker_idx] >= `0`
&& checker.json[checker.checker_idx] <= `9` {
checker.checker_idx++
}
} else {
return checker.checker_error('expected digit got ${checker.json[checker.checker_idx].ascii_str()}')
}
}
// exponent part
if checker.checker_idx != checker.json.len
&& (checker.json[checker.checker_idx] == `e` || checker.json[checker.checker_idx] == `E`) {
checker.checker_idx++
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('expected digit got EOF')
}
if checker.json[checker.checker_idx] == `-` || checker.json[checker.checker_idx] == `+` {
checker.checker_idx++
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('expected digit got EOF')
}
}
if checker.json[checker.checker_idx] >= `0` && checker.json[checker.checker_idx] <= `9` {
for checker.checker_idx < checker.json.len && checker.json[checker.checker_idx] >= `0`
&& checker.json[checker.checker_idx] <= `9` {
checker.checker_idx++
}
} else {
return checker.checker_error('expected digit got ${checker.json[checker.checker_idx].ascii_str()}')
}
}
checker.checker_idx--
}
fn (mut checker Decoder) check_boolean() ! {
// check if the JSON string is a valid boolean
match checker.json[checker.checker_idx] {
`t` {
if checker.json.len - checker.checker_idx <= 3 {
return checker.checker_error('EOF error: expecting `true`')
}
is_not_ok := unsafe {
vmemcmp(checker.json.str + checker.checker_idx, true_in_string.str, true_in_string.len)
}
if is_not_ok != 0 {
return checker.checker_error('invalid boolean value. Got `${checker.json[checker.checker_idx..
checker.checker_idx + 4]}` instead of `true`')
}
checker.checker_idx += 3
}
`f` {
if checker.json.len - checker.checker_idx <= 4 {
return checker.checker_error('EOF error: expecting `false`')
}
is_not_ok := unsafe {
vmemcmp(checker.json.str + checker.checker_idx, false_in_string.str, false_in_string.len)
}
if is_not_ok != 0 {
return checker.checker_error('invalid boolean value. Got `${checker.json[checker.checker_idx..
checker.checker_idx + 5]}` instead of `false`')
}
checker.checker_idx += 4
}
else {
return checker.checker_error('invalid boolean')
}
}
}
fn (mut checker Decoder) check_null() ! {
// check if the JSON string is a null value
if checker.json.len - checker.checker_idx <= 3 {
return checker.checker_error('EOF error: expecting `null`')
}
is_not_ok := unsafe {
vmemcmp(checker.json.str + checker.checker_idx, null_in_string.str, null_in_string.len)
}
if is_not_ok != 0 {
return checker.checker_error('invalid null value. Got `${checker.json[checker.checker_idx..
checker.checker_idx + 4]}` instead of `null`')
}
checker.checker_idx += 3
}
fn (mut checker Decoder) check_array() ! {
// check if the JSON string is an empty array
if checker.json.len >= checker.checker_idx + 2 {
checker.checker_idx++
} else {
return checker.checker_error('EOF error: There are not enough length for an array')
}
for checker.json[checker.checker_idx] != `]` {
// skip whitespace
for checker.json[checker.checker_idx] in whitespace_chars {
if checker.checker_idx == checker.json.len {
checker.checker_idx--
break
}
checker.checker_idx++
}
if checker.json[checker.checker_idx] == `]` {
break
}
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('EOF error: array not closed')
}
checker.check_json_format()!
// whitespace
for checker.json[checker.checker_idx] in whitespace_chars {
checker.checker_idx++
}
if checker.json[checker.checker_idx] == `]` {
break
}
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('EOF error: braces are not closed')
}
if checker.json[checker.checker_idx] == `,` {
checker.checker_idx++
for checker.json[checker.checker_idx] in whitespace_chars {
checker.checker_idx++
}
if checker.json[checker.checker_idx] == `]` {
return checker.checker_error('Cannot use `,`, before `]`')
}
continue
} else {
if checker.json[checker.checker_idx] == `]` {
break
} else {
return checker.checker_error('`]` after value')
}
}
}
}
fn (mut checker Decoder) check_object() ! {
if checker.json.len - checker.checker_idx < 2 {
return checker.checker_error('EOF error: expecting a complete object after `{`')
}
checker.checker_idx++
for checker.json[checker.checker_idx] != `}` {
// skip whitespace
for checker.json[checker.checker_idx] in whitespace_chars {
if checker.checker_idx == checker.json.len {
checker.checker_idx--
break
}
checker.checker_idx++
}
if checker.json[checker.checker_idx] == `}` {
continue
}
if checker.json[checker.checker_idx] != `"` {
return checker.checker_error('Expecting object key')
}
// Object key
checker.check_json_format()!
for checker.json[checker.checker_idx] != `:` {
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('EOF error: key colon not found')
}
if checker.json[checker.checker_idx] !in whitespace_chars {
return checker.checker_error('invalid value after object key')
}
checker.checker_idx++
}
if checker.json[checker.checker_idx] != `:` {
return checker.checker_error('Expecting `:` after object key')
}
// skip `:`
checker.checker_idx++
// skip whitespace
for checker.json[checker.checker_idx] in whitespace_chars {
checker.checker_idx++
}
match checker.json[checker.checker_idx] {
`"`, `[`, `{`, `0`...`9`, `-`, `n`, `t`, `f` {
checker.check_json_format()!
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('EOF error: braces are not closed')
}
// whitespace
for checker.json[checker.checker_idx] in whitespace_chars {
checker.checker_idx++
}
if checker.json[checker.checker_idx] == `}` {
break
}
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('EOF error: braces are not closed')
}
if checker.json[checker.checker_idx] == `,` {
checker.checker_idx++
if checker.checker_idx == checker.json.len {
checker.checker_idx--
return checker.checker_error('EOF error: Expecting object key after `,`')
}
for checker.json[checker.checker_idx] in whitespace_chars {
checker.checker_idx++
}
if checker.json[checker.checker_idx] != `"` {
return checker.checker_error('Expecting object key after `,`')
}
} else {
if checker.json[checker.checker_idx] == `}` {
break
} else {
return checker.checker_error('invalid object value')
}
}
}
else {
return checker.checker_error('invalid object value')
}
}
}
}