cgen: add auto str method generation for interfaces (#9095)
							parent
							
								
									5f9af3f594
								
							
						
					
					
						commit
						7663f826e5
					
				| 
						 | 
					@ -295,7 +295,7 @@ pub fn (c &Checker) get_default_fmt(ftyp table.Type, typ table.Type) byte {
 | 
				
			||||||
			return `s`
 | 
								return `s`
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if ftyp in [table.string_type, table.bool_type]
 | 
							if ftyp in [table.string_type, table.bool_type]
 | 
				
			||||||
			|| sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type, .none_]
 | 
								|| sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type, .interface_, .none_]
 | 
				
			||||||
			|| ftyp.has_flag(.optional) || sym.has_method('str') {
 | 
								|| ftyp.has_flag(.optional) || sym.has_method('str') {
 | 
				
			||||||
			return `s`
 | 
								return `s`
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,10 @@ module c
 | 
				
			||||||
import v.table
 | 
					import v.table
 | 
				
			||||||
import v.util
 | 
					import v.util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn should_use_indent_func(kind table.Kind) bool {
 | 
				
			||||||
 | 
						return kind in [.struct_, .alias, .array, .array_fixed, .map, .sum_type, .interface_]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp string, str_fn_name 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_ := ''
 | 
				
			||||||
| 
						 | 
					@ -144,7 +148,7 @@ fn (mut g Gen) gen_str_for_option(typ table.Type, styp string, str_fn_name strin
 | 
				
			||||||
	g.auto_str_funcs.writeln('\t} else if (it.state == 0) {')
 | 
						g.auto_str_funcs.writeln('\t} else if (it.state == 0) {')
 | 
				
			||||||
	if sym.kind == .string {
 | 
						if sym.kind == .string {
 | 
				
			||||||
		g.auto_str_funcs.writeln('\t\tres = _STR("\'%.*s\\000\'", 2, ${parent_str_fn_name}(*($sym.cname*)it.data));')
 | 
							g.auto_str_funcs.writeln('\t\tres = _STR("\'%.*s\\000\'", 2, ${parent_str_fn_name}(*($sym.cname*)it.data));')
 | 
				
			||||||
	} else if sym.kind == .struct_ && !sym_has_str_method {
 | 
						} else if should_use_indent_func(sym.kind) && !sym_has_str_method {
 | 
				
			||||||
		g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(*($sym.cname*)it.data, indent_count);')
 | 
							g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(*($sym.cname*)it.data, indent_count);')
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(*($sym.cname*)it.data);')
 | 
							g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(*($sym.cname*)it.data);')
 | 
				
			||||||
| 
						 | 
					@ -218,7 +222,7 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name stri
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			g.auto_str_funcs.writeln('\t\t$field_styp it = *($field_styp*)array_get(a, i);')
 | 
								g.auto_str_funcs.writeln('\t\t$field_styp it = *($field_styp*)array_get(a, i);')
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if sym.kind == .struct_ && !sym_has_str_method {
 | 
							if should_use_indent_func(sym.kind) && !sym_has_str_method {
 | 
				
			||||||
			if is_elem_ptr {
 | 
								if is_elem_ptr {
 | 
				
			||||||
				g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(*it, indent_count);')
 | 
									g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(*it, indent_count);')
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
| 
						 | 
					@ -292,7 +296,7 @@ fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp string, str_f
 | 
				
			||||||
		g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();')
 | 
							g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();')
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		g.auto_str_funcs.writeln('\tfor (int i = 0; i < $info.size; ++i) {')
 | 
							g.auto_str_funcs.writeln('\tfor (int i = 0; i < $info.size; ++i) {')
 | 
				
			||||||
		if sym.kind == .struct_ && !sym_has_str_method {
 | 
							if should_use_indent_func(sym.kind) && !sym_has_str_method {
 | 
				
			||||||
			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(a[i]));')
 | 
								g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(a[i]));')
 | 
				
			||||||
		} else if sym.kind in [.f32, .f64] {
 | 
							} else if sym.kind in [.f32, .f64] {
 | 
				
			||||||
			g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("%g", 1, a[i]));')
 | 
								g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("%g", 1, a[i]));')
 | 
				
			||||||
| 
						 | 
					@ -372,7 +376,7 @@ fn (mut g Gen) gen_str_for_map(info table.Map, styp string, str_fn_name string)
 | 
				
			||||||
		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}());')
 | 
							g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}());')
 | 
				
			||||||
	} else if val_sym.kind == .string {
 | 
						} else if val_sym.kind == .string {
 | 
				
			||||||
		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("\'%.*s\\000\'", 2, *($val_styp*)DenseArray_value(&m.key_values, i)));')
 | 
							g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("\'%.*s\\000\'", 2, *($val_styp*)DenseArray_value(&m.key_values, i)));')
 | 
				
			||||||
	} else if val_sym.kind == .struct_ && !val_sym.has_method('str') {
 | 
						} else if should_use_indent_func(val_sym.kind) && !val_sym.has_method('str') {
 | 
				
			||||||
		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, indent_${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i), indent_count));')
 | 
							g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, indent_${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i), indent_count));')
 | 
				
			||||||
	} else if val_sym.kind in [.f32, .f64] {
 | 
						} else if val_sym.kind in [.f32, .f64] {
 | 
				
			||||||
		g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("%g", 1, *($val_styp*)DenseArray_value(&m.key_values, i)));')
 | 
							g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _STR("%g", 1, *($val_styp*)DenseArray_value(&m.key_values, i)));')
 | 
				
			||||||
| 
						 | 
					@ -418,7 +422,7 @@ fn (mut g Gen) gen_str_for_multi_return(info table.MultiReturn, styp string, str
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			arg_str_fn_name = styp_to_str_fn_name(field_styp)
 | 
								arg_str_fn_name = styp_to_str_fn_name(field_styp)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if sym.kind == .struct_ && !sym_has_str_method {
 | 
							if should_use_indent_func(sym.kind) && !sym_has_str_method {
 | 
				
			||||||
			g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}(a.arg$i));')
 | 
								g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}(a.arg$i));')
 | 
				
			||||||
		} else if sym.kind in [.f32, .f64] {
 | 
							} else if sym.kind in [.f32, .f64] {
 | 
				
			||||||
			g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _STR("%g", 1, a.arg$i));')
 | 
								g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _STR("%g", 1, a.arg$i));')
 | 
				
			||||||
| 
						 | 
					@ -535,9 +539,9 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp string, str_fn_name st
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn struct_auto_str_func(sym table.TypeSymbol, field_type table.Type, fn_name string, field_name string) string {
 | 
					fn struct_auto_str_func(sym table.TypeSymbol, field_type table.Type, fn_name string, field_name string) string {
 | 
				
			||||||
	has_custom_str, expects_ptr, _ := sym.str_method_info()
 | 
						has_custom_str, expects_ptr, _ := sym.str_method_info()
 | 
				
			||||||
	if sym.kind in [.enum_, .interface_] {
 | 
						if sym.kind == .enum_ {
 | 
				
			||||||
		return '${fn_name}(it.${c_name(field_name)})'
 | 
							return '${fn_name}(it.${c_name(field_name)})'
 | 
				
			||||||
	} else if sym.kind == .struct_ {
 | 
						} else if should_use_indent_func(sym.kind) {
 | 
				
			||||||
		mut obj := 'it.${c_name(field_name)}'
 | 
							mut obj := 'it.${c_name(field_name)}'
 | 
				
			||||||
		if field_type.is_ptr() && !expects_ptr {
 | 
							if field_type.is_ptr() && !expects_ptr {
 | 
				
			||||||
			obj = '*$obj'
 | 
								obj = '*$obj'
 | 
				
			||||||
| 
						 | 
					@ -606,10 +610,51 @@ fn (mut g Gen) gen_str_for_enum(info table.Enum, styp string, str_fn_name string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn (mut g Gen) gen_str_for_interface(info table.Interface, styp string, str_fn_name string) {
 | 
					fn (mut g Gen) gen_str_for_interface(info table.Interface, styp string, str_fn_name string) {
 | 
				
			||||||
	// TODO
 | 
						mut gen_fn_names := map[string]string{}
 | 
				
			||||||
	g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
 | 
						for typ in info.types {
 | 
				
			||||||
	g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { /* gen_str_for_interface */')
 | 
							sym := g.table.get_type_symbol(typ)
 | 
				
			||||||
	g.auto_str_funcs.writeln('\treturn _SLIT("$styp{ /* TODO: Interface str */ }");')
 | 
							if !sym.has_method('str') {
 | 
				
			||||||
 | 
								field_styp := g.typ(typ)
 | 
				
			||||||
 | 
								field_fn_name := g.gen_str_for_type(typ)
 | 
				
			||||||
 | 
								gen_fn_names[field_styp] = field_fn_name
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mut clean_interface_v_type_name := styp.replace('__', '.')
 | 
				
			||||||
 | 
						if styp.ends_with('*') {
 | 
				
			||||||
 | 
							clean_interface_v_type_name = '&' + clean_interface_v_type_name.replace('*', '')
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						clean_interface_v_type_name = util.strip_main_name(clean_interface_v_type_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						g.type_definitions.writeln('static string ${str_fn_name}($styp x); // auto')
 | 
				
			||||||
 | 
						g.auto_str_funcs.writeln('static string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x, 0); }')
 | 
				
			||||||
 | 
						g.type_definitions.writeln('static string indent_${str_fn_name}($styp x, int indent_count); // auto')
 | 
				
			||||||
 | 
						g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp x, int indent_count) { /* gen_str_for_interface */')
 | 
				
			||||||
 | 
						for typ in info.types {
 | 
				
			||||||
 | 
							typ_str := g.typ(typ)
 | 
				
			||||||
 | 
							subtype := g.table.get_type_symbol(typ)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mut func_name := if typ_str in gen_fn_names {
 | 
				
			||||||
 | 
								gen_fn_names[typ_str]
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								g.gen_str_for_type(typ)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							sym_has_str_method, str_method_expects_ptr, _ := subtype.str_method_info()
 | 
				
			||||||
 | 
							if should_use_indent_func(subtype.kind) && !sym_has_str_method {
 | 
				
			||||||
 | 
								func_name = 'indent_$func_name'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' }
 | 
				
			||||||
 | 
							value_fmt := if typ == table.string_type { "'%.*s\\000'" } else { '%.*s\\000' }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							g.auto_str_funcs.write_string('\tif (x._interface_idx == _${styp}_${subtype.cname}_index)')
 | 
				
			||||||
 | 
							g.auto_str_funcs.write_string(' return _STR("${clean_interface_v_type_name}($value_fmt)", 2, ')
 | 
				
			||||||
 | 
							g.auto_str_funcs.write_string('${func_name}(${deref}($subtype.cname*)x._object')
 | 
				
			||||||
 | 
							if should_use_indent_func(subtype.kind) && !sym_has_str_method {
 | 
				
			||||||
 | 
								g.auto_str_funcs.write_string(', indent_count')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							g.auto_str_funcs.writeln('));')
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						g.auto_str_funcs.writeln('\treturn _SLIT("unknown interface value");')
 | 
				
			||||||
	g.auto_str_funcs.writeln('}')
 | 
						g.auto_str_funcs.writeln('}')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -648,11 +693,11 @@ fn (mut g Gen) gen_str_for_union_sum_type(info table.SumType, styp string, str_f
 | 
				
			||||||
		sym := g.table.get_type_symbol(typ)
 | 
							sym := g.table.get_type_symbol(typ)
 | 
				
			||||||
		sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
 | 
							sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
 | 
				
			||||||
		deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' }
 | 
							deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' }
 | 
				
			||||||
		if sym.kind == .struct_ && !sym_has_str_method {
 | 
							if should_use_indent_func(sym.kind) && !sym_has_str_method {
 | 
				
			||||||
			func_name = 'indent_$func_name'
 | 
								func_name = 'indent_$func_name'
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		g.auto_str_funcs.write_string('\t\tcase $typ: return _STR("${clean_sum_type_v_type_name}($value_fmt)", 2, ${func_name}(${deref}($typ_str*)x._$sym.cname')
 | 
							g.auto_str_funcs.write_string('\t\tcase $typ: return _STR("${clean_sum_type_v_type_name}($value_fmt)", 2, ${func_name}(${deref}($typ_str*)x._$sym.cname')
 | 
				
			||||||
		if sym.kind == .struct_ && !sym_has_str_method {
 | 
							if should_use_indent_func(sym.kind) && !sym_has_str_method {
 | 
				
			||||||
			g.auto_str_funcs.write_string(', indent_count')
 | 
								g.auto_str_funcs.write_string(', indent_count')
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		g.auto_str_funcs.writeln('));')
 | 
							g.auto_str_funcs.writeln('));')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					struct Dog { breed string }
 | 
				
			||||||
 | 
					struct Cat { breed string }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface Animal {
 | 
				
			||||||
 | 
						breed string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn test_auto_str_gen_for_interfaces() {
 | 
				
			||||||
 | 
						x := Animal(Cat{'Siamese'})
 | 
				
			||||||
 | 
						assert '$x' == "
 | 
				
			||||||
 | 
					Animal(Cat{
 | 
				
			||||||
 | 
					    breed: 'Siamese'
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					".trim_space()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Holder {
 | 
				
			||||||
 | 
						x Animal
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Holder2 {
 | 
				
			||||||
 | 
						x map[string]Holder
 | 
				
			||||||
 | 
						breed string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn test_auto_str_gen_for_complex_interface_types() {
 | 
				
			||||||
 | 
						a := Animal(Dog{'hi'})
 | 
				
			||||||
 | 
						h := Holder{a}
 | 
				
			||||||
 | 
						m := map{'dsa': h}
 | 
				
			||||||
 | 
						h2 := Holder2{ m, 'N/A' }
 | 
				
			||||||
 | 
						a2 := Animal(h2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert '$a2' == r"
 | 
				
			||||||
 | 
					Animal(Holder2{
 | 
				
			||||||
 | 
					    x: {'dsa': Holder{
 | 
				
			||||||
 | 
					        x: Animal(Dog{
 | 
				
			||||||
 | 
					            breed: 'hi'
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }}
 | 
				
			||||||
 | 
					    breed: 'N/A'
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
						".trim_space()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue