From 35a83464d0793084535580a0427a5b0c79688301 Mon Sep 17 00:00:00 2001 From: Enzo Date: Sun, 4 Oct 2020 19:50:23 +0200 Subject: [PATCH] gen: separate sumtype from classic match (#6547) --- vlib/v/gen/cgen.v | 239 +++++++++++++++++++++----------------- vlib/v/table/table.v | 19 +-- vlib/v/tests/match_test.v | 26 ++++- 3 files changed, 162 insertions(+), 122 deletions(-) diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 34b7f918d8..108ae6bda5 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2671,17 +2671,6 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) { g.inside_ternary++ // g.write('/* EM ret type=${g.typ(node.return_type)} expected_type=${g.typ(node.expected_type)} */') } - type_sym := g.table.get_type_symbol(node.cond_type) - if node.is_sum_type { - g.match_sumtype_exprs << node.cond - g.match_sumtype_syms << type_sym - } - defer { - if node.is_sum_type { - g.match_sumtype_exprs.pop() - g.match_sumtype_syms.pop() - } - } cur_line := if is_expr { g.empty_line = true g.go_before_stmt(0) @@ -2693,24 +2682,38 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) { g.expr(node.cond) g.writeln(';') g.write(cur_line) - // TODO refactor, there are far too many indents + if node.is_sum_type { + g.match_expr_sumtype(node, is_expr, cond_var) + } else { + g.match_expr_classic(node, is_expr, cond_var) + } + if is_expr { + g.decrement_inside_ternary() + } +} + +fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var string) { + type_sym := g.table.get_type_symbol(node.cond_type) + g.match_sumtype_exprs << node.cond + g.match_sumtype_syms << type_sym + defer { + g.match_sumtype_exprs.pop() + g.match_sumtype_syms.pop() + } for j, branch in node.branches { mut sumtype_index := 0 // iterates through all types in sumtype branches - // it loops only once for other types for { is_last := j == node.branches.len - 1 if branch.is_else || (node.is_expr && is_last) { - if node.branches.len > 1 { - if is_expr { - // TODO too many branches. maybe separate ?: matches - g.write(' : ') - } else { - g.writeln(' else {') - } + if is_expr { + // TODO too many branches. maybe separate ?: matches + g.write(' : ') + } else { + g.writeln(' else {') } } else { - if j > 0 { + if j > 0 || sumtype_index > 0 { if is_expr { g.write(' : ') } else { @@ -2722,63 +2725,18 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) { } else { g.write('if (') } - if node.is_sum_type && branch.exprs.len > 0 { - // the multiple expressions of sumtypes are treated by the immediate `for` loop - // TODO move sumtype match to there own function - g.write(cond_var) - sym := g.table.get_type_symbol(node.cond_type) - // branch_sym := g.table.get_type_symbol(branch.typ) - if sym.kind == .sum_type { - dot_or_ptr := if node.cond_type.is_ptr() { '->' } else { '.' } - g.write(dot_or_ptr) - g.write('typ == ') - } else if sym.kind == .interface_ { - // g.write('._interface_idx == _${sym.name}_${branch_sym} ') - g.write('._interface_idx == ') - } - g.expr(branch.exprs[sumtype_index]) - } else { - for i, expr in branch.exprs { - if i > 0 { - g.write(' || ') - } - if type_sym.kind == .string { - if (expr as ast.StringLiteral).val == '' { - g.write('${cond_var}.len == 0') - } else { - g.write('string_eq(') - g.write(cond_var) - g.write(', ') - g.expr(expr) - g.write(')') - } - } else if expr is ast.RangeExpr { - // if type is unsigned and low is 0, check is unneeded - mut skip_low := false - if expr.low is ast.IntegerLiteral as expr_low { - if node.cond_type in [table.u16_type, table.u32_type, table.u64_type] && - expr_low.val == '0' { - skip_low = true - } - } - g.write('(') - if !skip_low { - g.write(cond_var) - g.write(' >= ') - g.expr(expr.low) - g.write(' && ') - } - g.write(cond_var) - g.write(' <= ') - g.expr(expr.high) - g.write(')') - } else { - g.write(cond_var) - g.write(' == ') - g.expr(expr) - } - } + g.write(cond_var) + sym := g.table.get_type_symbol(node.cond_type) + // branch_sym := g.table.get_type_symbol(branch.typ) + if sym.kind == .sum_type { + dot_or_ptr := if node.cond_type.is_ptr() { '->' } else { '.' } + g.write(dot_or_ptr) + g.write('typ == ') + } else if sym.kind == .interface_ { + // g.write('._interface_idx == _${sym.name}_${branch_sym} ') + g.write('._interface_idx == ') } + g.expr(branch.exprs[sumtype_index]) if is_expr { g.write(') ? ') } else { @@ -2786,39 +2744,112 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) { } } // g.writeln('/* M sum_type=$node.is_sum_type is_expr=$node.is_expr exp_type=${g.typ(node.expected_type)}*/') - if node.is_sum_type && branch.exprs.len > 0 { - if !node.is_expr { - // Use the nodes in the expr to generate `it` variable. - type_expr := branch.exprs[sumtype_index] - if type_expr is ast.Type { - it_type := g.typ(type_expr.typ) - // g.writeln('$it_type* it = ($it_type*)${tmp}.obj; // ST it') - g.write('\t$it_type* it = ($it_type*)') - g.write(cond_var) - dot_or_ptr := if node.cond_type.is_ptr() { '->' } else { '.' } - g.write(dot_or_ptr) - g.writeln('_object; // ST it') - if node.var_name.len > 0 { - // for now we just copy it - g.writeln('\t$it_type* $node.var_name = it;') - } - } else { - verror('match sum type') - } + if !branch.is_else && !node.is_expr { + // Use the nodes in the expr to generate `it` variable. + type_expr := branch.exprs[sumtype_index] + if type_expr !is ast.Type { + verror('match sum type') + } + it_type := g.typ((type_expr as ast.Type).typ) + // g.writeln('$it_type* it = ($it_type*)${tmp}.obj; // ST it') + g.write('\t$it_type* it = ($it_type*)') + g.write(cond_var) + dot_or_ptr := if node.cond_type.is_ptr() { '->' } else { '.' } + g.write(dot_or_ptr) + g.writeln('_object; // ST it') + if node.var_name.len > 0 { + // for now we just copy it + g.writeln('\t$it_type* $node.var_name = it;') } - sumtype_index++ } g.stmts(branch.stmts) - if g.inside_ternary == 0 && node.branches.len > 1 { + if g.inside_ternary == 0 { g.write('}') } - if !node.is_sum_type || branch.exprs.len == 0 || sumtype_index == branch.exprs.len { + sumtype_index++ + if branch.exprs.len == 0 || sumtype_index == branch.exprs.len { break } } } - if is_expr { - g.decrement_inside_ternary() +} + +fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var string) { + type_sym := g.table.get_type_symbol(node.cond_type) + for j, branch in node.branches { + is_last := j == node.branches.len - 1 + if branch.is_else || (node.is_expr && is_last) { + if node.branches.len > 1 { + if is_expr { + // TODO too many branches. maybe separate ?: matches + g.write(' : ') + } else { + g.writeln(' else {') + } + } + } else { + if j > 0 { + if is_expr { + g.write(' : ') + } else { + g.write(' else ') + } + } + if is_expr { + g.write('(') + } else { + g.write('if (') + } + for i, expr in branch.exprs { + if i > 0 { + g.write(' || ') + } + if type_sym.kind == .string { + if (expr as ast.StringLiteral).val == '' { + g.write('${cond_var}.len == 0') + } else { + g.write('string_eq(') + g.write(cond_var) + g.write(', ') + g.expr(expr) + g.write(')') + } + } else if expr is ast.RangeExpr { + // if type is unsigned and low is 0, check is unneeded + mut skip_low := false + if expr.low is ast.IntegerLiteral as expr_low { + if node.cond_type in [table.u16_type, table.u32_type, table.u64_type] && + expr_low.val == '0' { + skip_low = true + } + } + g.write('(') + if !skip_low { + g.write(cond_var) + g.write(' >= ') + g.expr(expr.low) + g.write(' && ') + } + g.write(cond_var) + g.write(' <= ') + g.expr(expr.high) + g.write(')') + } else { + g.write(cond_var) + g.write(' == ') + g.expr(expr) + } + } + if is_expr { + g.write(') ? ') + } else { + g.writeln(') {') + } + } + g.stmts(branch.stmts) + if g.inside_ternary == 0 && node.branches.len > 1 { + g.write('}') + } } } @@ -3008,11 +3039,7 @@ fn (mut g Gen) ident(node ast.Ident) { fn (mut g Gen) should_write_asterisk_due_to_match_sumtype(expr ast.Expr) bool { if expr is ast.Ident { typ := if expr.info is ast.IdentVar { (expr.info as ast.IdentVar).typ } else { (expr.info as ast.IdentFn).typ } - return if typ.is_ptr() && g.match_sumtype_has_no_struct_and_contains(expr) { - true - } else { - false - } + return typ.is_ptr() && g.match_sumtype_has_no_struct_and_contains(expr) } else { return false } diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index de1988a6c5..b183895fc5 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -40,15 +40,9 @@ pub mut: } fn (f &Fn) method_equals(o &Fn) bool { - return f.params[1..].equals(o.params[1..]) && - f.return_type == o.return_type && - f.return_type_source_name == o.return_type_source_name && - f.is_variadic == o.is_variadic && - f.language == o.language && - f.is_generic == o.is_generic && - f.is_pub == o.is_pub && - f.mod == o.mod && - f.name == o.name + return f.params[1..].equals(o.params[1..]) && f.return_type == o.return_type && f.return_type_source_name == + o.return_type_source_name && f.is_variadic == o.is_variadic && f.language == o.language && + f.is_generic == o.is_generic && f.is_pub == o.is_pub && f.mod == o.mod && f.name == o.name } pub struct Param { @@ -62,11 +56,8 @@ pub: } fn (p &Param) equals(o &Param) bool { - return p.name == o.name - && p.is_mut == o.is_mut - && p.typ == o.typ - && p.type_source_name == o.type_source_name - && p.is_hidden == o.is_hidden + return p.name == o.name && p.is_mut == o.is_mut && p.typ == o.typ && p.type_source_name == + o.type_source_name && p.is_hidden == o.is_hidden } fn (p []Param) equals(o []Param) bool { diff --git a/vlib/v/tests/match_test.v b/vlib/v/tests/match_test.v index 99630fb516..22d4ba12ab 100644 --- a/vlib/v/tests/match_test.v +++ b/vlib/v/tests/match_test.v @@ -165,6 +165,20 @@ fn test_sum_type_name() { assert f(a) == 'A1' } +fn f_else(s Sum) string { + match s { + A1 { return typeof(s) } + else { return '' } + } +} + +fn test_sum_type_else() { + a := A1{ + pos: 22 + } + assert f_else(a) == 'A1' +} + struct Alfa { char rune = `a` } @@ -183,11 +197,13 @@ fn (b Bravo) letter() rune { return b.char } -struct Charlie {} +struct Charlie { + char rune = `c` +} type NATOAlphabet = Alfa | Bravo | Charlie -fn test_match_sum_type_multiple_type() { +fn test_match_sumtype_multiple_types() { a := Alfa{} match NATOAlphabet(a) as l { Alfa, Bravo { @@ -199,4 +215,10 @@ fn test_match_sum_type_multiple_type() { assert false } } + // test one branch + match NATOAlphabet(a) as l { + Alfa, Bravo, Charlie { + assert l.char == `a` + } + } }