1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00
v/vlib/x/json2/decoder.v

274 lines
5.4 KiB
V
Raw Normal View History

2022-01-04 12:21:08 +03:00
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
2020-09-10 13:05:40 +03:00
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module json2
// `Any` is a sum type that lists the possible types to be decoded and used.
pub type Any = Null | []Any | bool | f32 | f64 | i64 | int | map[string]Any | string | u64
2020-09-10 13:05:40 +03:00
// `Null` struct is a simple representation of the `null` value in JSON.
pub struct Null {
is_null bool = true
}
2020-09-10 13:05:40 +03:00
pub enum ValueKind {
unknown
array
object
string_
number
}
// str returns the string representation of the specific ValueKind
pub fn (k ValueKind) str() string {
return match k {
.unknown { 'unknown' }
.array { 'array' }
.object { 'object' }
.string_ { 'string' }
.number { 'number' }
}
}
fn format_message(msg string, line int, column int) string {
return '[x.json2] $msg ($line:$column)'
}
pub struct DecodeError {
line int
column int
message string
}
// code returns the error code of DecodeError
pub fn (err DecodeError) code() int {
return 3
}
// msg returns the message of the DecodeError
pub fn (err DecodeError) msg() string {
return format_message(err.message, err.line, err.column)
}
pub struct InvalidTokenError {
DecodeError
token Token
expected TokenKind
}
// code returns the error code of the InvalidTokenError
pub fn (err InvalidTokenError) code() int {
return 2
}
// msg returns the message of the InvalidTokenError
pub fn (err InvalidTokenError) msg() string {
footer_text := if err.expected != .none_ { ', expecting `$err.expected`' } else { '' }
return format_message('invalid token `$err.token.kind`$footer_text', err.token.line,
err.token.full_col())
}
pub struct UnknownTokenError {
DecodeError
token Token
kind ValueKind = .unknown
}
// code returns the error code of the UnknownTokenError
pub fn (err UnknownTokenError) code() int {
return 1
}
// msg returns the error message of the UnknownTokenError
pub fn (err UnknownTokenError) msg() string {
return format_message("unknown token '$err.token.lit' when decoding ${err.kind}.",
err.token.line, err.token.full_col())
}
2020-09-10 13:05:40 +03:00
struct Parser {
mut:
scanner &Scanner = unsafe { nil }
p_tok Token
tok Token
n_tok Token
n_level int
convert_type bool = true
2020-09-10 13:05:40 +03:00
}
fn (mut p Parser) next() {
p.p_tok = p.tok
p.tok = p.n_tok
p.n_tok = p.scanner.scan()
2020-09-10 13:05:40 +03:00
}
fn (mut p Parser) next_with_err() ! {
p.next()
if p.tok.kind == .error {
2022-10-28 19:08:30 +03:00
return DecodeError{
line: p.tok.line
column: p.tok.full_col()
message: p.tok.lit.bytestr()
2022-10-28 19:08:30 +03:00
}
2020-09-10 13:05:40 +03:00
}
}
// TODO: copied from v.util to avoid the entire module and its functions
// from being imported. remove later once -skip-unused is enabled by default.
fn skip_bom(file_content string) string {
mut raw_text := file_content
// BOM check
if raw_text.len >= 3 {
2020-09-10 13:05:40 +03:00
unsafe {
c_text := raw_text.str
2020-09-10 13:05:40 +03:00
if c_text[0] == 0xEF && c_text[1] == 0xBB && c_text[2] == 0xBF {
// skip three BOM bytes
offset_from_begin := 3
raw_text = tos(c_text[offset_from_begin], vstrlen(c_text) - offset_from_begin)
2020-09-10 13:05:40 +03:00
}
}
}
return raw_text
2020-09-10 13:05:40 +03:00
}
fn new_parser(srce string, convert_type bool) Parser {
src := skip_bom(srce)
return Parser{
scanner: &Scanner{
text: src.bytes()
}
convert_type: convert_type
2020-09-10 13:05:40 +03:00
}
}
2020-09-10 13:05:40 +03:00
fn (mut p Parser) decode() !Any {
p.next()
p.next_with_err()!
fi := p.decode_value()!
if p.tok.kind != .eof {
2022-10-28 19:08:30 +03:00
return InvalidTokenError{
token: p.tok
2022-10-28 19:08:30 +03:00
}
}
return fi
2020-09-10 13:05:40 +03:00
}
fn (mut p Parser) decode_value() !Any {
2021-06-28 14:05:27 +03:00
if p.n_level + 1 == 500 {
2022-10-28 19:08:30 +03:00
return DecodeError{
message: 'reached maximum nesting level of 500'
2022-10-28 19:08:30 +03:00
}
}
2020-09-10 13:05:40 +03:00
match p.tok.kind {
.lsbr {
return p.decode_array()
2020-09-10 13:05:40 +03:00
}
.lcbr {
return p.decode_object()
2020-09-10 13:05:40 +03:00
}
.int_, .float {
tl := p.tok.lit.bytestr()
kind := p.tok.kind
p.next_with_err()!
if p.convert_type {
$if !nofloat ? {
if kind == .float {
return Any(tl.f64())
}
}
return Any(tl.i64())
}
return Any(tl)
2020-09-10 13:05:40 +03:00
}
.bool_ {
lit := p.tok.lit.bytestr()
p.next_with_err()!
if p.convert_type {
return Any(lit.bool())
}
return Any(lit)
2020-09-10 13:05:40 +03:00
}
.null {
p.next_with_err()!
if p.convert_type {
return Any(null)
}
return Any('null')
2020-09-10 13:05:40 +03:00
}
.str_ {
str := p.tok.lit.bytestr()
p.next_with_err()!
return Any(str)
2020-09-10 13:05:40 +03:00
}
else {
2022-10-28 19:08:30 +03:00
return InvalidTokenError{
token: p.tok
2022-10-28 19:08:30 +03:00
}
2020-09-10 13:05:40 +03:00
}
}
return Any(null)
2020-09-10 13:05:40 +03:00
}
[manualfree]
fn (mut p Parser) decode_array() !Any {
2020-09-10 13:05:40 +03:00
mut items := []Any{}
p.next_with_err()!
2021-06-28 14:05:27 +03:00
p.n_level++
2020-09-10 13:05:40 +03:00
for p.tok.kind != .rsbr {
item := p.decode_value()!
2020-09-10 13:05:40 +03:00
items << item
if p.tok.kind == .comma {
p.next_with_err()!
if p.tok.kind == .rsbr {
2022-10-28 19:08:30 +03:00
return InvalidTokenError{
token: p.tok
2022-10-28 19:08:30 +03:00
}
}
} else if p.tok.kind != .rsbr {
2022-10-28 19:08:30 +03:00
return UnknownTokenError{
token: p.tok
kind: .array
2022-10-28 19:08:30 +03:00
}
2020-09-10 13:05:40 +03:00
}
}
p.next_with_err()!
2021-06-28 14:05:27 +03:00
p.n_level--
2020-09-10 13:05:40 +03:00
return Any(items)
}
fn (mut p Parser) decode_object() !Any {
mut fields := map[string]Any{}
p.next_with_err()!
2021-06-28 14:05:27 +03:00
p.n_level++
2020-09-10 13:05:40 +03:00
for p.tok.kind != .rcbr {
if p.tok.kind != .str_ {
2022-10-28 19:08:30 +03:00
return InvalidTokenError{
token: p.tok
expected: .str_
2022-10-28 19:08:30 +03:00
}
2020-09-10 13:05:40 +03:00
}
cur_key := p.tok.lit.bytestr()
p.next_with_err()!
if p.tok.kind != .colon {
2022-10-28 19:08:30 +03:00
return InvalidTokenError{
token: p.tok
expected: .colon
2022-10-28 19:08:30 +03:00
}
}
p.next_with_err()!
fields[cur_key] = p.decode_value()!
if p.tok.kind != .comma && p.tok.kind != .rcbr {
2022-10-28 19:08:30 +03:00
return UnknownTokenError{
token: p.tok
kind: .object
2022-10-28 19:08:30 +03:00
}
} else if p.tok.kind == .comma {
p.next_with_err()!
2020-09-10 13:05:40 +03:00
}
}
p.next_with_err()!
2021-06-28 14:05:27 +03:00
p.n_level--
2020-09-10 13:05:40 +03:00
return Any(fields)
}