diff options
Diffstat (limited to 'v_windows/v/vlib/x/json2/decoder.v')
-rw-r--r-- | v_windows/v/vlib/x/json2/decoder.v | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/v_windows/v/vlib/x/json2/decoder.v b/v_windows/v/vlib/x/json2/decoder.v new file mode 100644 index 0000000..a45a091 --- /dev/null +++ b/v_windows/v/vlib/x/json2/decoder.v @@ -0,0 +1,200 @@ +// 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 | + u64 + +// `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 +} + +struct InvalidTokenError { + msg string + code int +} + +struct UnknownTokenError { + msg string + code int +} + +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 IError(&InvalidTokenError{ + msg: p.emit_error('invalid token `$p.tok.kind`') + }) + } + return fi +} + +fn (mut p Parser) decode_value() ?Any { + if p.n_level + 1 == 500 { + return error(p.emit_error('reached maximum nesting level of 500')) + } + 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 { + if kind == .float { + return Any(tl.f64()) + } + return Any(tl.i64()) + } + return Any(tl) + } + .bool_ { + lit := p.tok.lit.bytestr() + p.next_with_err() ? + if p.convert_type { + return Any(lit.bool()) + } + return Any(lit) + } + .null { + p.next_with_err() ? + if p.convert_type { + return Any(null) + } + return Any('null') + } + .str_ { + str := p.tok.lit.bytestr() + p.next_with_err() ? + return Any(str) + } + else { + return IError(&InvalidTokenError{ + msg: p.emit_error('invalid token `$p.tok.kind`') + }) + } + } + return Any(null) +} + +fn (mut p Parser) decode_array() ?Any { + mut items := []Any{} + p.next_with_err() ? + p.n_level++ + 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 IError(&InvalidTokenError{ + msg: p.emit_error('invalid token `$p.tok.lit') + }) + } + } else if p.tok.kind == .rsbr { + break + } else { + return IError(&UnknownTokenError{ + msg: p.emit_error("unknown token '$p.tok.lit' when decoding array.") + }) + } + } + p.next_with_err() ? + p.n_level-- + return Any(items) +} + +fn (mut p Parser) decode_object() ?Any { + mut fields := map[string]Any{} + p.next_with_err() ? + p.n_level++ + for p.tok.kind != .rcbr { + is_key := p.tok.kind == .str_ && p.n_tok.kind == .colon + if !is_key { + return IError(&InvalidTokenError{ + msg: 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 IError(&UnknownTokenError{ + msg: p.emit_error("unknown token '$p.tok.lit' when decoding object.") + }) + } + } + } + p.next_with_err() ? + p.n_level-- + return Any(fields) +} |