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

cgen: add g.auto_str_funcs, print &Struct pointers with enum fields

This commit is contained in:
Delyan Angelov 2020-04-21 13:26:46 +03:00
parent baf3bf6778
commit 7c1d6b60c2
3 changed files with 147 additions and 78 deletions

View File

@ -53,6 +53,7 @@ struct Gen {
inits strings.Builder // contents of `void _vinit(){}` inits strings.Builder // contents of `void _vinit(){}`
gowrappers strings.Builder // all go callsite wrappers gowrappers strings.Builder // all go callsite wrappers
stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined
auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs
table &table.Table table &table.Table
pref &pref.Preferences pref &pref.Preferences
mut: mut:
@ -105,6 +106,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
definitions: strings.new_builder(100) definitions: strings.new_builder(100)
gowrappers: strings.new_builder(100) gowrappers: strings.new_builder(100)
stringliterals: strings.new_builder(100) stringliterals: strings.new_builder(100)
auto_str_funcs: strings.new_builder(100)
inits: strings.new_builder(100) inits: strings.new_builder(100)
table: table table: table
pref: pref pref: pref
@ -147,10 +149,17 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
} }
// //
g.finish() g.finish()
return g.hashes() + '\n// V typedefs:\n' + g.typedefs.str() + '\n// V typedefs2:\n' + g.typedefs2.str() + return g.hashes() +
'\n// V cheaders:\n' + g.cheaders.str() + '\n// V includes:\n' + g.includes.str() + '\n// V definitions:\n' + '\n// V typedefs:\n' + g.typedefs.str() +
g.definitions.str() + '\n// V gowrappers:\n' + g.gowrappers.str() + '\n// V stringliterals:\n' + '\n// V typedefs2:\n' + g.typedefs2.str() +
g.stringliterals.str() + '\n// V out\n' + g.out.str() '\n// V cheaders:\n' + g.cheaders.str() +
'\n// V includes:\n' + g.includes.str() +
'\n// V definitions:\n' + g.definitions.str() +
'\n// V gowrappers:\n' + g.gowrappers.str() +
'\n// V stringliterals:\n' + g.stringliterals.str() +
'\n// V auto str functions:\n' + g.auto_str_funcs.str() +
'\n// V out\n' + g.out.str() +
'\n// THE END.'
} }
pub fn (g Gen) hashes() string { pub fn (g Gen) hashes() string {
@ -428,19 +437,29 @@ fn (mut g Gen) stmt(node ast.Stmt) {
ast.EnumDecl { ast.EnumDecl {
enum_name := it.name.replace('.', '__') enum_name := it.name.replace('.', '__')
g.typedefs.writeln('typedef enum {') g.typedefs.writeln('typedef enum {')
mut cur_enum_expr := ''
mut cur_enum_offset := 0
for j, field in it.fields { for j, field in it.fields {
g.typedefs.write('\t${enum_name}_$field.name') g.typedefs.write('\t${enum_name}_${field.name}')
if field.has_expr { if field.has_expr {
g.typedefs.write(' = ') g.typedefs.write(' = ')
pos := g.out.len pos := g.out.len
g.expr(field.expr) g.expr(field.expr)
expr_str := g.out.after(pos) expr_str := g.out.after(pos)
g.out.go_back(expr_str.len) g.out.go_back(expr_str.len)
g.typedefs.write('$expr_str') g.typedefs.write(expr_str)
cur_enum_expr = expr_str
cur_enum_offset = 0
} }
g.typedefs.writeln(', // $j') cur_value := if cur_enum_offset > 0 {
'${cur_enum_expr}+${cur_enum_offset}'
} else {
cur_enum_expr
}
g.typedefs.writeln(', // ${cur_value}')
cur_enum_offset++
} }
g.typedefs.writeln('} $enum_name;\n') g.typedefs.writeln('} ${enum_name};\n')
} }
ast.ExprStmt { ast.ExprStmt {
g.expr(it.expr) g.expr(it.expr)
@ -2302,12 +2321,13 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
} }
if is_var { if is_var {
styp := g.typ(node.expr_types[i]) styp := g.typ(node.expr_types[i])
g.gen_str_for_type(sym, styp) str_fn_name := styp_to_str_fn_name(styp)
g.write('${styp}_str(') g.gen_str_for_type(sym, styp, str_fn_name)
g.write('${str_fn_name}(')
g.enum_expr(expr) g.enum_expr(expr)
g.write(')') g.write(')')
g.write('.len, ') g.write('.len, ')
g.write('${styp}_str(') g.write('${str_fn_name}(')
g.enum_expr(expr) g.enum_expr(expr)
g.write(').str') g.write(').str')
} else { } else {
@ -2321,22 +2341,24 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
} }
} else if sym.kind in [.array, .array_fixed] { } else if sym.kind in [.array, .array_fixed] {
styp := g.typ(node.expr_types[i]) styp := g.typ(node.expr_types[i])
g.gen_str_for_type(sym, styp) str_fn_name := styp_to_str_fn_name(styp)
g.write('${styp}_str(') g.gen_str_for_type(sym, styp, str_fn_name)
g.write('${str_fn_name}(')
g.expr(expr) g.expr(expr)
g.write(')') g.write(')')
g.write('.len, ') g.write('.len, ')
g.write('${styp}_str(') g.write('${str_fn_name}(')
g.expr(expr) g.expr(expr)
g.write(').str') g.write(').str')
} else if sym.kind == .struct_ && !sym.has_method('str') { } else if sym.kind == .struct_ && !sym.has_method('str') {
styp := g.typ(node.expr_types[i]) styp := g.typ(node.expr_types[i])
g.gen_str_for_type(sym, styp) str_fn_name := styp_to_str_fn_name(styp)
g.write('${styp}_str(') g.gen_str_for_type(sym, styp, str_fn_name)
g.write('${str_fn_name}(')
g.expr(expr) g.expr(expr)
g.write(',0)') g.write(',0)')
g.write('.len, ') g.write('.len, ')
g.write('${styp}_str(') g.write('${str_fn_name}(')
g.expr(expr) g.expr(expr)
g.write(',0).str') g.write(',0).str')
} else { } else {
@ -2792,22 +2814,28 @@ fn (mut g Gen) is_expr(node ast.InfixExpr) {
g.expr(node.right) g.expr(node.right)
} }
fn styp_to_str_fn_name(styp string) string {
res := styp.replace('.', '__').replace('*','_ptr') + '_str'
return res
}
// already generated styp, reuse it // already generated styp, reuse it
fn (mut g Gen) gen_str_for_type(sym table.TypeSymbol, styp string) { fn (mut g Gen) gen_str_for_type(sym table.TypeSymbol, styp string, str_fn_name string) {
if sym.has_method('str') || styp in g.str_types { already_generated_key := '${styp}:${str_fn_name}'
if sym.has_method('str') || already_generated_key in g.str_types {
return return
} }
g.str_types << styp g.str_types << already_generated_key
match sym.info { match sym.info {
table.Alias { g.gen_str_default(sym, styp) } table.Alias { g.gen_str_default(sym, styp, str_fn_name) }
table.Array { g.gen_str_for_array(it, styp) } table.Array { g.gen_str_for_array(it, styp, str_fn_name) }
table.Enum { g.gen_str_for_enum(it, styp) } table.Enum { g.gen_str_for_enum(it, styp, str_fn_name) }
table.Struct { g.gen_str_for_struct(it, styp) } table.Struct { g.gen_str_for_struct(it, styp, str_fn_name) }
else { verror("could not generate string method for type \'${styp}\'") } else { verror("could not generate string method $str_fn_name for type \'${styp}\'") }
} }
} }
fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp string) { fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp string, str_fn_name string) {
mut convertor := '' mut convertor := ''
mut typename := '' mut typename := ''
if sym.parent_idx in table.integer_type_idxs { if sym.parent_idx in table.integer_type_idxs {
@ -2825,97 +2853,127 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp string) {
} else { } else {
verror("could not generate string method for type \'${styp}\'") verror("could not generate string method for type \'${styp}\'")
} }
g.definitions.writeln('string ${styp}_str($styp it) {') g.definitions.writeln('string ${str_fn_name}($styp it); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) {')
if convertor == 'bool' { if convertor == 'bool' {
g.definitions.writeln('\tstring tmp1 = string_add(tos3("${styp}("), (${convertor})it ? tos3("true") : tos3("false"));') g.auto_str_funcs.writeln('\tstring tmp1 = string_add(tos3("${styp}("), (${convertor})it ? tos3("true") : tos3("false"));')
} else { } else {
g.definitions.writeln('\tstring tmp1 = string_add(tos3("${styp}("), tos3(${typename}_str((${convertor})it).str));') g.auto_str_funcs.writeln('\tstring tmp1 = string_add(tos3("${styp}("), tos3(${typename}_str((${convertor})it).str));')
} }
g.definitions.writeln('\tstring tmp2 = string_add(tmp1, tos3(")"));') g.auto_str_funcs.writeln('\tstring tmp2 = string_add(tmp1, tos3(")"));')
g.definitions.writeln('\tstring_free(tmp1);') g.auto_str_funcs.writeln('\tstring_free(tmp1);')
g.definitions.writeln('\treturn tmp2;') g.auto_str_funcs.writeln('\treturn tmp2;')
g.definitions.writeln('}') g.auto_str_funcs.writeln('}')
} }
fn (mut g Gen) gen_str_for_enum(info table.Enum, styp string) { fn (mut g Gen) gen_str_for_enum(info table.Enum, styp string, str_fn_name string) {
s := styp.replace('.', '__') s := styp.replace('.', '__')
g.definitions.write('string ${s}_str($styp it) {\n\tswitch(it) {\n') g.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 i, val in info.vals { for i, val in info.vals {
g.definitions.write('\t\tcase ${s}_$val: return tos3("$val");\n') g.auto_str_funcs.writeln('\t\tcase ${s}_$val: return tos3("$val");')
} }
g.definitions.write('\t\tdefault: return tos3("unknown enum value"); } }\n') g.auto_str_funcs.writeln('\t\tdefault: return tos3("unknown enum value");')
g.auto_str_funcs.writeln('\t}')
g.auto_str_funcs.writeln('}')
} }
fn (mut g Gen) gen_str_for_struct(info table.Struct, styp string) { fn (mut g Gen) gen_str_for_struct(info table.Struct, styp string, str_fn_name string) {
// TODO: short it if possible // TODO: short it if possible
// generates all definitions of substructs // generates all definitions of substructs
mut fnames2strfunc := map[string]string
for i, field in info.fields { for i, field in info.fields {
sym := g.table.get_type_symbol(field.typ) sym := g.table.get_type_symbol(field.typ)
if sym.kind == .struct_ { if sym.kind in [.struct_, .array, .array_fixed, .enum_] {
field_styp := g.typ(field.typ) field_styp := g.typ(field.typ)
g.gen_str_for_type(sym, field_styp) field_fn_name := styp_to_str_fn_name( field_styp )
fnames2strfunc[ field_styp ] = field_fn_name
g.gen_str_for_type(sym, field_styp, field_fn_name)
} }
} }
s := styp.replace('.', '__') g.definitions.writeln('string ${str_fn_name}($styp x, int indent_count); // auto')
g.definitions.write('string ${s}_str($styp it, int indent_count) {\n') 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('*') {
deref_typ := styp.replace('*', '')
g.auto_str_funcs.writeln('\t${deref_typ} *it = x;')
clean_struct_v_type_name = '&' + clean_struct_v_type_name.replace('*', '')
}else{
deref_typ := styp
g.auto_str_funcs.writeln('\t${deref_typ} *it = &x;')
}
// generate ident / indent length = 4 spaces // generate ident / indent length = 4 spaces
g.definitions.write('\tstring indents = tos3("");\n\tfor (int i = 0; i < indent_count; i++) { indents = string_add(indents, tos3(" ")); }\n') g.auto_str_funcs.writeln('\tstring indents = tos3("");')
g.definitions.write('\treturn _STR("$styp {\\n') g.auto_str_funcs.writeln('\tfor (int i = 0; i < indent_count; i++) {')
g.auto_str_funcs.writeln('\t\tindents = string_add(indents, tos3(" "));')
g.auto_str_funcs.writeln('\t}')
g.auto_str_funcs.writeln('\treturn _STR("${clean_struct_v_type_name} {\\n"')
for field in info.fields { for field in info.fields {
fmt := g.type_to_fmt(field.typ) fmt := g.type_to_fmt(field.typ)
g.definitions.write('%.*s ' + '$field.name: $fmt\\n') g.auto_str_funcs.writeln('\t\t"%.*s ' + '$field.name: $fmt\\n"')
} }
g.definitions.write('%.*s}"') g.auto_str_funcs.write('\t\t"%.*s}"')
if info.fields.len > 0 { if info.fields.len > 0 {
g.definitions.write(', ') g.auto_str_funcs.write(',\n\t\t')
for i, field in info.fields { for i, field in info.fields {
sym := g.table.get_type_symbol(field.typ) sym := g.table.get_type_symbol(field.typ)
if sym.kind in [.struct_, .array, .array_fixed] { has_custom_str := sym.has_method('str')
field_styp := g.typ(field.typ) second_str_param := if has_custom_str {''} else {', indent_count + 1'}
second_str_param := if sym.has_method('str') { '' } else { ', indent_count + 1' } field_styp := g.typ(field.typ)
g.definitions.write('indents.len, indents.str, ${field_styp}_str(it.$field.name$second_str_param).len, ${field_styp}_str(it.$field.name$second_str_param).str') field_styp_fn_name := if has_custom_str {'${field_styp}_str'} else {fnames2strfunc[ field_styp ]}
if sym.kind == .enum_ {
g.auto_str_funcs.write('indents.len, indents.str, ')
g.auto_str_funcs.write('${field_styp_fn_name}( it->${field.name} ).len, ')
g.auto_str_funcs.write('${field_styp_fn_name}( it->${field.name} ).str ')
}else if sym.kind in [.struct_, .array, .array_fixed] {
g.auto_str_funcs.write('indents.len, indents.str, ')
g.auto_str_funcs.write('${field_styp_fn_name}( it->${field.name}${second_str_param} ).len, ')
g.auto_str_funcs.write('${field_styp_fn_name}( it->${field.name}${second_str_param} ).str ')
} else { } else {
g.definitions.write('indents.len, indents.str, it.$field.name') g.auto_str_funcs.write('indents.len, indents.str, it->${field.name}')
if field.typ == table.string_type { if field.typ == table.string_type {
g.definitions.write('.len, it.${field.name}.str') g.auto_str_funcs.write('.len, it->${field.name}.str')
} else if field.typ == table.bool_type { } else if field.typ == table.bool_type {
g.definitions.write(' ? 4 : 5, it.${field.name} ? "true" : "false"') g.auto_str_funcs.write(' ? 4 : 5, it->${field.name} ? "true" : "false"')
} }
} }
if i < info.fields.len - 1 { if i < info.fields.len - 1 {
g.definitions.write(', ') g.auto_str_funcs.write(',\n\t\t')
} }
} }
} }
g.definitions.writeln(', indents.len, indents.str);\n}') g.auto_str_funcs.writeln(',')
g.auto_str_funcs.writeln('\t\tindents.len, indents.str);')
g.auto_str_funcs.writeln('}')
} }
fn (mut g Gen) gen_str_for_array(info table.Array, styp string) { fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name string) {
s := styp.replace('.', '__')
sym := g.table.get_type_symbol(info.elem_type) sym := g.table.get_type_symbol(info.elem_type)
field_styp := g.typ(info.elem_type) field_styp := g.typ(info.elem_type)
if sym.kind == .struct_ && !sym.has_method('str') { if sym.kind == .struct_ && !sym.has_method('str') {
g.gen_str_for_type(sym, field_styp) g.gen_str_for_type(sym, field_styp, styp_to_str_fn_name(field_styp) )
} }
g.definitions.writeln('string ${s}_str($styp a) {') g.definitions.writeln('string ${str_fn_name}($styp a); // auto')
g.definitions.writeln('\tstrings__Builder sb = strings__new_builder(a.len * 10);') g.auto_str_funcs.writeln('string ${str_fn_name}($styp a) {')
g.definitions.writeln('\tstrings__Builder_write(&sb, tos3("["));') g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(a.len * 10);')
g.definitions.writeln('\tfor (int i = 0; i < a.len; i++) {') g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos3("["));')
g.definitions.writeln('\t\t${field_styp} it = (*(${field_styp}*)array_get(a, i));') g.auto_str_funcs.writeln('\tfor (int i = 0; i < a.len; i++) {')
g.auto_str_funcs.writeln('\t\t${field_styp} it = (*(${field_styp}*)array_get(a, i));')
if sym.kind == .struct_ && !sym.has_method('str') { if sym.kind == .struct_ && !sym.has_method('str') {
g.definitions.writeln('\t\t\tstrings__Builder_write(&sb, ${field_styp}_str(it,0));') g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, ${field_styp}_str(it,0));')
} else if sym.kind in [.f32, .f64] { } else if sym.kind in [.f32, .f64] {
g.definitions.writeln('\t\t\tstrings__Builder_write(&sb, _STR("%g", it));') g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, _STR("%g", it));')
} else { } else {
g.definitions.writeln('\t\t\tstrings__Builder_write(&sb, ${field_styp}_str(it));') g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, ${field_styp}_str(it));')
} }
g.definitions.writeln('\t\tif (i != a.len-1) {') g.auto_str_funcs.writeln('\t\tif (i != a.len-1) {')
g.definitions.writeln('\t\t\tstrings__Builder_write(&sb, tos3(", "));') g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, tos3(", "));')
g.definitions.writeln('\t\t}') g.auto_str_funcs.writeln('\t\t}')
g.definitions.writeln('\t}') g.auto_str_funcs.writeln('\t}')
g.definitions.writeln('\tstrings__Builder_write(&sb, tos3("]"));') g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos3("]"));')
g.definitions.writeln('\treturn strings__Builder_str(&sb);') g.auto_str_funcs.writeln('\treturn strings__Builder_str(&sb);')
g.definitions.writeln('}') g.auto_str_funcs.writeln('}')
} }
fn (g Gen) type_to_fmt(typ table.Type) string { fn (g Gen) type_to_fmt(typ table.Type) string {
@ -2926,6 +2984,8 @@ fn (g Gen) type_to_fmt(typ table.Type) string {
return "\'%.*s\'" return "\'%.*s\'"
} else if typ == table.bool_type { } else if typ == table.bool_type {
return '%.*s' return '%.*s'
} else if sym.kind == .enum_ {
return '%.*s'
} else if typ in [table.f32_type, table.f64_type] { } else if typ in [table.f32_type, table.f64_type] {
return '%g' // g removes trailing zeros unlike %f return '%g' // g removes trailing zeros unlike %f
} }

View File

@ -310,12 +310,13 @@ fn (g mut Gen) fn_call(node ast.CallExpr) {
if table.type_is_ptr(typ) { if table.type_is_ptr(typ) {
styp = styp.replace('*', '') styp = styp.replace('*', '')
} }
g.gen_str_for_type(sym, styp) mut str_fn_name := styp_to_str_fn_name(styp)
g.gen_str_for_type(sym, styp, str_fn_name)
if g.autofree && !table.type_is(typ, .optional) { if g.autofree && !table.type_is(typ, .optional) {
// Create a temporary variable so that the value can be freed // Create a temporary variable so that the value can be freed
tmp := g.new_tmp_var() tmp := g.new_tmp_var()
// tmps << tmp // tmps << tmp
g.write('string $tmp = ${styp}_str(') g.write('string $tmp = ${str_fn_name}(')
g.expr(node.args[0].expr) g.expr(node.args[0].expr)
g.writeln('); ${print_method}($tmp); string_free($tmp); //MEM2 $styp') g.writeln('); ${print_method}($tmp); string_free($tmp); //MEM2 $styp')
} else { } else {
@ -334,10 +335,11 @@ fn (g mut Gen) fn_call(node ast.CallExpr) {
if table.type_is_ptr(typ) && sym.kind != .struct_ { if table.type_is_ptr(typ) && sym.kind != .struct_ {
// ptr_str() for pointers // ptr_str() for pointers
styp = 'ptr' styp = 'ptr'
str_fn_name = 'ptr_str'
} }
if sym.kind == .enum_ { if sym.kind == .enum_ {
if is_var { if is_var {
g.write('${print_method}(${styp}_str(') g.write('${print_method}(${str_fn_name}(')
} else { } else {
// when no var, print string directly // when no var, print string directly
g.write('${print_method}(tos3("') g.write('${print_method}(tos3("')
@ -352,7 +354,7 @@ fn (g mut Gen) fn_call(node ast.CallExpr) {
g.write('"') g.write('"')
} }
} else { } else {
g.write('${print_method}(${styp}_str(') g.write('${print_method}(${str_fn_name}(')
if table.type_is_ptr(typ) && sym.kind == .struct_ { if table.type_is_ptr(typ) && sym.kind == .struct_ {
// dereference // dereference
g.write('*') g.write('*')

View File

@ -26,3 +26,10 @@ fn test_enum_non_default_value() {
assert int(t.e) == 22 assert int(t.e) == 22
assert 't.e: $t.e | int(t.e): ${int(t.e).str()}' == 't.e: third | int(t.e): 22' assert 't.e: $t.e | int(t.e): ${int(t.e).str()}' == 't.e: third | int(t.e): 22'
} }
fn test_generation_of_string_interpolation_method_for_pointer_to_struct_containing_enum_fields(){
t := &MyStruct{
e: .third
}
assert 't: $t' == 't: &MyStruct {\n e: third\n}'
}