compiler: fix generation of default .str() methods in interpolation
parent
09d9dd2607
commit
d2ab9d3e77
|
@ -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) {
|
fn (p mut Parser) gen_array_str(typ Type) {
|
||||||
if typ.has_method('str') {
|
if typ.has_method('str') {
|
||||||
return
|
return
|
||||||
|
@ -379,7 +414,10 @@ fn (p mut Parser) gen_array_str(typ Type) {
|
||||||
p.gen_array_str(elm_type2)
|
p.gen_array_str(elm_type2)
|
||||||
}
|
}
|
||||||
else if p.typ_to_fmt(elm_type, 0) == '' && !p.table.type_has_method(elm_type2, 'str') {
|
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('
|
p.v.vgen_buf.writeln('
|
||||||
pub fn (a $typ.name) str() string {
|
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();'
|
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) {
|
fn (p mut Parser) gen_struct_str(typ Type) {
|
||||||
p.add_method(typ.name, Fn{
|
p.add_method(typ.name, Fn{
|
||||||
name: 'str'
|
name: 'str'
|
||||||
|
@ -413,7 +451,8 @@ fn (p mut Parser) gen_struct_str(typ Type) {
|
||||||
})
|
})
|
||||||
mut sb := strings.new_builder(typ.fields.len * 20)
|
mut sb := strings.new_builder(typ.fields.len * 20)
|
||||||
sb.writeln('pub fn (a $typ.name) str() string {\nreturn')
|
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 {
|
for field in typ.fields {
|
||||||
sb.writeln('\t$field.name: $' + 'a.${field.name}')
|
sb.writeln('\t$field.name: $' + 'a.${field.name}')
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,15 +114,10 @@ fn (p mut Parser) string_expr() {
|
||||||
else {
|
else {
|
||||||
f := p.typ_to_fmt(typ, 0)
|
f := p.typ_to_fmt(typ, 0)
|
||||||
if f == '' {
|
if f == '' {
|
||||||
is_array := typ.starts_with('array_')
|
has_str_method, styp := p.gen_default_str_method_if_missing( typ )
|
||||||
typ2 := p.table.find_type(typ)
|
if has_str_method {
|
||||||
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)
|
|
||||||
}
|
|
||||||
tmp_var := p.get_tmp()
|
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'
|
args = args.all_before_last(val) + '${tmp_var}.len, ${tmp_var}.str'
|
||||||
format += '%.*s '
|
format += '%.*s '
|
||||||
}
|
}
|
||||||
|
@ -165,4 +160,3 @@ fn (p mut Parser) string_expr() {
|
||||||
p.gen('_STR($format$args)')
|
p.gen('_STR($format$args)')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
MyStruct {
|
||||||
s: 6
|
s: 6
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
Loading…
Reference in New Issue