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

169 lines
3.9 KiB
V

// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
// 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
// `Null` struct is a simple representation of the `null` value in JSON.
pub struct Null {
is_null bool = true
}
struct Parser {
mut:
scanner &Scanner
p_tok Token
tok Token
n_tok Token
n_level int
convert_type bool = true
}
fn (mut p Parser) next() {
p.p_tok = p.tok
p.tok = p.n_tok
p.n_tok = p.scanner.scan()
}
fn (mut p Parser) next_with_err() ? {
p.next()
if p.tok.kind == .error {
return error(p.emit_error(p.tok.lit.bytestr()))
}
}
fn (p Parser) emit_error(msg string) string {
line := p.tok.line
column := p.tok.col + p.tok.lit.len
return '[x.json2] $msg ($line:$column)'
}
// 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 {
unsafe {
c_text := raw_text.str
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)
}
}
}
return raw_text
}
fn new_parser(srce string, convert_type bool) Parser {
src := skip_bom(srce)
return Parser{
scanner: &Scanner{
text: src.bytes()
}
convert_type: convert_type
}
}
fn (mut p Parser) decode() ?Any {
p.next()
p.next_with_err() ?
fi := p.decode_value() ?
if p.tok.kind != .eof {
return error(p.emit_error('invalid token `$p.tok.kind`'))
}
return fi
}
fn (mut p Parser) decode_value() ?Any {
if p.n_level == 500 {
return error(p.emit_error('reached maximum nesting level of 500'))
}
if (p.tok.kind == .lsbr && p.n_tok.kind == .lcbr)
|| (p.p_tok.kind == p.tok.kind && p.tok.kind == .lsbr) {
p.n_level++
}
match p.tok.kind {
.lsbr {
return p.decode_array()
}
.lcbr {
return p.decode_object()
}
.int_, .float {
tl := p.tok.lit.bytestr()
kind := p.tok.kind
p.next_with_err() ?
if p.convert_type {
return if kind == .float { Any(tl.f64()) } else { Any(tl.i64()) }
}
return Any(tl)
}
.bool_ {
lit := p.tok.lit.bytestr()
p.next_with_err() ?
return if p.convert_type { Any(lit.bool()) } else { Any(lit) }
}
.null {
p.next_with_err() ?
return if p.convert_type { Any(null) } else { Any('null') }
}
.str_ {
str := p.tok.lit.bytestr()
p.next_with_err() ?
return Any(str)
}
else {
return error(p.emit_error('invalid token `$p.tok.kind`'))
}
}
return Any{}
}
fn (mut p Parser) decode_array() ?Any {
mut items := []Any{}
p.next_with_err() ?
for p.tok.kind != .rsbr {
item := p.decode_value() ?
items << item
if p.tok.kind == .comma {
p.next_with_err() ?
if p.tok.kind == .rsbr || p.tok.kind == .rcbr {
return error(p.emit_error('invalid token `$p.tok.lit'))
}
} else if p.tok.kind == .rsbr {
break
} else {
return error(p.emit_error("unknown token '$p.tok.lit' when decoding array."))
}
}
p.next_with_err() ?
return Any(items)
}
fn (mut p Parser) decode_object() ?Any {
mut fields := map[string]Any{}
p.next_with_err() ?
for p.tok.kind != .rcbr {
is_key := p.tok.kind == .str_ && p.n_tok.kind == .colon
if !is_key {
return error(p.emit_error('invalid token `$p.tok.kind`, expecting `str_`'))
}
cur_key := p.tok.lit.bytestr()
p.next_with_err() ?
p.next_with_err() ?
fields[cur_key] = p.decode_value() ?
if p.tok.kind == .comma {
p.next_with_err() ?
if p.tok.kind != .str_ {
return error(p.emit_error("unknown token '$p.tok.lit' when decoding object."))
}
}
}
p.next_with_err() ?
return Any(fields)
}