cgen: implement sum type str gen (#6454)

pull/6464/head
Daniel Däschle 2020-09-23 20:51:51 +02:00 committed by GitHub
parent b0a2c28c19
commit 4f09ddccb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 113 additions and 5 deletions

View File

@ -315,7 +315,7 @@ pub fn (c &Checker) get_default_fmt(ftyp, typ table.Type) byte {
}
}
if ftyp in [table.string_type, table.bool_type] ||
sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return] || ftyp.has_flag(.optional) ||
sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type] || ftyp.has_flag(.optional) ||
sym.has_method('str') {
return `s`
} else {

View File

@ -53,7 +53,7 @@ fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string {
table.Struct { g.gen_str_for_struct(it, styp, str_fn_name) }
table.Map { g.gen_str_for_map(it, styp, str_fn_name) }
table.MultiReturn { g.gen_str_for_multi_return(it, styp, str_fn_name) }
table.SumType {}
table.SumType { g.gen_str_for_sum_type(it, styp, str_fn_name) }
else { verror("could not generate string method $str_fn_name for type \'$styp\'") }
}
}
@ -298,8 +298,7 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) {
}
// _str() functions should have a single argument, the indenting ones take 2:
g.type_definitions.writeln('string ${str_fn_name}($styp x); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x,0);}')
//
g.auto_str_funcs.writeln('string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x, 0);}')
g.type_definitions.writeln('string indent_${str_fn_name}($styp x, int indent_count); // auto')
g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp x, int indent_count) {')
mut clean_struct_v_type_name := styp.replace('__', '.')
@ -346,6 +345,8 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) {
} else if sym.kind in [.array, .array_fixed, .map] {
g.auto_str_funcs.write('indents, ')
g.auto_str_funcs.write('${field_styp_fn_name}( it->${c_name(field.name)}) ')
} else if sym.kind == .sum_type {
g.auto_str_funcs.write('indents, indent_${field_styp_fn_name}(it->${c_name(field.name)}, indent_count + 1)')
} else {
g.auto_str_funcs.write('indents, it->${c_name(field.name)}')
if field.typ == table.bool_type {
@ -381,3 +382,50 @@ fn (mut g Gen) gen_str_for_enum(info table.Enum, styp, str_fn_name string) {
g.auto_str_funcs.writeln('\t}')
g.auto_str_funcs.writeln('}')
}
fn (mut g Gen) gen_str_for_sum_type(info table.SumType, styp, str_fn_name string) {
mut gen_fn_names := map[string]string
for typ in info.variants {
sym := g.table.get_type_symbol(typ)
if !sym.has_method('str') {
field_styp := g.typ(typ)
field_fn_name := g.gen_str_for_type_with_styp(typ, field_styp)
gen_fn_names[field_styp] = field_fn_name
}
}
// _str() functions should have a single argument, the indenting ones take 2:
g.type_definitions.writeln('string ${str_fn_name}($styp x); // auto')
g.auto_str_funcs.writeln('string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x, 0); }')
g.type_definitions.writeln('string indent_${str_fn_name}($styp x, int indent_count); // auto')
g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp x, int indent_count) {')
mut clean_sum_type_v_type_name := styp.replace('__', '.')
if styp.ends_with('*') {
clean_sum_type_v_type_name = '&' + clean_sum_type_v_type_name.replace('*', '')
}
clean_sum_type_v_type_name = util.strip_main_name(clean_sum_type_v_type_name)
g.auto_str_funcs.writeln('\tswitch(x.typ) {')
for typ in info.variants {
mut value_fmt := '%.*s\\000'
if typ == table.string_type {
value_fmt = '\'$value_fmt\''
}
typ_str := g.typ(typ)
mut func_name := if typ_str in gen_fn_names {
gen_fn_names[typ_str]
} else {
g.gen_str_for_type_with_styp(typ, typ_str)
}
sym := g.table.get_type_symbol(typ)
if sym.kind == .struct_ {
func_name = 'indent_$func_name'
}
g.auto_str_funcs.write('\t\tcase $typ: return _STR("${clean_sum_type_v_type_name}($value_fmt)", 2, ${func_name}(*($typ_str*)x._object')
if sym.kind == .struct_ {
g.auto_str_funcs.write(', indent_count')
}
g.auto_str_funcs.writeln('));')
}
g.auto_str_funcs.writeln('\t\tdefault: return tos_lit("unknown sum type value");')
g.auto_str_funcs.writeln('\t}')
g.auto_str_funcs.writeln('}')
}

View File

@ -4159,7 +4159,8 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool {
g.enum_expr(expr)
g.write('")')
}
} else if sym_has_str_method || sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return] {
} else if sym_has_str_method || sym.kind in
[.array, .array_fixed, .map, .struct_, .multi_return, .sum_type] {
is_p := etype.is_ptr()
val_type := if is_p { etype.deref() } else { etype }
str_fn_name := g.gen_str_for_type(val_type)
@ -5073,6 +5074,8 @@ fn (g &Gen) type_to_fmt(typ table.Type) string {
return '%g\\000' // g removes trailing zeros unlike %f
} else if typ == table.u64_type {
return '%lld\\000'
} else if sym.kind == .sum_type {
return '%.*s\\000'
}
return '%d\\000'
}

View File

@ -0,0 +1,57 @@
struct Abc {
foo int
bar bool
str string
}
type ST = int | string | bool | Abc
fn test_int_st_str() {
a := ST(0)
assert '$a' == 'ST(0)'
}
fn test_string_st_str() {
a := ST('test')
assert '$a' == 'ST(\'test\')'
}
fn test_struct_st_str() {
a := ST(Abc{})
assert '$a' == 'ST(Abc {\n foo: 0\n bar: false\n str: \'\'\n})'
}
fn test_bool_st_str() {
a := ST(false)
assert '$a' == 'ST(false)'
}
fn test_str() {
a := ST(false)
assert a.str() == 'ST(false)'
}
struct Container {
st ST
}
fn test_in_struct() {
c := Container{ST(0)}
assert '$c' == 'Container {\n st: ST(0)\n}'
}
fn test_unknown_value() {
c := Container{}
assert '$c' == 'Container {\n st: unknown sum type value\n}'
}
fn test_nested_in_struct() {
abc := Abc{}
c := Container{ST(abc)}
assert '$c' == 'Container {\n st: ST(Abc {\n foo: 0\n bar: false\n str: \'\'\n })\n}'
}
fn test_pointer() {
st := ST(0)
assert '${&st}' == '&ST(0)'
}