diff --git a/vlib/compiler/comptime.v b/vlib/compiler/comptime.v index af1e3f63ce..16ec766ba5 100644 --- a/vlib/compiler/comptime.v +++ b/vlib/compiler/comptime.v @@ -357,6 +357,41 @@ fn (p mut Parser) comptime_method_call(typ Type) { } } +fn (p mut Parser) gen_default_str_method_if_missing(typename string) (bool, string) { + // NB: string_type_name can be != typename, if the base typename has str() + mut string_type_name := typename + typ := p.table.find_type(typename) + is_varg := typename.starts_with('varg_') + is_array := typename.starts_with('array_') + is_struct := typ.cat == .struct_ + mut has_str_method := p.table.type_has_method(typ, 'str') + if !has_str_method { + if is_varg { + p.gen_varg_str(typ) + has_str_method = true + } + else if is_array { + p.gen_array_str(typ) + has_str_method = true + } + else if is_struct { + p.gen_struct_str(typ) + has_str_method = true + } + else { + btypename := p.base_type(typ.name) + if btypename != typ.name { + base_type := p.find_type(btypename) + if base_type.has_method('str'){ + string_type_name = base_type.name + has_str_method = true + } + } + } + } + return has_str_method, string_type_name +} + fn (p mut Parser) gen_array_str(typ Type) { if typ.has_method('str') { return @@ -379,7 +414,10 @@ fn (p mut Parser) gen_array_str(typ Type) { p.gen_array_str(elm_type2) } else if p.typ_to_fmt(elm_type, 0) == '' && !p.table.type_has_method(elm_type2, 'str') { - p.error('cant print ${elm_type}[], unhandled print of ${elm_type}') + has_str_method, _ := p.gen_default_str_method_if_missing( elm_type ) + if !has_str_method { + p.error('cant print []${elm_type}, unhandled print of ${elm_type}') + } } p.v.vgen_buf.writeln(' pub fn (a $typ.name) str() string { @@ -398,7 +436,7 @@ pub fn (a $typ.name) str() string { p.cgen.fns << 'string ${typ.name}_str();' } -// `Foo { bar: 3, baz: 'hi' }` => '{ bar: 3, baz: "hi" }' +// `Foo { bar: 3, baz: 'hi' }` => interpolated to string 'Foo { bar: 3, baz: "hi" }' fn (p mut Parser) gen_struct_str(typ Type) { p.add_method(typ.name, Fn{ name: 'str' @@ -413,7 +451,8 @@ fn (p mut Parser) gen_struct_str(typ Type) { }) mut sb := strings.new_builder(typ.fields.len * 20) sb.writeln('pub fn (a $typ.name) str() string {\nreturn') - sb.writeln("'{") + short_struct_name := typ.name.all_after('__') + sb.writeln("'$short_struct_name {") for field in typ.fields { sb.writeln('\t$field.name: $' + 'a.${field.name}') } diff --git a/vlib/compiler/string_expression.v b/vlib/compiler/string_expression.v index e8bd11f4e7..02a34ec87d 100644 --- a/vlib/compiler/string_expression.v +++ b/vlib/compiler/string_expression.v @@ -114,15 +114,10 @@ fn (p mut Parser) string_expr() { else { f := p.typ_to_fmt(typ, 0) if f == '' { - is_array := typ.starts_with('array_') - typ2 := p.table.find_type(typ) - has_str_method := p.table.type_has_method(typ2, 'str') - if is_array || has_str_method { - if is_array && !has_str_method { - p.gen_array_str(typ2) - } + has_str_method, styp := p.gen_default_str_method_if_missing( typ ) + if has_str_method { tmp_var := p.get_tmp() - p.cgen.insert_before('string $tmp_var = ${typ}_str(${val});') + p.cgen.insert_before('string $tmp_var = ${styp}_str(${val});') args = args.all_before_last(val) + '${tmp_var}.len, ${tmp_var}.str' format += '%.*s ' } @@ -137,7 +132,7 @@ fn (p mut Parser) string_expr() { if complex_inter { p.fgen('}') } - + // p.fgen('\'') // println("hello %d", num) optimization. if p.cgen.nogen { @@ -165,4 +160,3 @@ fn (p mut Parser) string_expr() { p.gen('_STR($format$args)') } } - diff --git a/vlib/compiler/tests/prod/assoc.prod.v.expected.txt b/vlib/compiler/tests/prod/assoc.prod.v.expected.txt index 0d8e50ab55..d69f916aa1 100644 --- a/vlib/compiler/tests/prod/assoc.prod.v.expected.txt +++ b/vlib/compiler/tests/prod/assoc.prod.v.expected.txt @@ -1,3 +1,3 @@ -{ +MyStruct { s: 6 } \ No newline at end of file diff --git a/vlib/compiler/tests/string_interpolation_array_of_structs_test.v b/vlib/compiler/tests/string_interpolation_array_of_structs_test.v new file mode 100644 index 0000000000..e9a8a78311 --- /dev/null +++ b/vlib/compiler/tests/string_interpolation_array_of_structs_test.v @@ -0,0 +1,34 @@ +// This file tests whether v can generate default string methods for both: +// a) an array of custom structs, +// b) also for the custom struct itself (when the .str() for it is missing). +// +// NB: this is very simillar to string_interpolation_struct_test.v +// but they should NOT be merged into 1 file. If you merge it with +// string_interpolation_struct_test.v, which tests whether the compiler +// can generate the default method for a struct, then the b) case of +// this test will be done by *that* test, and so the testing will +// be incomplete. + +struct Man { + name string + age int + interests []string +} + +fn test_default_struct_array_of_structs_interpolation(){ + people := [ + Man{'Superman', 30, ['flying','fighting evil','being nice']}, + Man{'Bilbo Baggins', 111, ['exploring', 'hiding']}, + ] + s := '$people' // the compiler should generate code for both a) and b) + assert s.contains('Man {') + assert s.contains('name: Superman') + assert s.contains('age: 30') + assert s.contains('"being nice"') + assert s.contains('}, Man {') + assert s.contains('name: Bilbo Baggins') + assert s.contains('age: 111') + assert s.contains('interests: ["exploring", "hiding"]') + assert s.contains('}]') + //println(s) +} diff --git a/vlib/compiler/tests/string_interpolation_struct_test.v b/vlib/compiler/tests/string_interpolation_struct_test.v new file mode 100644 index 0000000000..8bf3f84213 --- /dev/null +++ b/vlib/compiler/tests/string_interpolation_struct_test.v @@ -0,0 +1,21 @@ +// This file tests whether V can generate a convenience default .str() method +// for a custom struct, when the developer has not defined one himself. +// The .str() methods are used for string interpolation and for println() calls. + +struct Man { + name string + age int + interests []string +} + +fn test_default_struct_string_interpolation(){ + superman := Man{'Superman', 30, ['flying','fighting evil','being nice']} + s := '$superman' + assert s.contains('Man {') + assert s.contains('name: Superman') + assert s.contains('age: 30') + assert s.contains('interests: [') + assert s.contains('"being nice"') + assert s.contains('}') + //println(s) +} diff --git a/vlib/compiler/tests/string_interpolation_variadic_test.v b/vlib/compiler/tests/string_interpolation_variadic_test.v new file mode 100644 index 0000000000..f603ecf3fc --- /dev/null +++ b/vlib/compiler/tests/string_interpolation_variadic_test.v @@ -0,0 +1,34 @@ +// This file tests whether V can generate a convenience default .str() method +// for var args of a custom type, when the developer has NOT defined one. +// Although similar to string_interpolation_struct_test.v, they should not be +// merged. + +struct Man { + name string + age int + interests []string +} + +fn my_variadic_function(x ...Man) string { + return '$x' // this interpolation should generate .str() methods for Man +} + +fn test_vargs_string_interpolation(){ + man := Man{'Me', 38, ['programming', 'reading', 'hiking']} + superman := Man{'Superman', 30, ['flying','fighting evil','being nice']} + + results := my_variadic_function(superman, man) + + assert results.contains('Man {') + // + assert results.contains('name: Superman') + assert results.contains('age: 30') + assert results.contains('}, Man {') + // + assert results.contains('interests: ["programming"') + assert results.contains('name: Me') + // + assert results.contains('}]') + // + println(results) +}