1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

all: variable sized options

This commit is contained in:
Emily Hudson 2020-05-31 11:57:26 +01:00 committed by GitHub
parent 3a36ed3802
commit 75eac291ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 251 additions and 94 deletions

View File

@ -44,12 +44,14 @@ fn panic_debug(line_no int, file, mod, fn_name, s string) {
eprintln(' line: ' + line_no.str())
eprintln('=========================================')
print_backtrace_skipping_top_frames(1)
break_if_debugger_attached()
C.exit(1)
}
pub fn panic(s string) {
eprintln('V panic: $s')
print_backtrace()
break_if_debugger_attached()
C.exit(1)
}

View File

@ -121,3 +121,6 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool {
}
return true
}
fn break_if_debugger_attached() {
}

View File

@ -190,6 +190,15 @@ fn unhandled_exception_handler(e &ExceptionPointers) u32 {
return 0
}
pub fn add_unhandled_exception_handler() {
fn add_unhandled_exception_handler() {
add_vectored_exception_handler(unhandled_exception_handler)
}
fn C.IsDebuggerPresent() bool
fn C.__debugbreak()
fn break_if_debugger_attached() {
if C.IsDebuggerPresent() {
C.__debugbreak()
}
}

View File

@ -12,12 +12,46 @@ struct Option2<T> {
}
*/
struct OptionBase {
ok bool
is_none bool
error string
ecode int
// Data is trailing after ecode
// and is not included in here but in the
// derived Option_xxx types
}
pub fn (o OptionBase) str() string {
if o.ok && !o.is_none {
return 'Option{ valid }'
}
if o.is_none {
return 'Option{ none }'
}
return 'Option{ error: "${o.error}" }'
}
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
fn opt_ok2(data voidptr, mut option &OptionBase, size int) {
unsafe {
*option = OptionBase {
ok: true
}
}
// use ecode to get the end of OptionBase and then memcpy into it
C.memcpy(byteptr(&option.ecode) + sizeof(int), data, size)
}
// Old option type used for bootstrapping
struct Option {
ok bool
is_none bool
error string
ecode int
data [400]byte
}

View File

@ -798,12 +798,17 @@ pub fn get_raw_stdin() []byte {
for {
pos := buf + offset
res := C.ReadFile(h_input, pos, block_bytes, &bytes_read, 0)
offset += bytes_read
if !res {
break
}
offset += bytes_read
buf = v_realloc(buf, offset + block_bytes + (block_bytes-bytes_read))
}
C.CloseHandle(h_input)
return array{element_size: 1 data: voidptr(buf) len: offset cap: offset }
}
} $else {

View File

@ -154,3 +154,7 @@ pub fn (mut f File) close() {
C.fflush(f.cfile)
C.fclose(f.cfile)
}
pub fn debugger_present() bool {
return false
}

View File

@ -346,3 +346,10 @@ pub type VectoredExceptionHandler fn(&ExceptionPointers)u32
pub fn add_vectored_exception_handler(first bool, handler VectoredExceptionHandler) {
C.AddVectoredExceptionHandler(u32(first), handler)
}
// this is defined in builtin_windows.c.v in builtin
// fn C.IsDebuggerPresent() bool
pub fn debugger_present() bool {
return C.IsDebuggerPresent()
}

View File

@ -51,6 +51,7 @@ struct Gen {
includes strings.Builder // all C #includes required by V modules
typedefs strings.Builder
typedefs2 strings.Builder
type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
inits strings.Builder // contents of `void _vinit(){}`
cleanups strings.Builder // contents of `void _vcleanup(){}`
@ -60,6 +61,7 @@ struct Gen {
comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI
pcs_declarations strings.Builder // -prof profile counter declarations for each function
hotcode_definitions strings.Builder // -live declarations & functions
options strings.Builder // `Option_xxxx` types
table &table.Table
pref &pref.Preferences
module_built string
@ -116,6 +118,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
includes: strings.new_builder(100)
typedefs: strings.new_builder(100)
typedefs2: strings.new_builder(100)
type_definitions: strings.new_builder(100)
definitions: strings.new_builder(100)
gowrappers: strings.new_builder(100)
stringliterals: strings.new_builder(100)
@ -125,6 +128,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
cleanups: strings.new_builder(100)
pcs_declarations: strings.new_builder(100)
hotcode_definitions: strings.new_builder(100)
options: strings.new_builder(100)
table: table
pref: pref
fn_decl: 0
@ -185,6 +189,10 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
b.writeln(g.cheaders.str())
b.writeln('\n// V includes:')
b.writeln(g.includes.str())
b.writeln('\n// V type definitions:')
b.writeln(g.type_definitions.str())
b.writeln('\n// V Option_xxx definitions:')
b.writeln(g.options.str())
b.writeln('\n// V definitions:')
b.writeln(g.definitions.str())
b.writeln('\n// V profile counters:')
@ -309,20 +317,9 @@ fn (mut g Gen) typ(t table.Type) string {
// T => int etc
return g.typ(g.cur_generic_type)
}
base := styp
if t.flag_is(.optional) {
if t.is_ptr() {
styp = styp.replace('*', '_ptr')
}
styp = 'Option_' + styp
// Register an optional if it's not registered yet
if styp !in g.optionals {
g.register_optional(t, base)
// println(styp)
x := styp // .replace('*', '_ptr') // handle option ptrs
g.typedefs2.writeln('typedef Option $x;')
g.optionals << styp
}
return g.register_optional(t)
}
/*
if styp.starts_with('C__') {
@ -341,17 +338,58 @@ fn (g &Gen) base_type(t table.Type) string {
return styp
}
fn (mut g Gen) register_optional(t table.Type, styp string) {
// TODO this really shouldnt be seperate from typ
// but I(emily) would rather have this generation
// all unified in one place so that it doesnt break
// if one location changes
fn (g &Gen) optional_type_name(t table.Type) (string, string) {
base := g.base_type(t)
mut styp := 'Option_$base'
if t.is_ptr() {
styp = styp.replace('*', '_ptr')
}
return styp, base
}
fn (g &Gen) optional_type_text(styp, base string) string {
x := styp // .replace('*', '_ptr') // handle option ptrs
// replace void with something else
size := if base == 'void' {
'int'
} else {
base
}
ret := 'struct $x {
bool ok;
bool is_none;
string v_error;
int ecode;
byte data[sizeof($size)];
} '
return ret
}
fn (mut g Gen) register_optional(t table.Type) string {
// g.typedefs2.writeln('typedef Option $x;')
no_ptr := styp.replace('*', '_ptr')
typ := if styp == 'void' { 'void*' } else { styp }
g.hotcode_definitions.writeln('typedef struct {
$typ data;
string error;
int ecode;
bool ok;
bool is_none;
} Option2_$no_ptr;')
styp, base := g.optional_type_name(t)
if styp !in g.optionals {
no_ptr := base.replace('*', '_ptr')
typ := if base == 'void' { 'void*' } else { base }
g.hotcode_definitions.writeln('typedef struct {
$typ data;
string error;
int ecode;
bool ok;
bool is_none;
} Option2_$no_ptr;')
// println(styp)
g.typedefs2.writeln('typedef struct $styp $styp;')
g.options.write(g.optional_type_text(styp, base))
g.options.writeln(';\n')
g.optionals << styp
}
return styp
}
// cc_type returns the Cleaned Concrete Type name, *without ptr*,
@ -388,18 +426,18 @@ typedef struct {
`.`
parent_styp := if is_c_parent { 'struct ' + parent.name[2..].replace('.', '__') } else { parent.name.replace('.',
'__') }
g.definitions.writeln('typedef $parent_styp $styp;')
g.type_definitions.writeln('typedef $parent_styp $styp;')
}
.array {
styp := typ.name.replace('.', '__')
g.definitions.writeln('typedef array $styp;')
g.type_definitions.writeln('typedef array $styp;')
}
.interface_ {
g.definitions.writeln('typedef _Interface ${c_name(typ.name)};')
g.type_definitions.writeln('typedef _Interface ${c_name(typ.name)};')
}
.map {
styp := typ.name.replace('.', '__')
g.definitions.writeln('typedef map $styp;')
g.type_definitions.writeln('typedef map $styp;')
}
.function {
info := typ.info as table.FnType
@ -416,14 +454,14 @@ typedef struct {
} else {
c_name(func.name)
}
g.definitions.write('typedef ${g.typ(func.return_type)} (*$fn_name)(')
g.type_definitions.write('typedef ${g.typ(func.return_type)} (*$fn_name)(')
for i, arg in func.args {
g.definitions.write(g.typ(arg.typ))
g.type_definitions.write(g.typ(arg.typ))
if i < func.args.len - 1 {
g.definitions.write(',')
g.type_definitions.write(',')
}
}
g.definitions.writeln(');')
g.type_definitions.writeln(');')
}
}
else {
@ -434,7 +472,7 @@ typedef struct {
}
pub fn (mut g Gen) write_multi_return_types() {
g.definitions.writeln('// multi return structs')
g.type_definitions.writeln('// multi return structs')
for typ in g.table.types {
// sym := g.table.get_type_symbol(typ)
if typ.kind != .multi_return {
@ -442,30 +480,30 @@ pub fn (mut g Gen) write_multi_return_types() {
}
name := typ.name.replace('.', '__')
info := typ.info as table.MultiReturn
g.definitions.writeln('typedef struct {')
g.type_definitions.writeln('typedef struct {')
// TODO copy pasta StructDecl
// for field in struct_info.fields {
for i, mr_typ in info.types {
type_name := g.typ(mr_typ)
g.definitions.writeln('\t$type_name arg${i};')
g.type_definitions.writeln('\t$type_name arg${i};')
}
g.definitions.writeln('} $name;\n')
g.type_definitions.writeln('} $name;\n')
// g.typedefs.writeln('typedef struct $name $name;')
}
}
pub fn (mut g Gen) write_variadic_types() {
if g.variadic_args.size > 0 {
g.definitions.writeln('// variadic structs')
g.type_definitions.writeln('// variadic structs')
}
for type_str, arg_len in g.variadic_args {
typ := table.Type(type_str.int())
type_name := g.typ(typ)
struct_name := 'varg_' + type_name.replace('*', '_ptr')
g.definitions.writeln('struct $struct_name {')
g.definitions.writeln('\tint len;')
g.definitions.writeln('\t$type_name args[$arg_len];')
g.definitions.writeln('};\n')
g.type_definitions.writeln('struct $struct_name {')
g.type_definitions.writeln('\tint len;')
g.type_definitions.writeln('\t$type_name args[$arg_len];')
g.type_definitions.writeln('};\n')
g.typedefs.writeln('typedef struct $struct_name $struct_name;')
}
}
@ -2223,26 +2261,26 @@ fn (g Gen) expr_is_multi_return_call(expr ast.Expr) bool {
}
fn (mut g Gen) return_statement(node ast.Return) {
g.write('return ')
if g.fn_decl.name == 'main' {
g.writeln('0;')
g.writeln('return 0;')
return
}
// got to do a correct check for multireturn
sym := g.table.get_type_symbol(g.fn_decl.return_type)
fn_return_is_multi := sym.kind == .multi_return
fn_return_is_optional := g.fn_decl.return_type.flag_is(.optional)
// handle none/error for optional
// handle promoting none/error/function returning 'Option'
if fn_return_is_optional {
optional_none := node.exprs[0] is ast.None
mut optional_error := false
match node.exprs[0] {
ast.CallExpr { optional_error = it.name == 'error' }
else { false }
}
if optional_none || optional_error {
mut is_regular_option := g.typ(node.types[0]) == 'Option'
if optional_none || is_regular_option {
tmp := g.new_tmp_var()
g.write('/*opt promotion*/ Option $tmp = ')
g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
g.write(';')
styp := g.typ(g.fn_decl.return_type)
g.writeln('return *($styp*)&$tmp;')
return
}
}
@ -2251,10 +2289,20 @@ fn (mut g Gen) return_statement(node ast.Return) {
// typ_sym := g.table.get_type_symbol(g.fn_decl.return_type)
// mr_info := typ_sym.info as table.MultiReturn
mut styp := ''
mut opt_tmp := ''
mut opt_type := ''
if fn_return_is_optional {
opt_type = g.typ(g.fn_decl.return_type)
// Create a tmp for this option
opt_tmp = g.new_tmp_var()
g.write('$opt_type $opt_tmp;')
styp = g.base_type(g.fn_decl.return_type)
g.write('opt_ok(&($styp/*X*/[]) { ')
g.write('opt_ok2(&($styp/*X*/[]) { ')
} else {
g.write('return ')
styp = g.typ(g.fn_decl.return_type)
}
// Use this to keep the tmp assignments in order
@ -2275,6 +2323,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
g.writeln(';')
multi_unpack += g.go_before_stmt(0)
g.write(s)
expr_types := expr_sym.mr_info().types
for j, _ in expr_types {
g.write('.arg$arg_idx=${tmp}.arg$j')
@ -2294,7 +2343,8 @@ fn (mut g Gen) return_statement(node ast.Return) {
}
g.write('}')
if fn_return_is_optional {
g.write(' }, sizeof($styp))')
g.writeln(' }, (OptionBase*)(&$opt_tmp), sizeof($styp));')
g.write('return $opt_tmp')
}
// Make sure to add our unpacks
g.insert_before_stmt(multi_unpack)
@ -2305,7 +2355,11 @@ fn (mut g Gen) return_statement(node ast.Return) {
if fn_return_is_optional && !node.types[0].flag_is(.optional) && return_sym.name !=
'Option' {
styp := g.base_type(g.fn_decl.return_type)
g.write('/*$return_sym.name*/opt_ok(&($styp[]) { ')
opt_type := g.typ(g.fn_decl.return_type)
// Create a tmp for this option
opt_tmp := g.new_tmp_var()
g.write('$opt_type $opt_tmp;')
g.write('/*:)$return_sym.name*/opt_ok2(&($styp[]) { ')
if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() {
// Automatic Dereference for optional
g.write('*')
@ -2316,9 +2370,11 @@ fn (mut g Gen) return_statement(node ast.Return) {
g.write(', ')
}
}
g.writeln(' }, sizeof($styp));')
g.writeln(' }, (OptionBase*)(&$opt_tmp), sizeof($styp));')
g.writeln('return $opt_tmp;')
return
}
g.write('return ')
cast_interface := sym.kind == .interface_ && node.types[0] != g.fn_decl.return_type
if cast_interface {
g.interface_call(node.types[0], g.fn_decl.return_type)
@ -2327,6 +2383,8 @@ fn (mut g Gen) return_statement(node ast.Return) {
if cast_interface {
g.write(')')
}
} else {
g.write('return')
}
g.writeln(';')
}
@ -2653,8 +2711,8 @@ fn (mut g Gen) write_sorted_types() {
// sort structs
types_sorted := g.sort_structs(types)
// Generate C code
g.definitions.writeln('// builtin types:')
g.definitions.writeln('//------------------ #endbuiltin')
g.type_definitions.writeln('// builtin types:')
g.type_definitions.writeln('//------------------ #endbuiltin')
g.write_types(types_sorted)
}
@ -2668,39 +2726,59 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
match typ.info {
table.Struct {
info := typ.info as table.Struct
// g.definitions.writeln('typedef struct {')
// TODO avoid buffer manip
start_pos := g.type_definitions.len
if info.is_union {
g.definitions.writeln('union $name {')
g.type_definitions.writeln('union $name {')
} else {
g.definitions.writeln('struct $name {')
g.type_definitions.writeln('struct $name {')
}
if info.fields.len > 0 {
for field in info.fields {
// Some of these structs may want to contain
// optionals that may not be defined at this point
// if this is the case then we are going to
// buffer manip out in front of the struct
// write the optional in and then continue
if field.typ.flag_is(.optional) {
// Dont use g.typ() here becuase it will register
// optional and we dont want that
last_text := g.type_definitions.after(start_pos).clone()
g.type_definitions.go_back_to(start_pos)
styp, base := g.optional_type_name(field.typ)
g.optionals << styp
g.typedefs2.writeln('typedef struct $styp $styp;')
g.type_definitions.writeln('${g.optional_type_text(styp, base)};')
g.type_definitions.write(last_text)
}
type_name := g.typ(field.typ)
field_name := c_name(field.name)
g.definitions.writeln('\t$type_name $field_name;')
g.type_definitions.writeln('\t$type_name $field_name;')
}
} else {
g.definitions.writeln('EMPTY_STRUCT_DECLARATION;')
g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;')
}
// g.definitions.writeln('} $name;\n')
// g.type_definitions.writeln('} $name;\n')
//
g.definitions.writeln('};\n')
g.type_definitions.writeln('};\n')
}
table.Alias {
// table.Alias, table.SumType { TODO
}
table.SumType {
g.definitions.writeln('')
g.definitions.writeln('// Sum type $name = ')
g.type_definitions.writeln('')
g.type_definitions.writeln('// Sum type $name = ')
for sv in it.variants {
g.definitions.writeln('// | ${sv:4d} = ${g.typ(sv):-20s}')
g.type_definitions.writeln('// | ${sv:4d} = ${g.typ(sv):-20s}')
}
g.definitions.writeln('typedef struct {')
g.definitions.writeln(' void* obj;')
g.definitions.writeln(' int typ;')
g.definitions.writeln('} $name;')
g.definitions.writeln('')
g.type_definitions.writeln('typedef struct {')
g.type_definitions.writeln(' void* obj;')
g.type_definitions.writeln(' int typ;')
g.type_definitions.writeln('} $name;')
g.type_definitions.writeln('')
}
table.ArrayFixed {
// .array_fixed {
@ -2712,7 +2790,7 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
if fixed.starts_with('C__') {
fixed = fixed[3..]
}
g.definitions.writeln('typedef $fixed $styp [$len];')
g.type_definitions.writeln('typedef $fixed $styp [$len];')
// }
}
else {}
@ -2987,8 +3065,8 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
}
}
} else if g.typ(node.expr_types[i]).starts_with('Option') {
str_fn_name := 'Option_str'
g.write('${str_fn_name}(*(Option*)&')
str_fn_name := 'OptionBase_str'
g.write('${str_fn_name}(*(OptionBase*)&')
g.expr(expr)
g.write(')')
} else {
@ -3166,7 +3244,9 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table.
g.writeln('\tv_panic(${cvar_name}.v_error);')
}
} else {
g.writeln('\treturn $cvar_name;')
// Now that option types are distinct we need a cast here
styp := g.typ(g.fn_decl.return_type)
g.writeln('\treturn *($styp *)&$cvar_name;')
}
}
g.write('}')
@ -3560,17 +3640,17 @@ fn (mut g Gen) go_stmt(node ast.GoStmt) {
if name in g.threaded_fns {
return
}
g.definitions.writeln('\ntypedef struct $wrapper_struct_name {')
g.type_definitions.writeln('\ntypedef struct $wrapper_struct_name {')
if it.is_method {
styp := g.typ(it.receiver_type)
g.definitions.writeln('\t$styp arg0;')
g.type_definitions.writeln('\t$styp arg0;')
}
for i, arg in it.args {
styp := g.typ(arg.typ)
g.definitions.writeln('\t$styp arg${i+1};')
g.type_definitions.writeln('\t$styp arg${i+1};')
}
g.definitions.writeln('} $wrapper_struct_name;')
g.definitions.writeln('void* ${wrapper_fn_name}($wrapper_struct_name *arg);')
g.type_definitions.writeln('} $wrapper_struct_name;')
g.type_definitions.writeln('void* ${wrapper_fn_name}($wrapper_struct_name *arg);')
g.gowrappers.writeln('void* ${wrapper_fn_name}($wrapper_struct_name *arg) {')
g.gowrappers.write('\t${name}(')
if it.is_method {
@ -3667,7 +3747,7 @@ fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string {
already_generated_key_no_ptr := '${styp}:${str_fn_name_no_ptr}'
if already_generated_key_no_ptr !in g.str_types {
g.str_types << already_generated_key_no_ptr
g.definitions.writeln('string ${str_fn_name_no_ptr}(${styp} it); // auto no_ptr version')
g.type_definitions.writeln('string ${str_fn_name_no_ptr}(${styp} it); // auto no_ptr version')
g.auto_str_funcs.writeln('string ${str_fn_name_no_ptr}(${styp} it){ return ${str_fn_name}(&it); }')
}
/*
@ -3724,7 +3804,7 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) {
} else {
verror("could not generate string method for type \'${styp}\'")
}
g.definitions.writeln('string ${str_fn_name}($styp it); // auto')
g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) {')
if convertor == 'bool' {
g.auto_str_funcs.writeln('\tstring tmp1 = string_add(tos_lit("${styp}("), (${convertor})it ? tos_lit("true") : tos_lit("false"));')
@ -3739,7 +3819,7 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) {
fn (mut g Gen) gen_str_for_enum(info table.Enum, styp, str_fn_name string) {
s := styp.replace('.', '__')
g.definitions.writeln('string ${str_fn_name}($styp it); // auto')
g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { /* gen_str_for_enum */')
g.auto_str_funcs.writeln('\tswitch(it) {')
for val in info.vals {
@ -3764,7 +3844,7 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) {
fnames2strfunc[field_styp] = field_fn_name
}
}
g.definitions.writeln('string ${str_fn_name}($styp x, int indent_count); // auto')
g.type_definitions.writeln('string ${str_fn_name}($styp x, int indent_count); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp x, int indent_count) {')
mut clean_struct_v_type_name := styp.replace('__', '.')
if styp.ends_with('*') {
@ -3841,7 +3921,7 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp, str_fn_name string) {
// eprintln('> sym.name: does not have method `str`')
g.gen_str_for_type_with_styp(info.elem_type, field_styp)
}
g.definitions.writeln('string ${str_fn_name}($styp a); // auto')
g.type_definitions.writeln('string ${str_fn_name}($styp a); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp a) {')
g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(a.len * 10);')
g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos_lit("["));')
@ -3902,7 +3982,7 @@ fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp, str_fn_name
if !sym.has_method('str') {
g.gen_str_for_type_with_styp(info.elem_type, field_styp)
}
g.definitions.writeln('string ${str_fn_name}($styp a); // auto')
g.type_definitions.writeln('string ${str_fn_name}($styp a); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp a) {')
g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder($info.size * 10);')
g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos_lit("["));')
@ -3944,7 +4024,7 @@ fn (mut g Gen) gen_str_for_map(info table.Map, styp, str_fn_name string) {
g.gen_str_for_type_with_styp(info.value_type, val_styp)
}
zero := g.type_default(info.value_type)
g.definitions.writeln('string ${str_fn_name}($styp m); // auto')
g.type_definitions.writeln('string ${str_fn_name}($styp m); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp m) { /* gen_str_for_map */')
g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(m.key_values.size*10);')
g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos_lit("{"));')

View File

@ -37,16 +37,20 @@ fn (mut g Gen) gen_json_for_type(typ table.Type) {
// cJSON_Parse(str) call is added by the compiler
// Code gen decoder
dec_fn_name := js_dec_name(sym.name)
// Make sure that this optional type actually exists
g.register_optional(typ)
dec.writeln('
//Option ${dec_fn_name}(cJSON* root, $styp* res) {
Option ${dec_fn_name}(cJSON* root) {
//Option_$styp ${dec_fn_name}(cJSON* root, $styp* res) {
Option_$styp ${dec_fn_name}(cJSON* root) {
$styp res;
if (!root) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
// fprintf(stderr, "Error in decode() for $styp error_ptr=: %%s\\n", error_ptr);
// printf("\\nbad js=%%s\\n", js.str);
return v_error(tos2(error_ptr));
Option err = v_error(tos2(error_ptr));
return *(Option_$styp *)&err;
}
}
')
@ -101,7 +105,9 @@ 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('Option_$styp ret;')
dec.writeln('opt_ok2(&res, (OptionBase*)&ret, sizeof(res));')
dec.writeln('return ret;\n}')
enc.writeln('\treturn o;\n}')
g.definitions.writeln(dec.str())
g.gowrappers.writeln(enc.str())

View File

@ -213,6 +213,13 @@ mut:
opt ?int
}
struct Thing2 {
mut:
opt ?Thing
}
fn test_opt_field() {
/*
QTODO