From f8be2110ecb2918af76d7f8c9e49d0d809729e26 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 12 Aug 2020 18:43:41 +0200 Subject: [PATCH] sort: fix sorting by struct fields --- vlib/builtin/array_test.v | 3 ++ vlib/cli/flag.v | 7 ++++ vlib/v/gen/cgen.v | 74 +++++++++++++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/vlib/builtin/array_test.v b/vlib/builtin/array_test.v index 6e57e097e3..f3706223cb 100644 --- a/vlib/builtin/array_test.v +++ b/vlib/builtin/array_test.v @@ -736,6 +736,9 @@ fn test_sort() { assert(users[0].age == 25) assert(users[1].age == 22) assert(users[2].age == 20) + // + users.sort(a.name < b.name) // Test sorting by string fields + //assert users.map(it.name).join(' ') == 'Alice Bob Peter' } fn test_f32_sort() { diff --git a/vlib/cli/flag.v b/vlib/cli/flag.v index 4c79227e9a..2171a32dda 100644 --- a/vlib/cli/flag.v +++ b/vlib/cli/flag.v @@ -183,3 +183,10 @@ fn (flags []Flag) contains(name string) bool { return false } + +fn (mut flags []Flag) sort2() { + flags.sort_with_compare(fn (a, b &Flag) int { + return compare_strings(&a.name, &b.name) + }) +} + diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 4173a40c9f..142070884d 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -24,10 +24,10 @@ const ( ) struct Gen { - table &table.Table pref &pref.Preferences module_built string mut: + table &table.Table out strings.Builder cheaders strings.Builder includes strings.Builder // all C #includes required by V modules @@ -3739,14 +3739,14 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) { // `users.sort(a.age < b.age)` fn (mut g Gen) gen_array_sort(node ast.CallExpr) { // println('filter s="$s"') - sym := g.table.get_type_symbol(node.return_type) - if sym.kind != .array { + return_sym := g.table.get_type_symbol(node.return_type) + if return_sym.kind != .array { println(node.name) println(g.typ(node.receiver_type)) - println(sym.kind) + // println(sym.kind) verror('sort() requires an array') } - info := sym.info as table.Array + info := return_sym.info as table.Array // No arguments means we are sorting an array of builtins (e.g. `numbers.sort()`) // The type for the comparison fns is the type of the element itself. mut typ := info.elem_type @@ -3756,29 +3756,73 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) { // Get the type of the field that's being compared // `a.age > b.age` => `age int` => int infix_expr := node.args[0].expr as ast.InfixExpr - typ = infix_expr.left_type + // typ = infix_expr.left_type is_reverse = infix_expr.op == .gt } - mut compare_fn := match typ { + mut compare_fn := '' + match typ { table.int_type { - 'compare_ints' + compare_fn = 'compare_ints' } table.string_type { - 'compare_strings' + compare_fn = 'compare_strings' } table.f64_type { - 'compare_floats' + compare_fn = 'compare_floats' } else { - q := g.table.get_type_symbol(typ) + // Generate a comparison function for a custom type if node.args.len == 0 { - verror('usage: .sort(a.field < b.field) $q.name') + verror('usage: .sort(a.field < b.field)') + } + // verror('sort(): unhandled type $typ $q.name') + compare_fn = 'compare_' + g.typ(typ) + if is_reverse { + compare_fn += '_reverse' + } + if _ := g.table.find_fn(compare_fn) { + } else { + // Register a new custom `compare_xxx` function for qsort() + g.table.register_fn({ + name: compare_fn + return_type: table.int_type + }) + infix_expr := node.args[0].expr as ast.InfixExpr + styp := g.typ(typ) + // Variables `a` and `b` are used in the `.sort(a < b)` syntax, so we can reuse them + // when generating the function as long as the args are named the same. + g.definitions.writeln('int $compare_fn ($styp* a, $styp* b) {') + field_type := g.typ(infix_expr.left_type) + left_expr_str := g.write_expr_to_string(infix_expr.left).replace('.', + '->') + right_expr_str := g.write_expr_to_string(infix_expr.right).replace('.', + '->') + g.definitions.writeln('$field_type a_ = $left_expr_str;') + g.definitions.writeln('$field_type b_ = $right_expr_str;') + mut op1, mut op2 := '', '' + if infix_expr.left_type == table.string_type { + if is_reverse { + op1 = 'string_gt(a_, b_)' + op2 = 'string_lt(a_, b_)' + } else { + op1 = 'string_lt(a_, b_)' + op2 = 'string_gt(a_, b_)' + } + } else { + if is_reverse { + op1 = 'a_ > b_' + op2 = 'a_ < b_' + } else { + op1 = 'a_ < b_' + op2 = 'a_ > b_' + } + } + g.definitions.writeln('if ($op1) return -1;') + g.definitions.writeln('if ($op2) return 1; return 0; }\n') } - verror('sort(): unhandled type $typ $q.name') - '' } } - if is_reverse { + if is_reverse && !compare_fn.ends_with('_reverse') { compare_fn += '_reverse' } //