v/vlib/compiler/json_gen.v

163 lines
4.2 KiB
V
Raw Normal View History

2019-06-22 20:20:28 +02:00
// Copyright (c) 2019 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 compiler
2019-06-22 20:20:28 +02:00
// TODO replace with comptime code generation.
// TODO remove cJSON dependency.
// OLD: User decode_User(string js) {
// now it's
// User decode_User(cJSON* root) {
// User res;
// res.name = decode_string(js_get(root, "name"));
// res.profile = decode_Profile(js_get(root, "profile"));
// return res;
// }
// Codegen json_decode/encode funcs
fn (p mut Parser) gen_json_for_type(typ Type) {
mut dec := ''
mut enc := ''
t := typ.name
if t == 'int' || t == 'string' || t == 'bool' {
return
}
2019-07-29 18:21:36 +02:00
if p.first_pass() {
2019-06-22 20:20:28 +02:00
return
}
// println('gen_json_for_type( $typ.name )')
// Register decoder fn
2019-12-19 22:29:37 +01:00
mut dec_fn := Fn{
mod: p.mod
2019-06-22 20:20:28 +02:00
typ: 'Option_$typ.name'
name: js_dec_name(t)
}
// Already registered? Skip.
if p.table.known_fn(dec_fn.name) {
return
}
// decode_TYPE funcs receive an actual cJSON* object to decode
// cJSON_Parse(str) call is added by the compiler
2019-12-19 22:29:37 +01:00
arg := Var{
2019-06-22 20:20:28 +02:00
typ: 'cJSON*'
}
dec_fn.args << arg
p.table.register_fn(dec_fn)
// Register encoder fn
2019-12-19 22:29:37 +01:00
mut enc_fn := Fn{
mod: p.mod
2019-06-22 20:20:28 +02:00
typ: 'cJSON*'
name: js_enc_name(t)
}
// encode_TYPE funcs receive an object to encode
2019-12-19 22:29:37 +01:00
enc_arg := Var{
2019-06-22 20:20:28 +02:00
typ: t
}
enc_fn.args << enc_arg
p.table.register_fn(enc_fn)
// Code gen decoder
dec += '
2019-12-08 20:22:47 +01:00
//$t $dec_fn.name (cJSON* root) {
Option ${dec_fn.name}(cJSON* root, $t* res) {
2019-08-17 21:19:37 +02:00
// $t res;
2019-06-22 20:20:28 +02:00
if (!root) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
fprintf(stderr, "Error in decode() for $t error_ptr=: %%s\\n", error_ptr);
2019-08-17 21:19:37 +02:00
// printf("\\nbad js=%%s\\n", js.str);
2019-06-23 00:47:49 +02:00
return v_error(tos2(error_ptr));
2019-06-22 20:20:28 +02:00
}
}
'
// Code gen encoder
enc += '
2019-12-08 20:22:47 +01:00
cJSON* $enc_fn.name ($t val) {
2019-06-22 20:20:28 +02:00
cJSON *o = cJSON_CreateObject();
2019-08-17 21:19:37 +02:00
string res = tos2("");
2019-06-22 20:20:28 +02:00
'
// Handle arrays
if t.starts_with('array_') {
dec += p.decode_array(t)
enc += p.encode_array(t)
}
// Range through fields
for field in typ.fields {
if field.attr == 'skip' {
continue
}
2019-12-19 22:29:37 +01:00
name := if field.attr.starts_with('json:') { field.attr[5..] } else { field.name }
2019-06-22 20:20:28 +02:00
field_type := p.table.find_type(field.typ)
_typ := field.typ.replace('*', '')
enc_name := js_enc_name(_typ)
if field.attr == 'raw' {
2019-12-19 22:29:37 +01:00
dec += ' res->$field.name = tos2(cJSON_PrintUnformatted(' + 'js_get(root, "$name")));\n'
}
else {
// Now generate decoders for all field types in this struct
// need to do it here so that these functions are generated first
p.gen_json_for_type(field_type)
dec_name := js_dec_name(_typ)
if is_js_prim(_typ) {
2019-12-19 22:29:37 +01:00
dec += ' res->$field.name = $dec_name (js_get(' + 'root, "$name"))'
}
else {
2019-12-08 20:22:47 +01:00
dec += ' $dec_name (js_get(root, "$name"), & (res->$field.name))'
}
dec += ';\n'
2019-06-22 20:20:28 +02:00
}
2019-12-08 20:22:47 +01:00
enc += ' cJSON_AddItemToObject(o, "$name",$enc_name (val.$field.name)); \n'
2019-06-22 20:20:28 +02:00
}
// cJSON_delete
2019-12-19 22:29:37 +01:00
// p.cgen.fns << '$dec return opt_ok(res); \n}'
2019-07-03 21:47:49 +02:00
p.cgen.fns << '$dec return opt_ok(res, sizeof(*res)); \n}'
2019-06-22 20:20:28 +02:00
p.cgen.fns << '/*enc start*/ $enc return o;}'
}
fn is_js_prim(typ string) bool {
2019-12-19 22:29:37 +01:00
return typ == 'int' || typ == 'string' || typ == 'bool' || typ == 'f32' || typ == 'f64' || typ == 'i8' || typ == 'i16' || typ == 'i64' || typ == 'u16' || typ == 'u32' || typ == 'u64'
2019-06-22 20:20:28 +02:00
}
fn (p mut Parser) decode_array(array_type string) string {
typ := array_type.replace('array_', '')
2019-06-22 20:20:28 +02:00
t := p.table.find_type(typ)
fn_name := js_dec_name(typ)
// If we have `[]Profile`, have to register a Profile en(de)coder first
p.gen_json_for_type(t)
mut s := ''
if is_js_prim(typ) {
2019-12-08 20:22:47 +01:00
s = '$typ val= $fn_name (jsval); '
2019-06-22 20:20:28 +02:00
}
else {
2019-12-08 20:22:47 +01:00
s = ' $typ val; $fn_name (jsval, &val); '
2019-06-22 20:20:28 +02:00
}
return '
*res = new_array(0, 0, sizeof($typ));
const cJSON *jsval = NULL;
cJSON_ArrayForEach(jsval, root)
{
2019-08-17 21:19:37 +02:00
$s
array_push(res, &val);
2019-06-22 20:20:28 +02:00
}
'
}
fn js_enc_name(typ string) string {
name := 'json__jsencode_$typ'
return name
}
fn js_dec_name(typ string) string {
name := 'json__jsdecode_$typ'
return name
}
fn (p &Parser) encode_array(array_type string) string {
typ := array_type.replace('array_', '')
2019-06-22 20:20:28 +02:00
fn_name := js_enc_name(typ)
return '
o = cJSON_CreateArray();
for (int i = 0; i < val.len; i++){
2019-12-08 20:22:47 +01:00
cJSON_AddItemToArray(o, $fn_name ( (($typ*)val.data)[i] ));
2019-08-17 21:19:37 +02:00
}
2019-06-22 20:20:28 +02:00
'
}