From 35a0fe79f99c2259c2a143019b5d4714df8e98ce Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Wed, 25 Nov 2020 13:41:05 +0200 Subject: [PATCH] cgen: generate an unique sort comparator function for each .sort() call --- vlib/v/gen/cgen.v | 74 +++++++++---------- .../sorting_by_different_criteria_test.v | 23 ++++++ 2 files changed, 59 insertions(+), 38 deletions(-) create mode 100644 vlib/v/tests/sorting_by_different_criteria_test.v diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 6494d3c717..79e180c695 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -4879,50 +4879,48 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) { verror('usage: .sort(a.field < b.field)') } // verror('sort(): unhandled type $typ $q.name') - compare_fn = 'compare_' + g.typ(typ) + tmp_name := g.new_tmp_var() + compare_fn = 'compare_${tmp_name}_' + 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_)' - } + // 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_once('.', + '->') + right_expr_str := g.write_expr_to_string(infix_expr.right).replace_once('.', + '->') + 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 { - if is_reverse { - op1 = 'a_ > b_' - op2 = 'a_ < b_' - } else { - op1 = 'a_ < b_' - op2 = 'a_ > b_' - } + 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') } + g.definitions.writeln('if ($op1) return -1;') + g.definitions.writeln('if ($op2) return 1; return 0; }\n') } } if is_reverse && !compare_fn.ends_with('_reverse') { diff --git a/vlib/v/tests/sorting_by_different_criteria_test.v b/vlib/v/tests/sorting_by_different_criteria_test.v new file mode 100644 index 0000000000..22dc6b1617 --- /dev/null +++ b/vlib/v/tests/sorting_by_different_criteria_test.v @@ -0,0 +1,23 @@ +struct Child { + f f64 +} + +struct Parent { + child Child + name string +} + +fn test_sorting_by_different_criteria_in_same_function() { + mut arr := [ + Parent{Child{0.2}, 'def'}, + Parent{Child{0.1}, 'xyz'}, + Parent{Child{0.3}, 'abc'}, + ] + assert arr[0].name == 'def' + arr.sort(a.name < b.name) + // println(arr) + assert arr[0].name == 'abc' + // println(arr) + arr.sort(a.child.f < b.child.f) + assert arr[0].name == 'xyz' +}