From b288ecb795c6baf1585c7f59bc753bc9c7eaa8be Mon Sep 17 00:00:00 2001 From: Kris Cherven <50562493+krischerven@users.noreply.github.com> Date: Tue, 21 Apr 2020 22:00:38 -0400 Subject: [PATCH] cgen: add map string generation --- vlib/builtin/map.v | 10 ++++++ vlib/v/gen/cgen.v | 60 +++++++++++++++++++++++++++++-- vlib/v/tests/map_to_string_test.v | 27 ++++++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 vlib/v/tests/map_to_string_test.v diff --git a/vlib/builtin/map.v b/vlib/builtin/map.v index 670f295178..ec41fe2f3d 100644 --- a/vlib/builtin/map.v +++ b/vlib/builtin/map.v @@ -135,6 +135,16 @@ fn (d mut DenseArray) push(kv KeyValue) u32 { return push_index } +// Private function. Used to implement array[] operator +fn (d DenseArray) get(i int) voidptr { + $if !no_bounds_checking? { + if i < 0 || i >= d.size { + panic('DenseArray.get: index out of range (i == $i, d.len == $d.size)') + } + } + return byteptr(d.data) + i * sizeof(KeyValue) +} + // Move all zeros to the end of the array // and resize array fn (d mut DenseArray) zeros_to_end() { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index c1ee187887..bb79b8a52e 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2284,7 +2284,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { g.write('%.*s') } else if node.expr_types[i] in [table.f32_type, table.f64_type] { g.write('%g') - } else if sym.kind == .struct_ && !sym.has_method('str') { + } else if sym.kind in [.struct_, .map] && !sym.has_method('str') { g.write('%.*s') } else if node.expr_types[i] == table.i16_type { g.write('%"PRId16"') @@ -2364,6 +2364,17 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { g.write('${str_fn_name}(') g.expr(expr) g.write(').str') + } else if sym.kind == .map && !sym.has_method('str') { + styp := g.typ(node.expr_types[i]) + str_fn_name := styp_to_str_fn_name(styp) + g.gen_str_for_type(sym, styp, str_fn_name) + g.write('${str_fn_name}(') + g.expr(expr) + g.write(')') + g.write('.len, ') + g.write('${str_fn_name}(') + g.expr(expr) + g.write(').str') } else if sym.kind == .struct_ && !sym.has_method('str') { styp := g.typ(node.expr_types[i]) str_fn_name := styp_to_str_fn_name(styp) @@ -2845,6 +2856,7 @@ fn (mut g Gen) gen_str_for_type(sym table.TypeSymbol, styp string, str_fn_name s table.Array { g.gen_str_for_array(it, styp, str_fn_name) } table.Enum { g.gen_str_for_enum(it, styp, str_fn_name) } table.Struct { g.gen_str_for_struct(it, styp, str_fn_name) } + table.Map { g.gen_str_for_map(it, styp, str_fn_name) } else { verror("could not generate string method $str_fn_name for type \'${styp}\'") } } } @@ -2899,7 +2911,7 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp string, str_fn_name st mut fnames2strfunc := map[string]string for i, field in info.fields { sym := g.table.get_type_symbol(field.typ) - if sym.kind in [.struct_, .array, .array_fixed, .enum_] { + if sym.kind in [.struct_, .array, .array_fixed, .map, .enum_] { field_styp := g.typ(field.typ) field_fn_name := styp_to_str_fn_name( field_styp ) fnames2strfunc[ field_styp ] = field_fn_name @@ -2990,9 +3002,51 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name stri g.auto_str_funcs.writeln('}') } +fn (mut g Gen) gen_str_for_map(info table.Map, styp string, str_fn_name string) { + key_sym := g.table.get_type_symbol(info.key_type) + key_styp := g.typ(info.key_type) + if key_sym.kind == .struct_ && !key_sym.has_method('str') { + g.gen_str_for_type(key_sym, key_styp, styp_to_str_fn_name(key_styp)) + } + val_sym := g.table.get_type_symbol(info.value_type) + val_styp := g.typ(info.value_type) + if val_sym.kind == .struct_ && !val_sym.has_method('str') { + g.gen_str_for_type(val_sym, val_styp, styp_to_str_fn_name(val_styp)) + } + zero := g.type_default(info.value_type) + g.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, tos3("$styp"));') + g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos3("{"));') + g.auto_str_funcs.writeln('\tfor (unsigned int i = 0; i < m.key_values.size; i++) {') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, (*(string*)DenseArray_get(m.key_values, i)));') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, tos3(": "));') + g.auto_str_funcs.write('\t$val_styp it = (*($val_styp*)map_get3(') + g.auto_str_funcs.write('m, (*(string*)DenseArray_get(m.key_values, i))') + g.auto_str_funcs.write(', ') + g.auto_str_funcs.writeln(' &($val_styp[]) { $zero }));') + if val_sym.kind == .string { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, it);') + } else if val_sym.kind == .struct_ && !val_sym.has_method('str') { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${val_styp}_str(it,0));') + } else if val_sym.kind in [.f32, .f64] { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, _STR("%g", it));') + } else { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, ${val_styp}_str(it));') + } + g.auto_str_funcs.writeln('\t\tif (i != m.key_values.size-1) {') + g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, tos3(", "));') + g.auto_str_funcs.writeln('\t\t}') + g.auto_str_funcs.writeln('\t}') + g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos3("}"));') + g.auto_str_funcs.writeln('\treturn strings__Builder_str(&sb);') + g.auto_str_funcs.writeln('}') +} + fn (g Gen) type_to_fmt(typ table.Type) string { sym := g.table.get_type_symbol(typ) - if sym.kind in [.struct_, .array, .array_fixed] { + if sym.kind in [.struct_, .array, .array_fixed, .map] { return '%.*s' } else if typ == table.string_type { return "\'%.*s\'" diff --git a/vlib/v/tests/map_to_string_test.v b/vlib/v/tests/map_to_string_test.v new file mode 100644 index 0000000000..7af0b3a80a --- /dev/null +++ b/vlib/v/tests/map_to_string_test.v @@ -0,0 +1,27 @@ +struct Test { + a bool + b int + y string +} + +fn test_interpolation_map_to_string() { + a := map[string]string + a['1'] = 'one' + a['2'] = 'two' + a['3'] = 'three' + assert '$a' == 'map_string_string{1: one, 2: two, 3: three}' + b := map[string]int + b['1'] = 1 + b['2'] = 2 + b['3'] = 3 + assert '$b' == 'map_string_int{1: 1, 2: 2, 3: 3}' + c := map[string]bool + c['1'] = true + c['2'] = false + assert '$c' == 'map_string_bool{1: true, 2: false}' + d := map[string]Test + d['1'] = Test{true, 0, 'abc'} + d['2'] = Test{true, 1, 'def'} + d['3'] = Test{false, 2, 'ghi'} + assert '$d'.replace('\n', '').replace('\'', '"') == 'map_string_Test{1: Test { a: true b: 0 y: "abc"}, 2: Test { a: true b: 1 y: "def"}, 3: Test { a: false b: 2 y: "ghi"}}' +}