Skip to content

Commit

Permalink
json: support null sum types in decode()
Browse files Browse the repository at this point in the history
  • Loading branch information
medvednikov committed Sep 26, 2024
1 parent b1c9941 commit 9c0a7e7
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 5 deletions.
21 changes: 20 additions & 1 deletion vlib/v/gen/c/json.v
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,26 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st

// DECODING (inline)
$if !json_no_inline_sumtypes ? {
// Handle "key": null
// In this case the first variant must be used (something like InvalidExpr for example)
// An empty instance of the first variant is generated.
// This way the user can easily check if the sum type was not provided in json ("key":null):
// `if node.expr is InvalidExpr { ... }`
// (Do this only for structs)
type_tmp := g.new_tmp_var()
dec.writeln('\tif (cJSON_IsObject(root) || (cJSON_IsArray(root) && cJSON_IsObject(root->child))) {')
first_variant := info.variants[0]
variant_typ := g.typ(first_variant)
fv_sym := g.table.sym(first_variant)
first_variant_name := fv_sym.cname
// println('KIND=${fv_sym.kind}')
if fv_sym.kind == .struct_ && !is_option && field_op != '->' {
dec.writeln('/*sum type ${fv_sym.name} ret_styp=${ret_styp}*/\tif (root->type == cJSON_NULL) { ')
dec.writeln('\t\tstruct ${first_variant_name} empty = {0};')
dec.writeln('res = ${variant_typ}_to_sumtype_${ret_styp}(&empty); } \n else ')
// dec.writeln('res = ${variant_typ}_to_sumtype_${sym.cname}(&empty); } \n else ')
}
//
dec.writeln('if (cJSON_IsObject(root) || (cJSON_IsArray(root) && cJSON_IsObject(root->child))) {')
dec.writeln('\t\tcJSON* ${type_tmp} = cJSON_IsObject(root) ? js_get(root, "_type") : js_get(root->child, "_type");')
dec.writeln('\t\tif (${type_tmp} != 0) {')
dec.writeln('\t\t\tchar* ${type_var} = cJSON_GetStringValue(${type_tmp});')
Expand Down Expand Up @@ -924,6 +942,7 @@ fn gen_js_get_opt(dec_name string, field_type string, styp string, tmp string, n
value_field_type := field_type.replace('*', '_ptr')
dec.writeln('\t${result_name}_${value_field_type.replace('*', '_ptr')} ${tmp} = {0};')
dec.writeln('\tif (jsonroot_${tmp}) {')
// dec.writeln('\t\tif (jsonroot_${tmp}->type == cJSON_NULL) { puts("${name} IS JSON_NULL"); }')
dec.writeln('\t\t${tmp} = ${dec_name}(jsonroot_${tmp});')
dec.writeln('\t\tif (${tmp}.is_error) {')
dec.writeln('\t\t\treturn (${result_name}_${styp}){ /*A*/ .is_error = true, .err = ${tmp}.err, .data = {0} };')
Expand Down
4 changes: 3 additions & 1 deletion vlib/v/parser/assign.v
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr) ast.Stmt {
ast.Ident {
if op == .decl_assign {
if p.scope.known_var(lx.name) {
return p.error_with_pos('redefinition of `${lx.name}`', lx.pos)
if !(p.pref.translated_go && lx.name in ['err', 'ok']) {
return p.error_with_pos('redefinition of `${lx.name}`', lx.pos)
}
}
mut share := unsafe { ast.ShareType(0) }
if mut lx.info is ast.IdentVar {
Expand Down
5 changes: 4 additions & 1 deletion vlib/v/parser/parser.v
Original file line number Diff line number Diff line change
Expand Up @@ -2900,7 +2900,10 @@ fn (mut p Parser) name_expr() ast.Expr {
}
} else if p.peek_tok.kind == .lcbr
&& ((p.inside_if && lit0_is_capital && p.tok.lit.len > 1 && !known_var && language == .v)
|| (p.inside_match_case && p.tok.kind == .name && p.peek_tok.is_next_to(p.tok))) {
|| (p.inside_match_case && lit0_is_capital && p.tok.kind == .name
&& p.peek_tok.is_next_to(p.tok))) {
// XTODO check iscap
//|| (p.inside_match_case && p.tok.kind == .name && p.peek_tok.is_next_to(p.tok))) {
// `if a == Foo{} {...}` or `match foo { Foo{} {...} }`
return p.struct_init(p.mod + '.' + p.tok.lit, .normal, is_option)
} else if p.peek_tok.kind == .dot && lit0_is_capital && !known_var && language == .v {
Expand Down
9 changes: 7 additions & 2 deletions vlib/v/pref/pref.v
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@ pub mut:
profile_no_inline bool // when true, @[inline] functions would not be profiled
profile_fns []string // when set, profiling will be off by default, but inside these functions (and what they call) it will be on.
translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc
obfuscate bool // `v -obf program.v`, renames functions to "f_XXX"
hide_auto_str bool // `v -hide-auto-str program.v`, doesn't generate str() with struct data
translated_go bool = true // Are we running V code translated from Go? Allow err shadowing
obfuscate bool // `v -obf program.v`, renames functions to "f_XXX"
hide_auto_str bool // `v -hide-auto-str program.v`, doesn't generate str() with struct data
// Note: passing -cg instead of -g will set is_vlines to false and is_debug to true, thus making v generate cleaner C files,
// which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks).
sanitize bool // use Clang's new "-fsanitize" option
Expand Down Expand Up @@ -659,6 +660,10 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
res.translated = true
res.gc_mode = .no_gc // no gc in c2v'ed code, at least for now
}
'-translated-go' {
println('got -translated-go')
res.translated_go = true
}
'-m32', '-m64' {
res.m64 = arg[2] == `6`
res.cflags += ' ${arg}'
Expand Down

0 comments on commit 9c0a7e7

Please sign in to comment.