all: new `.sort(a < b)` syntax

pull/6115/head
Alexander Medvednikov 2020-08-12 05:54:51 +02:00
parent 9800cf1e7b
commit 4bc0dde413
8 changed files with 108 additions and 11 deletions

View File

@ -539,6 +539,16 @@ fn compare_ints(a, b &int) int {
return 0
}
fn compare_floats(a, b &f64) int {
if *a < *b {
return -1
}
if *a > *b {
return 1
}
return 0
}
// []int.sort sorts array of int in place in ascending order.
pub fn (mut a []int) sort() {
a.sort_with_compare(compare_ints)

View File

@ -710,6 +710,13 @@ fn test_sort() {
assert nums[2] == 42
assert nums[3] == 67
assert nums[4] == 108
//
nums.sort(a < b)
assert nums[0] == -3
assert nums[1] == 7
assert nums[2] == 42
assert nums[3] == 67
assert nums[4] == 108
}
fn test_f32_sort() {

View File

@ -126,7 +126,7 @@ pub fn (bp byteptr) vstring() string {
return string{
str: bp
len: unsafe {C.strlen(bp)}
}
}
}
// byteptr.vstring_with_len() - converts a C style string to a V string. NB: the string data is reused, NOT copied.
@ -135,7 +135,7 @@ pub fn (bp byteptr) vstring_with_len(len int) string {
return string{
str: bp
len: len
}
}
}
// string.clone_static returns an independent copy of a given array
@ -250,7 +250,7 @@ fn compare_rep_index(a, b &RepIndex) int {
}
fn (mut a []RepIndex) sort() {
fn (mut a []RepIndex) sort2() {
a.sort_with_compare(compare_rep_index)
}
@ -300,7 +300,7 @@ pub fn (s string) replace_each(vals []string) string {
if idxs.len == 0 {
return s
}
idxs.sort()
idxs.sort2()
mut b := malloc(new_len + 1) // add a \0 just in case
// Fill the new string
mut idx_pos := 0
@ -1446,7 +1446,7 @@ pub fn (s string) repeat(count int) string {
}
}
unsafe {
new_len := s.len * count
new_len := s.len * count
ret[new_len] = 0
return ret.vstring_with_len(new_len)
}

View File

@ -14,6 +14,7 @@ pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | C
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr |
None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf |
SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | TypeOf | UnsafeExpr
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | CompIf | ConstDecl |
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl |
@ -612,12 +613,13 @@ pub:
mod string
}
// filter(), map()
/*
// filter(), map(), sort()
pub struct Lambda {
pub:
name string
}
*/
pub struct AssignStmt {
pub:
right []Expr

View File

@ -847,12 +847,19 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
// TODO: remove this for actual methods, use only for compiler magic
// FIXME: Argument count != 1 will break these
if left_type_sym.kind == .array &&
method_name in ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice'] {
method_name in ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort'] {
mut elem_typ := table.void_type
if method_name in ['filter', 'map'] {
is_filter_map := method_name in ['filter', 'map']
is_sort := method_name == 'sort'
if is_filter_map || is_sort {
array_info := left_type_sym.info as table.Array
mut scope := c.file.scope.innermost(call_expr.pos.pos)
scope.update_var_type('it', array_info.elem_type)
if is_filter_map {
scope.update_var_type('it', array_info.elem_type)
} else if is_sort {
scope.update_var_type('a', array_info.elem_type)
scope.update_var_type('b', array_info.elem_type)
}
elem_typ = array_info.elem_type
}
// map/filter are supposed to have 1 arg only

View File

@ -3736,6 +3736,56 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
g.write(tmp)
}
// `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 {
println(node.name)
println(g.typ(node.receiver_type))
println(sym.kind)
verror('sort() requires an array')
}
info := 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
// `users.sort(a.age > b.age)`
if node.args.len > 0 {
// 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
}
mut compare_fn := match typ {
table.int_type {
'compare_ints'
}
table.string_type {
'compare_strings'
}
table.f64_type {
'compare_floats'
}
else {
q := g.table.get_type_symbol(typ)
if node.args.len == 0 {
verror('usage: .sort(a.field < b.field)')
}
verror('sort(): unhandled type $typ $q.name')
''
}
}
//
g.write('qsort(')
g.expr(node.left)
g.write('.data, ')
g.expr(node.left)
g.write('.len, ')
g.expr(node.left)
g.writeln('.element_size, $compare_fn);')
}
// `nums.filter(it % 2 == 0)`
fn (mut g Gen) gen_array_filter(node ast.CallExpr) {
tmp := g.new_tmp_var()

View File

@ -341,6 +341,10 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
g.gen_array_filter(node)
return
}
'sort' {
g.gen_array_sort(node)
return
}
'insert' {
g.gen_array_insert(node)
return

View File

@ -1133,6 +1133,19 @@ fn (mut p Parser) scope_register_it() {
})
}
fn (mut p Parser) scope_register_ab() {
p.scope.register('a', ast.Var{
name: 'a'
pos: p.tok.position()
is_used: true
})
p.scope.register('b', ast.Var{
name: 'b'
pos: p.tok.position()
is_used: true
})
}
fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
p.next()
if p.tok.kind == .dollar {
@ -1149,6 +1162,10 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
// defer {
// p.close_scope()
// }
} else if field_name == 'sort' {
p.open_scope()
name_pos = p.tok.position()
p.scope_register_ab()
}
// ! in mutable methods
if p.tok.kind == .not && p.peek_tok.kind == .lpar {
@ -1208,7 +1225,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
pos: pos
}
}
if is_filter {
if is_filter || field_name == 'sort' {
p.close_scope()
}
return mcall_expr