// 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) }