diff --git a/vlib/json/json_primitives.v b/vlib/json/json_primitives.v index 517d2583c0..a0a7af47e7 100644 --- a/vlib/json/json_primitives.v +++ b/vlib/json/json_primitives.v @@ -6,6 +6,7 @@ module json #flag -I @VROOT/thirdparty/cJSON #flag @VROOT/thirdparty/cJSON/cJSON.o #include "cJSON.h" +#define js_get(object, key) cJSON_GetObjectItemCaseSensitive((object), (key)) struct C.cJSON { valueint int valuedouble f32 @@ -22,84 +23,84 @@ pub fn encode(x voidptr) string { return '' } -fn jsdecode_int(root &C.cJSON) int { +fn decode_int(root &C.cJSON) int { if isnil(root) { return 0 } return root.valueint } -fn jsdecode_i8(root &C.cJSON) i8 { +fn decode_i8(root &C.cJSON) i8 { if isnil(root) { return i8(0) } return i8(root.valueint) } -fn jsdecode_i16(root &C.cJSON) i16 { +fn decode_i16(root &C.cJSON) i16 { if isnil(root) { return i16(0) } return i16(root.valueint) } -fn jsdecode_i64(root &C.cJSON) i64 { +fn decode_i64(root &C.cJSON) i64 { if isnil(root) { return i64(0) } return i64(root.valuedouble) // i64 is double in C } -fn jsdecode_byte(root &C.cJSON) byte { +fn decode_byte(root &C.cJSON) byte { if isnil(root) { return byte(0) } return byte(root.valueint) } -fn jsdecode_u16(root &C.cJSON) u16 { +fn decode_u16(root &C.cJSON) u16 { if isnil(root) { return u16(0) } return u16(root.valueint) } -fn jsdecode_u32(root &C.cJSON) u32 { +fn decode_u32(root &C.cJSON) u32 { if isnil(root) { return u32(0) } return u32(root.valueint) } -fn jsdecode_u64(root &C.cJSON) u64 { +fn decode_u64(root &C.cJSON) u64 { if isnil(root) { return u64(0) } return u64(root.valueint) } -fn jsdecode_f32(root &C.cJSON) f32 { +fn decode_f32(root &C.cJSON) f32 { if isnil(root) { return f32(0) } return root.valuedouble } -fn jsdecode_f64(root &C.cJSON) f64 { +fn decode_f64(root &C.cJSON) f64 { if isnil(root) { return f64(0) } return f64(root.valuedouble) } -fn jsdecode_string(root &C.cJSON) string { +fn decode_string(root &C.cJSON) string { if isnil(root) { return '' } if isnil(root.valuestring) { return '' } - // println('jsdecode string valuestring="$root.valuestring"') + // println('decode string valuestring="$root.valuestring"') // return tos(root.valuestring, _strlen(root.valuestring)) return tos_clone(root.valuestring) // , _strlen(root.valuestring)) } @@ -122,7 +123,7 @@ fn C.cJSON_Parse() &C.cJSON fn C.cJSON_PrintUnformatted() byteptr -fn jsdecode_bool(root &C.cJSON) bool { +fn decode_bool(root &C.cJSON) bool { if isnil(root) { return false } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index ea810d3c9c..39b81ae5af 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -721,13 +721,14 @@ pub fn (mut c Checker) call_fn(call_expr mut ast.CallExpr) table.Type { // println(fn_name) // } if fn_name == 'json.encode' { - } - if fn_name == 'json.decode' { - ident := call_expr.args[0].expr as ast.Ident - // sym := c.table.find_type(ident.name) - idx := c.table.find_type_idx(ident.name) - println('js.decode t=$ident.name') - return table.Type(idx) + } else if fn_name == 'json.decode' { + expr := call_expr.args[0].expr + if !(expr is ast.Type) { + c.error('json.decode: first argument needs to be a type', call_expr.pos) + return table.void_type + } + typ := expr as ast.Type + return typ.typ.set_flag(.optional) } // look for function in format `mod.fn` or `fn` (main/builtin) mut f := table.Fn{} diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index 71f498cfb4..e6abdc3122 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -383,12 +383,22 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { mut name := node.name is_print := name == 'println' || name == 'print' print_method := if name == 'println' { 'println' } else { 'print' } - g.is_json_fn = name == 'json.encode' + is_json_encode := name == 'json.encode' + is_json_decode := name == 'json.decode' + g.is_json_fn = is_json_encode || is_json_decode mut json_type_str := '' if g.is_json_fn { - g.write('json__json_print(') - g.gen_json_for_type(node.args[0].typ) - json_type_str = g.table.get_type_symbol(node.args[0].typ).name + if name == 'json.encode' { + g.write('json__json_print(') + g.gen_json_for_type(node.args[0].typ) + json_type_str = g.table.get_type_symbol(node.args[0].typ).name + } else { + g.insert_before('// json.decode') + ast_type := node.args[0].expr as ast.Type + // `json.decode(User, s)` => json.decode_User(s) + sym := g.table.get_type_symbol(ast_type.typ) + name += '_' + sym.name + } } if node.is_c { // Skip "C." @@ -397,8 +407,8 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { } else { name = c_name(name) } - if g.is_json_fn { - // `json__decode` => `json__decode_User` + if is_json_encode { + // `json__encode` => `json__encode_User` name += '_' + json_type_str } // Generate tmp vars for values that have to be freed. @@ -476,14 +486,25 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { pafn := g.fn_decl.name.after('.') mut pamod := g.fn_decl.name.all_before_last('.') if pamod == pafn { - pamod = if g.fn_decl.is_builtin { 'builtin' } else { 'main' } + pamod = if g.fn_decl.is_builtin { + 'builtin' + } else { + 'main' + } } g.write('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ') g.call_args(node.args, node.expected_arg_types) g.write(')') } else { g.write('${name}(') - g.call_args(node.args, node.expected_arg_types) + if is_json_decode { + g.write('json__json_parse(') + // Skip the first argument in json.decode which is a type + // its name was already used to generate the function call + g.call_args(node.args[1..], node.expected_arg_types) + } else { + g.call_args(node.args, node.expected_arg_types) + } g.write(')') } // if node.or_block.stmts.len > 0 { diff --git a/vlib/v/gen/json.v b/vlib/v/gen/json.v index 6e19efcafc..f0d5feff23 100644 --- a/vlib/v/gen/json.v +++ b/vlib/v/gen/json.v @@ -31,8 +31,9 @@ fn (mut g Gen) gen_json_for_type(typ table.Type) { // Code gen decoder dec_fn_name := js_dec_name(sym.name) dec.writeln(' -Option ${dec_fn_name}(cJSON* root, $styp* res) { -// $styp res; +//Option ${dec_fn_name}(cJSON* root, $styp* res) { +Option ${dec_fn_name}(cJSON* root) { + $styp res; if (!root) { const char *error_ptr = cJSON_GetErrorPtr(); if (error_ptr != NULL) { @@ -70,7 +71,7 @@ cJSON* ${enc_fn_name}($styp val) { g.gen_json_for_type(field.typ) dec_name := js_dec_name(field_type) if is_js_prim(field_type) { - dec.writeln(' res->$field.name = $dec_name (js_get(' + 'root, "$name"))') + dec.writeln(' res . $field.name = $dec_name (js_get(' + 'root, "$name"))') } else { dec.writeln(' $dec_name (js_get(root, "$name"), & (res->$field.name))') } @@ -80,7 +81,7 @@ cJSON* ${enc_fn_name}($styp val) { } // cJSON_delete // p.cgen.fns << '$dec return opt_ok(res); \n}' - dec.writeln('return opt_ok(res, sizeof(*res)); \n}') + dec.writeln('return opt_ok(&res, sizeof(res)); \n}') enc.writeln('\treturn o;\n}') g.definitions.writeln(dec.str()) g.gowrappers.writeln(enc.str()) diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index e2fa7e7fbd..6c650847c4 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -20,6 +20,11 @@ pub fn (mut p Parser) call_expr(is_c, is_js bool, mod string) ast.CallExpr { } else { name } + if fn_name == 'json.decode' { + // Makes name_expr() parse the type (`User` in `json.decode(User, txt)`)` + p.inside_is = true + p.expr_mod = '' + } p.check(.lpar) args := p.call_args() last_pos := p.tok.position() diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index cccfc1b905..2692114c96 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -32,7 +32,7 @@ mut: mod string // current module name attr string attr_ctdefine string - expr_mod string + expr_mod string // for constructing full type names in parse_type() scope &ast.Scope global_scope &ast.Scope imports map[string]string @@ -104,7 +104,7 @@ pub fn parse_file(path string, b_table &table.Table, comments_mode scanner.Comme for { if p.tok.kind == .eof { if p.pref.is_script && !p.pref.is_test && p.mod == 'main' && !have_fn_main(stmts) { - stmts << ast.FnDecl { + stmts << ast.FnDecl{ name: 'main' file: p.file_name return_type: table.void_type @@ -355,7 +355,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt { } else { if p.pref.is_script && !p.pref.is_test { - p.scanner.add_fn_main_and_rescan(p.tok.pos-1) + p.scanner.add_fn_main_and_rescan(p.tok.pos - 1) p.read_first_token() return p.top_stmt() } else { @@ -588,8 +588,9 @@ pub fn (mut p Parser) name_expr() ast.Expr { p.inside_is = false // get type position before moving to next type_pos := p.tok.position() + typ := p.parse_type() return ast.Type{ - typ: p.parse_type() + typ: typ pos: type_pos } } @@ -1062,8 +1063,8 @@ fn (mut p Parser) return_stmt() ast.Return { // left hand side of `=` or `:=` in `a,b,c := 1,2,3` fn (mut p Parser) global_decl() ast.GlobalDecl { - if !p.pref.translated && !p.pref.is_livemain && !p.builtin_mod && !p.pref.building_v && p.mod != - 'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !os.getwd().contains('/volt') && !p.pref.enable_globals { + if !p.pref.translated && !p.pref.is_livemain && !p.builtin_mod && !p.pref.building_v && + p.mod != 'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !os.getwd().contains('/volt') && !p.pref.enable_globals { p.error('use `v --enable-globals ...` to enable globals') } p.next()