diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index bc5701b81c..3fddec3870 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -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) diff --git a/vlib/builtin/array_test.v b/vlib/builtin/array_test.v index 32b2153d5a..f0cd2d061f 100644 --- a/vlib/builtin/array_test.v +++ b/vlib/builtin/array_test.v @@ -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() { diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 3edb59b754..0dd55187b1 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -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) } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 07827215e8..35123083a5 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -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 diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 93fbde4eaf..44630db8d8 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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 diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 6af20e7820..3e30ed29e5 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -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() diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index a82c772071..f818d79112 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -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 diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index a5ba088609..cd3da27559 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -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