cgen: json sumtype inlining (#11961)
parent
430677a0c0
commit
c75271fcb7
|
@ -10,7 +10,7 @@ module json
|
||||||
|
|
||||||
struct C.cJSON {
|
struct C.cJSON {
|
||||||
valueint int
|
valueint int
|
||||||
valuedouble f32
|
valuedouble f64
|
||||||
valuestring &char
|
valuestring &char
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +78,13 @@ fn decode_byte(root &C.cJSON) byte {
|
||||||
return byte(root.valueint)
|
return byte(root.valueint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decode_u8(root &C.cJSON) u8 {
|
||||||
|
if isnil(root) {
|
||||||
|
return byte(0)
|
||||||
|
}
|
||||||
|
return byte(root.valueint)
|
||||||
|
}
|
||||||
|
|
||||||
fn decode_u16(root &C.cJSON) u16 {
|
fn decode_u16(root &C.cJSON) u16 {
|
||||||
if isnil(root) {
|
if isnil(root) {
|
||||||
return u16(0)
|
return u16(0)
|
||||||
|
@ -103,14 +110,26 @@ fn decode_f32(root &C.cJSON) f32 {
|
||||||
if isnil(root) {
|
if isnil(root) {
|
||||||
return f32(0)
|
return f32(0)
|
||||||
}
|
}
|
||||||
return root.valuedouble
|
return f32(root.valuedouble)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_f64(root &C.cJSON) f64 {
|
fn decode_f64(root &C.cJSON) f64 {
|
||||||
if isnil(root) {
|
if isnil(root) {
|
||||||
return f64(0)
|
return f64(0)
|
||||||
}
|
}
|
||||||
return f64(root.valuedouble)
|
return root.valuedouble
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_rune(root &C.cJSON) rune {
|
||||||
|
if isnil(root) {
|
||||||
|
return rune(0)
|
||||||
|
}
|
||||||
|
if isnil(root.valuestring) {
|
||||||
|
return rune(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Parse as runes, bypassing string casting...?
|
||||||
|
return unsafe { tos_clone(&byte(root.valuestring)).runes().first() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_string(root &C.cJSON) string {
|
fn decode_string(root &C.cJSON) string {
|
||||||
|
@ -153,6 +172,10 @@ fn encode_byte(val byte) &C.cJSON {
|
||||||
return C.cJSON_CreateNumber(val)
|
return C.cJSON_CreateNumber(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn encode_u8(val u8) &C.cJSON {
|
||||||
|
return C.cJSON_CreateNumber(val)
|
||||||
|
}
|
||||||
|
|
||||||
fn encode_u16(val u16) &C.cJSON {
|
fn encode_u16(val u16) &C.cJSON {
|
||||||
return C.cJSON_CreateNumber(val)
|
return C.cJSON_CreateNumber(val)
|
||||||
}
|
}
|
||||||
|
@ -177,6 +200,10 @@ fn encode_bool(val bool) &C.cJSON {
|
||||||
return C.cJSON_CreateBool(val)
|
return C.cJSON_CreateBool(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn encode_rune(val rune) &C.cJSON {
|
||||||
|
return C.cJSON_CreateString(&char(val.str().str))
|
||||||
|
}
|
||||||
|
|
||||||
fn encode_string(val string) &C.cJSON {
|
fn encode_string(val string) &C.cJSON {
|
||||||
return C.cJSON_CreateString(&char(val.str))
|
return C.cJSON_CreateString(&char(val.str))
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ fn test_encode_decode_sumtype() ? {
|
||||||
other: [
|
other: [
|
||||||
Entity(Item{'Pen'}),
|
Entity(Item{'Pen'}),
|
||||||
Item{'Cookie'},
|
Item{'Cookie'},
|
||||||
Animal.dog,
|
Animal.cat,
|
||||||
'Stool',
|
'Stool',
|
||||||
time.now(),
|
time.now(),
|
||||||
]
|
]
|
||||||
|
@ -81,6 +81,7 @@ fn test_encode_decode_sumtype() ? {
|
||||||
|
|
||||||
assert game.title == dec.title
|
assert game.title == dec.title
|
||||||
assert game.player == dec.player
|
assert game.player == dec.player
|
||||||
|
assert (game.other[2] as Animal) == (dec.other[2] as Animal)
|
||||||
assert (game.other[4] as time.Time).unix_time() == (dec.other[4] as time.Time).unix_time()
|
assert (game.other[4] as time.Time).unix_time() == (dec.other[4] as time.Time).unix_time()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,10 +86,13 @@ $enc_fn_dec {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
enc.writeln('\to = cJSON_CreateObject();')
|
enc.writeln('\to = cJSON_CreateObject();')
|
||||||
if psym.info !is ast.Struct {
|
if psym.info is ast.Struct {
|
||||||
|
g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec)
|
||||||
|
} else if psym.kind == .sum_type {
|
||||||
|
verror('json: $sym.name aliased sumtypes does not work at the moment')
|
||||||
|
} else {
|
||||||
verror('json: $sym.name is not struct')
|
verror('json: $sym.name is not struct')
|
||||||
}
|
}
|
||||||
g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec)
|
|
||||||
} else if sym.kind == .sum_type {
|
} else if sym.kind == .sum_type {
|
||||||
enc.writeln('\to = cJSON_CreateObject();')
|
enc.writeln('\to = cJSON_CreateObject();')
|
||||||
// Sumtypes. Range through variants of sumtype
|
// Sumtypes. Range through variants of sumtype
|
||||||
|
@ -118,47 +121,165 @@ $enc_fn_dec {
|
||||||
[inline]
|
[inline]
|
||||||
fn (mut g Gen) gen_sumtype_enc_dec(sym ast.TypeSymbol, mut enc strings.Builder, mut dec strings.Builder) {
|
fn (mut g Gen) gen_sumtype_enc_dec(sym ast.TypeSymbol, mut enc strings.Builder, mut dec strings.Builder) {
|
||||||
info := sym.info as ast.SumType
|
info := sym.info as ast.SumType
|
||||||
|
type_var := g.new_tmp_var()
|
||||||
typ := sym.idx
|
typ := sym.idx
|
||||||
|
|
||||||
|
// DECODING (inline)
|
||||||
|
$if !json_no_inline_sumtypes ? {
|
||||||
|
type_tmp := g.new_tmp_var()
|
||||||
|
dec.writeln('\tif (cJSON_IsObject(root)) {')
|
||||||
|
dec.writeln('\t\tcJSON* $type_tmp = js_get(root, "_type");')
|
||||||
|
dec.writeln('\t\tif ($type_tmp != 0) {')
|
||||||
|
dec.writeln('\t\t\tchar* $type_var = cJSON_GetStringValue($type_tmp);')
|
||||||
|
// dec.writeln('\t\t\tcJSON_DeleteItemFromObjectCaseSensitive(root, "_type");')
|
||||||
|
}
|
||||||
|
|
||||||
|
mut variant_types := []string{}
|
||||||
|
mut variant_symbols := []ast.TypeSymbol{}
|
||||||
|
mut at_least_one_prim := false
|
||||||
for variant in info.variants {
|
for variant in info.variants {
|
||||||
variant_typ := g.typ(variant)
|
variant_typ := g.typ(variant)
|
||||||
|
variant_types << variant_typ
|
||||||
variant_sym := g.table.get_type_symbol(variant)
|
variant_sym := g.table.get_type_symbol(variant)
|
||||||
|
variant_symbols << variant_sym
|
||||||
|
at_least_one_prim = at_least_one_prim || is_js_prim(variant_typ)
|
||||||
|
|| variant_sym.kind == .enum_ || variant_sym.name == 'time.Time'
|
||||||
unmangled_variant_name := variant_sym.name.split('.').last()
|
unmangled_variant_name := variant_sym.name.split('.').last()
|
||||||
|
|
||||||
|
// TODO: Do not generate dec/enc for 'time.Time', because we handle it by saving it as u64
|
||||||
g.gen_json_for_type(variant)
|
g.gen_json_for_type(variant)
|
||||||
|
|
||||||
|
// Helpers for decoding
|
||||||
g.write_sumtype_casting_fn(variant, typ)
|
g.write_sumtype_casting_fn(variant, typ)
|
||||||
g.definitions.writeln('static inline $sym.cname ${variant_typ}_to_sumtype_${sym.cname}($variant_typ* x);')
|
g.definitions.writeln('static inline $sym.cname ${variant_typ}_to_sumtype_${sym.cname}($variant_typ* x);')
|
||||||
|
|
||||||
// ENCODING
|
// ENCODING
|
||||||
enc.writeln('\tif (val._typ == $variant) {')
|
enc.writeln('\tif (val._typ == $variant) {')
|
||||||
|
$if json_no_inline_sumtypes ? {
|
||||||
if variant_sym.kind == .enum_ {
|
if variant_sym.kind == .enum_ {
|
||||||
enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", json__encode_u64(*val._$variant_typ));')
|
enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", ${js_enc_name('u64')}(*val._$variant_typ));')
|
||||||
} else if variant_sym.name == 'time.Time' {
|
} else if variant_sym.name == 'time.Time' {
|
||||||
enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", json__encode_i64(val._$variant_typ->_v_unix));')
|
enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", ${js_enc_name('i64')}(val._$variant_typ->_v_unix));')
|
||||||
} else {
|
} else {
|
||||||
enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", json__encode_${variant_typ}(*val._$variant_typ));')
|
enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", ${js_enc_name(variant_typ)}(*val._$variant_typ));')
|
||||||
|
}
|
||||||
|
} $else {
|
||||||
|
if is_js_prim(variant_typ) {
|
||||||
|
enc.writeln('\t\to = ${js_enc_name(variant_typ)}(*val._$variant_typ);')
|
||||||
|
} else if variant_sym.kind == .enum_ {
|
||||||
|
enc.writeln('\t\to = ${js_enc_name('u64')}(*val._$variant_typ);')
|
||||||
|
} else if variant_sym.name == 'time.Time' {
|
||||||
|
enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("$unmangled_variant_name"));')
|
||||||
|
enc.writeln('\t\tcJSON_AddItemToObject(o, "value", ${js_enc_name('i64')}(val._$variant_typ->_v_unix));')
|
||||||
|
} else {
|
||||||
|
enc.writeln('\t\to = ${js_enc_name(variant_typ)}(*val._$variant_typ);')
|
||||||
|
enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("$unmangled_variant_name"));')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
enc.writeln('\t}')
|
enc.writeln('\t}')
|
||||||
|
|
||||||
// DECODING
|
// DECODING
|
||||||
dec.writeln('\tif (strcmp("$unmangled_variant_name", root->child->string) == 0) {')
|
|
||||||
tmp := g.new_tmp_var()
|
tmp := g.new_tmp_var()
|
||||||
|
$if json_no_inline_sumtypes ? {
|
||||||
|
dec.writeln('\tif (strcmp("$unmangled_variant_name", root->child->string) == 0) {')
|
||||||
if is_js_prim(variant_typ) {
|
if is_js_prim(variant_typ) {
|
||||||
gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, false)
|
gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, true)
|
||||||
dec.writeln('\t\t$variant_typ value = (${js_dec_name(variant_typ)})(jsonroot_$tmp);')
|
dec.writeln('\t\t$variant_typ value = ${js_dec_name(variant_typ)}(jsonroot_$tmp);')
|
||||||
} else if variant_sym.kind == .enum_ {
|
} else if variant_sym.kind == .enum_ {
|
||||||
gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, false)
|
gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, true)
|
||||||
dec.writeln('\t\t$variant_typ value = json__decode_u64(jsonroot_$tmp);')
|
dec.writeln('\t\t$variant_typ value = ${js_dec_name('u64')}(jsonroot_$tmp);')
|
||||||
} else if variant_sym.name == 'time.Time' {
|
} else if variant_sym.name == 'time.Time' {
|
||||||
gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, false)
|
gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, true)
|
||||||
dec.writeln('\t\t$variant_typ value = time__unix(json__decode_i64(jsonroot_$tmp));')
|
dec.writeln('\t\t$variant_typ value = time__unix(${js_dec_name('i64')}(jsonroot_$tmp));')
|
||||||
} else {
|
} else {
|
||||||
gen_js_get_opt(js_dec_name(variant_typ), variant_typ, sym.cname, tmp, unmangled_variant_name, mut
|
gen_js_get_opt(js_dec_name(variant_typ), variant_typ, sym.cname, tmp,
|
||||||
dec, false)
|
unmangled_variant_name, mut dec, true)
|
||||||
dec.writeln('\t\t$variant_typ value = *($variant_typ*)(${tmp}.data);')
|
dec.writeln('\t\t$variant_typ value = *($variant_typ*)(${tmp}.data);')
|
||||||
}
|
}
|
||||||
dec.writeln('\t\tres = ${variant_typ}_to_sumtype_${sym.cname}(&value);')
|
dec.writeln('\t\tres = ${variant_typ}_to_sumtype_${sym.cname}(&value);')
|
||||||
dec.writeln('\t}')
|
dec.writeln('\t}')
|
||||||
|
} $else {
|
||||||
|
if variant_sym.name == 'time.Time' {
|
||||||
|
dec.writeln('\t\t\tif (strcmp("Time", $type_var) == 0) {')
|
||||||
|
gen_js_get(sym.cname, tmp, 'value', mut dec, true)
|
||||||
|
dec.writeln('\t\t\t\t$variant_typ $tmp = time__unix(${js_dec_name('i64')}(jsonroot_$tmp));')
|
||||||
|
dec.writeln('\t\t\t\tres = ${variant_typ}_to_sumtype_${sym.cname}(&$tmp);')
|
||||||
|
dec.writeln('\t\t\t}')
|
||||||
|
} else if !is_js_prim(variant_typ) && variant_sym.kind != .enum_ {
|
||||||
|
dec.writeln('\t\t\tif (strcmp("$unmangled_variant_name", $type_var) == 0) {')
|
||||||
|
dec.writeln('\t\t\t\tOption_$variant_typ $tmp = ${js_dec_name(variant_typ)}(root);')
|
||||||
|
dec.writeln('\t\t\t\tif (${tmp}.state != 0) {')
|
||||||
|
dec.writeln('\t\t\t\t\treturn (Option_$sym.cname){ .state = ${tmp}.state, .err = ${tmp}.err, .data = {0} };')
|
||||||
|
dec.writeln('\t\t\t\t}')
|
||||||
|
dec.writeln('\t\t\t\tres = ${variant_typ}_to_sumtype_${sym.cname}(($variant_typ*)${tmp}.data);')
|
||||||
|
dec.writeln('\t\t\t}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DECODING (inline)
|
||||||
|
$if !json_no_inline_sumtypes ? {
|
||||||
|
dec.writeln('\t\t}')
|
||||||
|
|
||||||
|
mut number_is_met := false
|
||||||
|
mut string_is_met := false
|
||||||
|
mut last_number_type := ''
|
||||||
|
|
||||||
|
if at_least_one_prim {
|
||||||
|
dec.writeln('\t} else {')
|
||||||
|
|
||||||
|
if 'bool' in variant_types {
|
||||||
|
var_t := 'bool'
|
||||||
|
dec.writeln('\t\tif (cJSON_IsBool(root)) {')
|
||||||
|
dec.writeln('\t\t\t$var_t value = ${js_dec_name(var_t)}(root);')
|
||||||
|
dec.writeln('\t\t\tres = ${var_t}_to_sumtype_${sym.cname}(&value);')
|
||||||
|
dec.writeln('\t\t}')
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, var_t in variant_types {
|
||||||
|
if variant_symbols[i].kind == .enum_ {
|
||||||
|
if number_is_met {
|
||||||
|
var_num := var_t.replace('__', '.')
|
||||||
|
last_num := last_number_type.replace('__', '.')
|
||||||
|
verror('json: can not decode `$sym.name` sumtype, too many numeric types (conflict of `$last_num` and `$var_num`), you can try to use alias for `$var_num` or compile v with `json_no_inline_sumtypes` flag')
|
||||||
|
}
|
||||||
|
number_is_met = true
|
||||||
|
last_number_type = var_t
|
||||||
|
dec.writeln('\t\tif (cJSON_IsNumber(root)) {')
|
||||||
|
dec.writeln('\t\t\t$var_t value = ${js_dec_name('u64')}(root);')
|
||||||
|
dec.writeln('\t\t\tres = ${var_t}_to_sumtype_${sym.cname}(&value);')
|
||||||
|
dec.writeln('\t\t}')
|
||||||
|
}
|
||||||
|
|
||||||
|
if var_t in ['string', 'rune'] {
|
||||||
|
if string_is_met {
|
||||||
|
var_num := var_t.replace('__', '.')
|
||||||
|
verror('json: can not decode `$sym.name` sumtype, too many string types (conflict of `string` and `rune`), you can try to use alias for `$var_num` or compile v with `json_no_inline_sumtypes` flag')
|
||||||
|
}
|
||||||
|
string_is_met = true
|
||||||
|
dec.writeln('\t\tif (cJSON_IsString(root)) {')
|
||||||
|
dec.writeln('\t\t\t$var_t value = ${js_dec_name(var_t)}(root);')
|
||||||
|
dec.writeln('\t\t\tres = ${var_t}_to_sumtype_${sym.cname}(&value);')
|
||||||
|
dec.writeln('\t\t}')
|
||||||
|
}
|
||||||
|
|
||||||
|
if var_t in ['i64', 'int', 'i8', 'u64', 'u32', 'u16', 'byte', 'u8', 'rune', 'f64',
|
||||||
|
'f32'] {
|
||||||
|
if number_is_met {
|
||||||
|
var_num := var_t.replace('__', '.')
|
||||||
|
last_num := last_number_type.replace('__', '.')
|
||||||
|
verror('json: can not decode `$sym.name` sumtype, too many numeric types (conflict of `$last_num` and `$var_num`), you can try to use alias for `$var_num` or compile v with `json_no_inline_sumtypes` flag')
|
||||||
|
}
|
||||||
|
number_is_met = true
|
||||||
|
last_number_type = var_t
|
||||||
|
dec.writeln('\t\tif (cJSON_IsNumber(root)) {')
|
||||||
|
dec.writeln('\t\t\t$var_t value = ${js_dec_name(var_t)}(root);')
|
||||||
|
dec.writeln('\t\t\tres = ${var_t}_to_sumtype_${sym.cname}(&value);')
|
||||||
|
dec.writeln('\t\t}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dec.writeln('\t}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,8 +415,8 @@ fn js_dec_name(typ string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_js_prim(typ string) bool {
|
fn is_js_prim(typ string) bool {
|
||||||
return typ in ['int', 'string', 'bool', 'f32', 'f64', 'i8', 'i16', 'i64', 'u16', 'u32', 'u64',
|
return typ in ['int', 'rune', 'string', 'bool', 'f32', 'f64', 'i8', 'i16', 'i64', 'u8', 'u16',
|
||||||
'byte']
|
'u32', 'u64', 'byte']
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) decode_array(value_type ast.Type) string {
|
fn (mut g Gen) decode_array(value_type ast.Type) string {
|
||||||
|
|
Loading…
Reference in New Issue