1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

cgen: generate an unique sort comparator function for each .sort() call

This commit is contained in:
Delyan Angelov 2020-11-25 13:41:05 +02:00
parent a46eda7c44
commit 35a0fe79f9
2 changed files with 59 additions and 38 deletions

View File

@ -4879,50 +4879,48 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) {
verror('usage: .sort(a.field < b.field)') verror('usage: .sort(a.field < b.field)')
} }
// verror('sort(): unhandled type $typ $q.name') // 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 { if is_reverse {
compare_fn += '_reverse' compare_fn += '_reverse'
} }
if _ := g.table.find_fn(compare_fn) { // Register a new custom `compare_xxx` function for qsort()
} else { g.table.register_fn({
// Register a new custom `compare_xxx` function for qsort() name: compare_fn
g.table.register_fn({ return_type: table.int_type
name: compare_fn })
return_type: table.int_type infix_expr := node.args[0].expr as ast.InfixExpr
}) styp := g.typ(typ)
infix_expr := node.args[0].expr as ast.InfixExpr // Variables `a` and `b` are used in the `.sort(a < b)` syntax, so we can reuse them
styp := g.typ(typ) // when generating the function as long as the args are named the same.
// Variables `a` and `b` are used in the `.sort(a < b)` syntax, so we can reuse them g.definitions.writeln('int $compare_fn ($styp* a, $styp* b) {')
// when generating the function as long as the args are named the same. field_type := g.typ(infix_expr.left_type)
g.definitions.writeln('int $compare_fn ($styp* a, $styp* b) {') left_expr_str := g.write_expr_to_string(infix_expr.left).replace_once('.',
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_once('.',
'->') '->')
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;')
g.definitions.writeln('$field_type a_ = $left_expr_str;') mut op1, mut op2 := '', ''
g.definitions.writeln('$field_type b_ = $right_expr_str;') if infix_expr.left_type == table.string_type {
mut op1, mut op2 := '', '' if is_reverse {
if infix_expr.left_type == table.string_type { op1 = 'string_gt(a_, b_)'
if is_reverse { op2 = 'string_lt(a_, b_)'
op1 = 'string_gt(a_, b_)'
op2 = 'string_lt(a_, b_)'
} else {
op1 = 'string_lt(a_, b_)'
op2 = 'string_gt(a_, b_)'
}
} else { } else {
if is_reverse { op1 = 'string_lt(a_, b_)'
op1 = 'a_ > b_' op2 = 'string_gt(a_, b_)'
op2 = 'a_ < b_' }
} else { } else {
op1 = 'a_ < b_' if is_reverse {
op2 = 'a_ > b_' 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') { if is_reverse && !compare_fn.ends_with('_reverse') {

View File

@ -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'
}